16

3DシーンをViewport3Dからビットマップにエクスポートしたいと思います。

これを行うための明白な方法は、RenderTargetBitmapを使用することです。ただし、これを行うと、エクスポートされたビットマップの品質が画面上の画像よりも大幅に低くなります。インターネットを見回すと、RenderTargetBitmapはハードウェアレンダリングを利用していないようです。これは、レンダリングがTier0で行われることを意味します。これは、ミップマッピングなどがないため、エクスポートされた画像の品質が低下することを意味します。

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;
    }
4

6 に答える 6

5

ハードウェアを使用してレンダリングするように指示する設定がない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(); 
}

次のように、WPF コンテンツがレンダリングされる場所に対応する Direct3D デバイスを作成できます。

  1. Visual.PointToScreen画面上の画像内のポイントを呼び出す
  2. hMonitorを取得するために電話MonitorFromPointをかけるUser32.dll
  3. pD3Dを取得するために電話Direct3DCreate9をかけるd3d9.dll
  4. pD3D->GetAdapterCount()アダプターをカウントするための呼び出し
  5. 0 から count-1 まで反復pD3D->GetAdapterMonitor()し、以前に取得した hMonitor を呼び出して比較し、アダプター インデックスを決定します。
  6. pD3D->CreateDevice()デバイス自体を作成するための呼び出し

C++/CLR でコード化された別のライブラリでこのほとんどを実行するのは、そのアプローチに慣れているからですが、 SlimDXを使用して、純粋な C# およびマネージ コードに変換するのは簡単だと思うかもしれません。 私はまだそれを試していません。

于 2010-02-16T22:30:57.363 に答える
4

ミップマッピングとは何か (または、ソフトウェア レンダラーがそれやマルチレベル アンチエイリアシングを行うのかどうか) はわかりませんが、Charles Petzold による少し前の高解像度 WPF の印刷に関する投稿を思い出します。 3Dビジュアル。

サンプルコードで試してみましたが、うまくいくようです。ですから、少しスケールアップする必要があったと思います。

次のように、rtbImage で Stretch を None に設定し、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;
}

それがあなたの問題を解決することを願っています!

于 2010-02-15T22:09:35.357 に答える
1

いくつかの理由で空白の画面が表示されたと思います。まず、VisualBrush が可視の Visual を指している必要がありました。次に、RectangleGeometry に次元が必要であることを忘れている可能性があります (最初は知っていました)。

私はよくわからない奇妙なものを見ました。つまり、VisualBrush で AlignmentY を Bottom に設定しなければならなかった理由がわかりません。

それ以外は、うまくいくと思います...実際の状況に合わせてコードを簡単に変更できるはずです。

ボタン クリック イベント ハンドラは次のとおりです。

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>
于 2010-02-16T15:31:55.327 に答える
0

また、 http: //social.msdn.microsoft.com/Forums/en-US/wpf/thread/50989d46-7240-4af5-a8b5-のWindows Presentation Foundationフォーラムで、この質問に対するいくつかの有用な回答がありました。d034cb2a498b/

特に、MarcoZhouからの次の2つの答えを試してみます。

または、Viewport3Dを画面外のHwndSourceにレンダリングしてから、そのHWNDを取得して、Bitblt関数にフィードすることもできます。Bitbltは、ハードウェアラスタライザーによって既にレンダリングされているものをメモリバッファー内の独自のものにコピーします。私はこの方法を自分で試しているわけではありませんが、試す価値があり、理論的にはうまくいくはずです。

WIN32 APIにピンボークしない簡単な方法の1つは、Viewport3DをElementHostに配置し、そのElementHost.DrawToBitmap()を呼び出すことです。ここでの注意点の1つは、Viewport3Dが"になった後、適切なタイミングでDrawToBitmap()を呼び出す必要があることです。完全にレンダリングされた」場合は、System.Windows.Forms.Application.DoEvents()を呼び出してメッセージを手動でポンピングし、CompositionTarget.Renderingイベントをフックして、コンポジションスレッドからコールバックを取得できます(これは、レンダリングイベントは、この典型的な状況で信頼できます)。ところで、上記の方法は、ElementHostを画面に表示する必要がないという前提に基づいています。ElementHostが画面に表示されます。DrawToBitmap()メソッドを直接呼び出すことができます。

于 2010-02-20T11:55:34.170 に答える
0

ここでの問題は、WPF のソフトウェア レンダラーがミップ マッピングとマルチレベル アンチ エイリアシングを実行しないことだと思います。を使用する代わりに、レンダリングしたい 3D シーンのRanderTargetBitmapを作成できる場合がありDrawingImageます。ImageSource理論的には、ハードウェア レンダーはシーン イメージを生成する必要があり、これをプログラムで から抽出できますDrawingImage

于 2010-02-01T18:49:02.597 に答える