我想将3D场景从Viewport3D导出到位图。

这样做的明显方法是使用RenderTargetBitmap-但是,当我这样做时,导出的位图的质量明显低于屏幕上的图像。环顾互联网,似乎RenderTargetBitmap没有利用硬件渲染的优势。这意味着渲染是在Tier 0完成的。这意味着没有映射等,因此降低了导出图像的质量。

有谁知道如何以屏幕质量导出Viewport3D的位图?

澄清

尽管下面给出的示例没有显示此内容,但我最终需要将Viewport3D的位图导出到文件中。 据我了解,执行此操作的唯一方法是将图像转换为从BitmapSource派生的图像。下面的Cplotts显示,使用RenderTargetBitmap提高导出的质量可以改善图像,但是由于渲染仍在软件中完成,因此速度实在太慢了。

是否可以使用硬件渲染将渲染的3D场景导出到文件中? 当然应该可行吗?

您可以看到此xaml的问题:

<Window x:Class="RenderTargetBitmapProblem.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="400" Width="500">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Viewport3D Name="viewport3D">
            <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 Name="rtbImage" Visibility="Collapsed"/>
        <Button Grid.Row="1" Click="Button_Click">RenderTargetBitmap!</Button>
    </Grid>
</Window>

这段代码:
    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。我建议使用this article中提供的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();
}

您可以创建与呈现WPF内容的位置相对应的Direct3D设备,如下所示:
  • 在屏幕图像上的某个点上调用Visual.PointToScreen
  • MonitorFromPoint中调用User32.dll以获取hMonitor
  • Direct3DCreate9中调用d3d9.dll以获取pD3D
  • 调用pD3D->GetAdapterCount()来计算适配器
  • 从0迭代到count-1,然后调用pD3D->GetAdapterMonitor()并与先前检索的hMonitor进行比较,以确定适配器索引
  • 调用pD3D->CreateDevice()来创 build 备本身

  • 我可能会在一个用C++ / CLR编码的单独的库中完成大多数操作,因为这种方法对我来说很熟悉,但是您可能会发现使用SlimDX.轻松将其转换为纯C#和托管代码很容易,我还没有尝试过。

    10-04 19:44