3D オブジェクト

Last Updated 2011/09/21


3D オブジェクト上にテキストを描画する

3D オブジェクト上にテキストを描画する方法を従来の .Net Framework プログラミングの発想で考えていると答えは得られません。WPF 的解決法は、ブラシを使うこなすことです。つまり、Brush プロパティには VisualBrush オブジェクトも設定できることを思い出してください。VisualBrush オブジェクトは Visual オブジェクトそのものをブラシとして利用するものです。Visual オブジェクトは WPF 標準のほとんどのコントロールをカバーします。たとえば、Button コントロールや TextBlock コントロールなどです。

3D オブジェクト上にテキストを描画するためにはテキストを表示するコントロール、たとえば、Label コントロールか TextBlock コントロールを使うことになるでしょう。以下のコードは TextBlock コントロールを VisualBrush オブジェクトに設定することにしたものです。

【注意】以下の [使用例の実行] は、IE(インターネットエクスプローラ)でしか正常に動作しません。

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Page.Resources>
    <MeshGeometry3D x:Key="squareMesh"
                    Positions="-1,1,1  -1,-1,1  1,-1,1  1,1,1"
                    TriangleIndices="0 1 2 0 2 3"
                    Normals="0,0,1  0,0,1  0,0,1  0,0,1  0,0,1  0,0,1"
                    TextureCoordinates="0,-1  0,0  1,0  1,-1" />

    <GeometryModel3D x:Key="redModel" Geometry="{StaticResource squareMesh}">
      <GeometryModel3D.Material>
        <DiffuseMaterial>
          <DiffuseMaterial.Brush>
            <VisualBrush Stretch="Fill">
              <VisualBrush.Visual>
                <TextBlock Background="OrangeRed" Foreground="Yellow">Orange</TextBlock>
              </VisualBrush.Visual>
            </VisualBrush>
          </DiffuseMaterial.Brush>
        </DiffuseMaterial>
      </GeometryModel3D.Material>
    </GeometryModel3D>
    <GeometryModel3D x:Key="yellowModel" Geometry="{StaticResource squareMesh}">
      <GeometryModel3D.Material>
        <DiffuseMaterial>
          <DiffuseMaterial.Brush>
            <VisualBrush Stretch="Fill">
              <VisualBrush.Visual>
                <TextBlock Background="Yellow" Foreground="Blue">Yellow</TextBlock>
              </VisualBrush.Visual>
            </VisualBrush>
          </DiffuseMaterial.Brush>
        </DiffuseMaterial>
      </GeometryModel3D.Material>
    </GeometryModel3D>
    <GeometryModel3D x:Key="blueModel" Geometry="{StaticResource squareMesh}">
      <GeometryModel3D.Material>
        <DiffuseMaterial>
          <DiffuseMaterial.Brush>
            <VisualBrush Stretch="Fill">
              <VisualBrush.Visual>
                <TextBlock Background="DeepSkyBlue" Foreground="Yellow">SkyBlue</TextBlock>
              </VisualBrush.Visual>
            </VisualBrush>
          </DiffuseMaterial.Brush>
        </DiffuseMaterial>
      </GeometryModel3D.Material>
    </GeometryModel3D>
    <GeometryModel3D x:Key="greenModel" Geometry="{StaticResource squareMesh}">
      <GeometryModel3D.Material>
        <DiffuseMaterial>
          <DiffuseMaterial.Brush>
            <VisualBrush Stretch="Fill">
              <VisualBrush.Visual>
                <TextBlock Background="LawnGreen" Foreground="Yellow">Green</TextBlock>
              </VisualBrush.Visual>
            </VisualBrush>
          </DiffuseMaterial.Brush>
        </DiffuseMaterial>
      </GeometryModel3D.Material>
    </GeometryModel3D>
    <GeometryModel3D x:Key="magentaModel" Geometry="{StaticResource squareMesh}">
      <GeometryModel3D.Material>
        <DiffuseMaterial>
          <DiffuseMaterial.Brush>
            <VisualBrush Stretch="Fill">
              <VisualBrush.Visual>
                <TextBlock Background="Magenta" Foreground="Yellow">Magenta</TextBlock>
              </VisualBrush.Visual>
            </VisualBrush>
          </DiffuseMaterial.Brush>
        </DiffuseMaterial>
      </GeometryModel3D.Material>
    </GeometryModel3D>
  </Page.Resources>

  <StackPanel Background="DarkSlateBlue">
    <Viewport3D Width="400" Height="300">
      <Viewport3D.Camera>
        <PerspectiveCamera Position="0, 3, 10" LookDirection="0,-3,-10" FieldOfView="30"/>
      </Viewport3D.Camera>

      <ModelVisual3D>
        <ModelVisual3D.Children>
          <ModelVisual3D>
            <ModelVisual3D.Content>
              <AmbientLight Color="White"/>
            </ModelVisual3D.Content>
          </ModelVisual3D>

          <ModelVisual3D Content="{StaticResource redModel}"/>
          <ModelVisual3D Content="{StaticResource blueModel}">
            <ModelVisual3D.Transform>
              <RotateTransform3D>
                <RotateTransform3D.Rotation>
                  <AxisAngleRotation3D Angle="90" Axis="0 1 0"/>
                </RotateTransform3D.Rotation>
              </RotateTransform3D>
            </ModelVisual3D.Transform>
          </ModelVisual3D>
          <ModelVisual3D Content="{StaticResource yellowModel}">
            <ModelVisual3D.Transform>
              <RotateTransform3D>
                <RotateTransform3D.Rotation>
                  <AxisAngleRotation3D Angle="180" Axis="0 1 0"/>
                </RotateTransform3D.Rotation>
              </RotateTransform3D>
            </ModelVisual3D.Transform>
          </ModelVisual3D>
          <ModelVisual3D Content="{StaticResource greenModel}">
            <ModelVisual3D.Transform>
              <RotateTransform3D>
                <RotateTransform3D.Rotation>
                  <AxisAngleRotation3D Angle="-90" Axis="0 1 0"/>
                </RotateTransform3D.Rotation>
              </RotateTransform3D>
            </ModelVisual3D.Transform>
          </ModelVisual3D>
          <ModelVisual3D Content="{StaticResource greenModel}">
            <ModelVisual3D.Transform>
              <RotateTransform3D>
                <RotateTransform3D.Rotation>
                  <AxisAngleRotation3D Angle="90" Axis="1 0 0"/>
                </RotateTransform3D.Rotation>
              </RotateTransform3D>
            </ModelVisual3D.Transform>
          </ModelVisual3D>
          <ModelVisual3D Content="{StaticResource magentaModel}">
            <ModelVisual3D.Transform>
              <RotateTransform3D>
                <RotateTransform3D.Rotation>
                  <AxisAngleRotation3D Angle="-90" Axis="1 0 0"/>
                </RotateTransform3D.Rotation>
              </RotateTransform3D>
            </ModelVisual3D.Transform>
          </ModelVisual3D>
        </ModelVisual3D.Children>

        <ModelVisual3D.Transform>
          <RotateTransform3D>
            <RotateTransform3D.Rotation>
              <AxisAngleRotation3D x:Name="rotateTransform" Axis="1,1,0"/>
            </RotateTransform3D.Rotation>
          </RotateTransform3D>
        </ModelVisual3D.Transform>
      </ModelVisual3D>

      <Viewport3D.Triggers>
        <EventTrigger RoutedEvent="Viewport3D.Loaded">
          <BeginStoryboard>
            <Storyboard >
              <DoubleAnimation Storyboard.TargetName="rotateTransform"
                               Storyboard.TargetProperty="Angle"
                               RepeatBehavior="Forever" From="0" To="360" Duration="0:0:5" />
            </Storyboard>
          </BeginStoryboard>
        </EventTrigger>
      </Viewport3D.Triggers>

    </Viewport3D>
  </StackPanel>
</Page>

3D オブジェクトをクリップボードに貼り付ける

3D オブジェクトは Viewport3D コントロール上に表示することになりますが、このコントロール上に表示したイメージをビットマップとしてクリップボードに貼り付ける手順を説明します。

フォームに Viewport3D コントロールを配置し、すでに 3D オブジェクトが表示されていると仮定します。button1 をクリックすると、Viewport3D コントロールの内容をビットマップに変換し、クリップボードに貼り付けます。

private void button1_Click(object sender, RoutedEventArgs e)
{
  double width = viewport3D.ActualWidth;
  double height = viewport3D.ActualHeight;

  RenderTargetBitmap bmp = new RenderTargetBitmap((int)width, (int)height, 96, 96, PixelFormats.Pbgra32);

  DrawingVisual drawingVisual = new DrawingVisual();
  DrawingContext drawingContext = drawingVisual.RenderOpen();
  drawingContext.DrawRectangle(Brushes.AliceBlue, null, new Rect(0, 0, Width, Height));
  drawingContext.Close();

  // 背景色を設定する
  bmp.Render(drawingVisual);

  // viewport3D の内容を描画する
  bmp.Render(viewport3D);

  // クリップボードに貼り付ける
  Clipboard.SetImage(bmp);
}

「背景色を設定する」については若干の説明が必要です。Viewport3D クラスには Background プロパティがないことからも分かるように、このクラスは 3D オブジェクトを表示するためのコンテナにすぎません。したがって、Viewport3D コントロールの背景色はそのコンテナの背景色になります。たとえば、Viewport3D コントロールを StackPanel コントロール上に配置した場合、StackPanel コントロールの背景色となります。StackPanel の背景色を設定する手順を省略すれば分かりますが、背景は黒になります。

ところで、上記のコードでは背景色の設定のために DrawingVisual オブジェクトを使いましたが、Rectangle オブジェクトでいいはずだと思ってテストしたところ、うまくいきませんでした。その原因は不明です。


3D オブジェクトをファイルに保存する

「3D オブジェクトをクリップボードに貼り付ける」の続きですが、Viewport3D コントロールの内容をファイルに保存する手順を説明します。ここでは、JPEG に保存しますが、エンコーダーを変えれば、そのほかの形式で保存することができます。

private void button1_Click(object sender, RoutedEventArgs e)
{
  double width = viewport3D.ActualWidth;
  double height = viewport3D.ActualHeight;

  RenderTargetBitmap bmp = new RenderTargetBitmap((int)width, (int)height, 96, 96, PixelFormats.Pbgra32);

  DrawingVisual drawingVisual = new DrawingVisual();
  DrawingContext drawingContext = drawingVisual.RenderOpen();
  drawingContext.DrawRectangle(Brushes.AliceBlue, null, new Rect(0, 0, Width, Height));
  drawingContext.Close();

  // 背景色を設定する
  bmp.Render(drawingVisual);

  // viewport3D の内容を描画する
  bmp.Render(viewport3D);

  // JPEG ファイルとして保存する
  JpegBitmapEncoder encoder = new JpegBitmapEncoder();
  encoder.Frames.Add(BitmapFrame.Create(bmp));

  Stream stream = File.Create(@"c:\Test.jpg");

  encoder.Save(stream);

  stream.Close();
}

3D オブジェクトを印刷する

PrintDialog クラスの PrintVisual メソッドは Visual オブジェクトを印刷するものです。

「3D オブジェクトをクリップボードに貼り付ける」と同様に、フォームに Viewport3D コントロールを配置し、すでに 3D オブジェクトが表示されていると仮定します。button1 をクリックすると、印刷ダイアログボックスを表示したあと、Viewport3D コントロールの内容を印刷します。

private void button1_Click(object sender, RoutedEventArgs e)
{
  PrintDialog dlg = new PrintDialog();

  if ((dlg.ShowDialog() == true))
  {
    dlg.PrintVisual(viewport3D as Visual, "Viewport3D の印刷");
  }
}

−以上−