Я желаю экспортировать 3D сцену от Viewport3D до битового массива.
Очевидный способ сделать это должно было бы использовать RenderTargetBitmap - однако, когда я это качество экспортированного растрового изображения значительно ниже, чем экранное изображение. Озираясь в Интернете, кажется, что RenderTargetBitmap не использует в своих интересах аппаратный рендеринг. Что означает, что рендеринг сделан в Tier 0. Что не означает множественного отображения и т.д., следовательно уменьшенное качество экспортируемого изображения.
Кто-либо знает, как экспортировать битовый массив Viewport3D в экранном качестве?
Разъяснение
Хотя пример, данный ниже, не показывает это, я должен в конечном счете экспортировать битовый массив Viewport3D в файл. Поскольку я понимаю единственный способ сделать, это должно получить изображение во что-то, что происходит из BitmapSource. Cplotts ниже показывает, что, увеличивая качество использования экспорта RenderTargetBitmap улучшает изображение, но поскольку рендеринг все еще сделан в программном обеспечении, это непомерно медленно.
Существует ли способ экспортировать представленную 3D сцену в файл, с помощью аппаратного рендеринга? Конечно, это должно быть возможно?
Вы видите проблему с этим xaml:
И этот код:
private void Button_Click(object sender, RoutedEventArgs e)
{
RenderTargetBitmap bmp = new RenderTargetBitmap((int)viewport3D.ActualWidth,
(int)viewport3D.ActualHeight, 96, 96, PixelFormats.Default);
bmp.Render(viewport3D);
rtbImage.Source = bmp;
viewport3D.Visibility = Visibility.Collapsed;
rtbImage.Visibility = Visibility.Visible;
}
В RenderTargetBitmap
нет настройки, указывающей ему рендеринг с использованием оборудования, поэтому вам придется вернуться к использованию Win32 или DirectX. Я бы рекомендовал использовать технику DirectX, описанную в этой статье . Следующий код из статьи показывает, как это можно сделать (это код C ++):
extern IDirect3DDevice9* g_pd3dDevice;
Void CaptureScreen()
{
IDirect3DSurface9* pSurface;
g_pd3dDevice->CreateOffscreenPlainSurface(ScreenWidth, ScreenHeight,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurface, NULL);
g_pd3dDevice->GetFrontBufferData(0, pSurface);
D3DXSaveSurfaceToFile("Desktop.bmp",D3DXIFF_BMP,pSurface,NULL,NULL);
pSurface->Release();
}
Вы можете создать устройство Direct3D, соответствующее месту, где отображается содержимое WPF, следующим образом:
Visual.PointToScreen
в точке вашего экранного изображения MonitorFromPoint
в User32.dll
, чтобы получить hMonitor Direct3DCreate9
в d3d9.dll
для получения pD3D pD3D-> GetAdapterCount ()
для подсчета адаптеров pD3D-> GetAdapterMonitor ()
и сравнение с ранее полученным hMonitor для определения индекса адаптера pD3D-> CreateDevice ()
для создания самого устройства Я бы, вероятно, сделал большую часть этого в отдельная библиотека, написанная на C ++ / CLR, потому что этот подход мне знаком, но вы можете легко перевести ее на чистый C # и управляемый код, используя SlimDX . Я еще не пробовал.
Я думаю, что проблема здесь действительно заключается в том, что программное обеспечение Renderer для WPF не выполняет MIP-сопоставление и многоуровневое антисемейство. Вместо того, чтобы использовать Randertargetbitmap
, вы можете создать Pontining
, чьи ImpOURCE
- это сцена 3D, которую вы хотите рендер. Теоретически, аппаратное обеспечение должно производить изображение сцены, которое вы можете затем программно извлечь из PriceImage
.
Я не знаю, что такое mip-mapping (или делает ли это программный рендерер и/или многоуровневое сглаживание), но я помню пост Чарльза Петцольда некоторое время назад, в котором речь шла о печати 3D-изображений высокого разрешения в WPF.
Я попробовал это с вашим примером кода, и, похоже, он работает отлично. Итак, я предполагаю, что вам просто нужно немного увеличить масштаб.
Вам нужно установить Stretch в None для rtbImage и изменить обработчик события Click следующим образом:
private void Button_Click(object sender, RoutedEventArgs e)
{
// Scale dimensions from 96 dpi to 600 dpi.
double scale = 600 / 96;
RenderTargetBitmap bmp =
new RenderTargetBitmap
(
(int)(scale * (viewport3D.ActualWidth + 1)),
(int)(scale * (viewport3D.ActualHeight + 1)),
scale * 96,
scale * 96,
PixelFormats.Default
);
bmp.Render(viewport3D);
rtbImage.Source = bmp;
viewport3D.Visibility = Visibility.Collapsed;
rtbImage.Visibility = Visibility.Visible;
}
Надеюсь, это решит вашу проблему!
Я думаю, у вас был пустой экран по нескольким причинам. Во-первых, VisualBrush должен был указывать на видимый Visual. Во-вторых, возможно, вы забыли, что у RectangleGeometry должны быть размеры (сначала я знаю).
Я видел некоторые странные вещи, которых не совсем понимаю. То есть я не понимаю, почему мне пришлось установить AlignmentY на Bottom на VisualBrush.
В остальном, я думаю, это работает ... и я думаю, что вы легко сможете изменить код для вашей реальной ситуации.
Вот обработчик события нажатия кнопки:
private void Button_Click(object sender, RoutedEventArgs e)
{
GeometryDrawing geometryDrawing = new GeometryDrawing();
geometryDrawing.Geometry =
new RectangleGeometry
(
new Rect(0, 0, viewport3D.ActualWidth, viewport3D.ActualHeight)
);
geometryDrawing.Brush =
new VisualBrush(viewport3D)
{
Stretch = Stretch.None,
AlignmentY = AlignmentY.Bottom
};
DrawingImage drawingImage = new DrawingImage(geometryDrawing);
image.Source = drawingImage;
}
Вот Window1.xaml:
<Window
x:Class="RenderTargetBitmapProblem.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="400"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400"/>
</Grid.ColumnDefinitions>
<Viewport3D
x:Name="viewport3D"
Grid.Row="0"
Grid.Column="0"
>
<Viewport3D.Camera>
<PerspectiveCamera Position="0,0,3"/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<AmbientLight Color="White"/>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D
Positions="-1,-10,0 1,-10,0 -1,20,0 1,20,0"
TextureCoordinates="0,1 0,0 1,1 1,0"
TriangleIndices="0,1,2 1,3,2"
/>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource="http://www.wyrmcorp.com/galleries/illusions/Hermann%20Grid.png"
TileMode="Tile"
Viewport="0,0,0.25,0.25"
/>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
</GeometryModel3D>
</ModelVisual3D.Content>
<ModelVisual3D.Transform>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D Axis="1,0,0" Angle="-82"/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
</ModelVisual3D.Transform>
</ModelVisual3D>
</Viewport3D>
<Image
x:Name="image"
Grid.Row="0"
Grid.Column="0"
/>
<Button Grid.Row="1" Click="Button_Click">Render!</Button>
</Grid>
</Window>
Используя SlimDX, попробуйте получить доступ к поверхности DirectX, на которую визуализирует ViewPort3D,
, затем выполните чтение пикселя для чтения буфера из пиксельного буфера графической карты в обычную память.
Получив (неуправляемый) буфер, скопируйте его в существующее растровое изображение с возможностью записи, используя небезопасный код или маршалинг.
Используйте класс обертки Байт
:
List<Byte> mylist = new ArrayList<Byte>();
Затем, из-за автобоксирования, вы по-прежнему можете иметь:
for (byte b : mylist) {
}
-121--3894331- Если явно не указать схему в имени создаваемой таблицы, она будет создана в текущей схеме пользователя по умолчанию.
Я уверен, что пользователь, которого вы используете, имеет собственную личную схему, заданную в качестве схемы по умолчанию - поэтому ваши таблицы создаются в его собственной личной схеме.
Можно проверить, какие у вас пользователи базы данных и какова их схема по умолчанию, проверив sys.database _ principals
(SQL Server 2005 и выше):
SELECT name, type_desc, default_schema_name
FROM sys.database_principals
Для решения этой проблемы
укажите явно используемую схему (в любом случае, рекомендуется!)
CREATE TABLE dbo. T_TableName
измените схему пользователя по умолчанию на dbo
ALTER USER [Домен\Ваш пользователь] WITH DEFAULT_SCHEMA = dbo
Но как общее правило, я рекомендую всегда использовать префикс «dbo». явно, если вы хотите, чтобы все объекты базы данных были в схеме dbo. Это также помогает повысить производительность (хотя бы незначительно), поскольку SQL Server не придется выполнять поиск в различных схемах, если явно указать, где находятся объекты базы данных.
-121--3586917-У меня также было несколько полезных ответов на этот вопрос на форумах Windows Presentation Foundation по адресу http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/50989d46-7240-4af5-a8b5-d034cb2a498b/ .
В частности, я попробую эти два ответа, оба от Марко Чжоу:
Или вы могли бы попытаться сделать Viewport3D на внешний экран HwndSource, а затем захватить его HWND, и подать его в функцию Bitblt. Bitblt скопирует то, что уже рендеринг аппаратным растризатором обратно в собственный буфер памяти. Я я сам не пробую этот метод, но стоит попробовать, и теоретически говоря, это должно сработать.
и
Я думаю, что один простой способ без pinvoking в WIN32 API для размещения Viewport3D в ElityHost и вызовите его ElityHost.DrawToBitmap (), один предостережение здесь заключается в том, что вам нужно позвонить DrawToBitmap () в нужное время после того, как Viewport3D "полностью визуализировано, "вы можете вручную прокачать сообщения путем вызова Система. Windows. Формы. Приложение. DoEvents (), и подключение ComposityTarget.Rendering событие в получить обратный вызов из состава Поток (Это может работать, так как я не точно уверен, является ли событие Rendering надежное в этом типичном обстоятельства). BTW, вышеуказанный метод основан на предположении, что вы нет необходимости отображать ElityHost на экране. Объект ElureHost будет отображается на экране, вы можете вызовите непосредственно DrawToBitmap () способ.