リード開発メモ

大阪のソフトウェア会社です。 技術的な事柄についてのメモとしてブログを始めます。

2013年09月

WPF で 3D(その6)

今回は半透明を試してみる。完全な透明はアルファ値が最大の場合と考えればいい。

半透明を試すとなると、向こう側に透けて見える物体が欲しくなるので、立方体を3つ作ることにする。

半透明の面の作り方だが、立方体のひとつはビットマップ画像全体にアルファ値を設定する方法とした。そのために画像操作のヘルパークラス ImageHelper を追加した。ソースコードは以下のとおり。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
 
namespace sample1
{
    public static class ImageHelper
    {
        public static Bitmap BitmapImage2Bitmap(BitmapImage bitmapImage)
        {
            using (MemoryStream outStream = new MemoryStream())
            {
                BitmapEncoder enc = new BmpBitmapEncoder();
                enc.Frames.Add(BitmapFrame.Create(bitmapImage));
                enc.Save(outStream);
                System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);
                return new Bitmap(bitmap);
            }
        }
 
        public static BitmapSource ToWpfBitmap(Bitmap bitmap)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                bitmap.Save(stream, ImageFormat.Png);
 
                stream.Position = 0;
                BitmapImage result = new BitmapImage();
                result.BeginInit();
                result.CacheOption = BitmapCacheOption.OnLoad;
                result.StreamSource = stream;
                result.EndInit();
                result.Freeze();
                return result;
            }
        }
 
        public static Bitmap CreateAlphaImage(Image srcImage, float alpha)
        {
            float[][] matrixItems ={ 
                new float[] {1, 0, 0, 0, 0},
                new float[] {0, 1, 0, 0, 0},
                new float[] {0, 0, 1, 0, 0},
                new float[] {0, 0, 0, alpha, 0}, 
                new float[] {0, 0, 0, 0, 1}};
            ColorMatrix colorMatrix = new ColorMatrix(matrixItems);
 
            ImageAttributes imageAtt = new ImageAttributes();
            imageAtt.SetColorMatrix(
                colorMatrix,
                ColorMatrixFlag.Default,
                ColorAdjustType.Bitmap);
 
            int iWidth = srcImage.Width;
            int iHeight = srcImage.Height;
            Bitmap newImage = new Bitmap(iWidth, iHeight);
 
            using (Graphics g = Graphics.FromImage(newImage))
            {
                g.DrawImage(srcImage,
                            new Rectangle(0, 0, iWidth, iHeight),
                            0.0f,
                            0.0f,
                            iWidth,
                            iHeight,
                            GraphicsUnit.Pixel,
                            imageAtt);
            }
 
            return newImage;
        }
    }
}

また、のこりふたつの立方体は簡単に SolidColorBrush の Opacity を設定した。MainWindow.xaml.cs は以下のとおり。
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;
 
namespace sample1
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Init();
        }
 
        private void Init()
        {
            var root = this.Content as Grid;
 
            // trackball
            var td = new _3DTools.TrackballDecorator();
            root.Children.Add(td);
 
            // viewport
            var viewport = new Viewport3D();
            td.Content = viewport;
 
            // camera
            var camera = new PerspectiveCamera();
            camera.Position = new Point3D(14, 13, 12);
            camera.LookDirection = new Vector3D(-14, -13, -12);
            camera.UpDirection = new Vector3D(0, 1, 0);
            viewport.Camera = camera;
 
            // light
            var light = new DirectionalLight();
            light.Color = Colors.White;
            light.Direction = new Vector3D(-2, -3, -1);
            var lightModel = new ModelVisual3D();
            lightModel.Content = light;
            viewport.Children.Add(lightModel);
 
            // light2
            var light2 = new AmbientLight();
            light2.Color = Color.FromRgb(128, 128, 128);
            var lightModel2 = new ModelVisual3D();
            lightModel2.Content = light2;
            viewport.Children.Add(lightModel2);
 
            // axis
            var xAxis = new _3DTools.ScreenSpaceLines3D();
            xAxis.Points.Add(new Point3D(-100, 0, 0));
            xAxis.Points.Add(new Point3D(100, 0, 0));
            xAxis.Color = Colors.Red;
            xAxis.Thickness = 1;
            var yAxis = new _3DTools.ScreenSpaceLines3D();
            yAxis.Points.Add(new Point3D(0, -100, 0));
            yAxis.Points.Add(new Point3D(0, 100, 0));
            yAxis.Color = Colors.Green;
            yAxis.Thickness = 1;
            var zAxis = new _3DTools.ScreenSpaceLines3D();
            zAxis.Points.Add(new Point3D(0, 0, -100));
            zAxis.Points.Add(new Point3D(0, 0, 100));
            zAxis.Color = Colors.Blue;
            zAxis.Thickness = 1;
            var axis = new ModelVisual3D();
            axis.Children.Add(xAxis);
            axis.Children.Add(yAxis);
            axis.Children.Add(zAxis);
            viewport.Children.Add(axis);
 
            // cube
            var point0 = new Point3D(0, 0, 0);    // bottom-back-left
            var point1 = new Point3D(5, 0, 0);    // bottom-back-right
            var point2 = new Point3D(5, 0, 5);    // bottom-front-right
            var point3 = new Point3D(0, 0, 5);    // bottom-front-left
            var point4 = new Point3D(0, 5, 0);    // top-back-left
            var point5 = new Point3D(5, 5, 0);    // top-back-right
            var point6 = new Point3D(5, 5, 5);    // top-front-right
            var point7 = new Point3D(0, 5, 5);    // top-front-left
 
            //       Y
            //       | 
            //    p4 +------------------+ p5
            //      /|                 /|
            //     / |                / |
            // p7 /  |            p6 /  |
            //   +------------------+   |
            //   |   +--------------|---+---------- X
            //   |  / p0            |  / p1
            //   | /                | /
            //   |/                 |/
            //   +------------------+
            //  / p3                p2
            // Z
 
            var imageSrc = new BitmapImage(new Uri(@"C:\syaraku.JPG"));
            var bitmap = ImageHelper.BitmapImage2Bitmap(imageSrc);
            var alphaImage = ImageHelper.CreateAlphaImage(bitmap, 0.5F);
            var alphaImageSrc = ImageHelper.ToWpfBitmap(alphaImage);
            var material = new DiffuseMaterial(new ImageBrush(alphaImageSrc));
 
            var group = new Model3DGroup();
            group.Children.Add(new GeometryModel3D(CreateMesh(point7, point6, point2, point3), material));    // front
            group.Children.Add(new GeometryModel3D(CreateMesh(point6, point5, point1, point2), material));    // right
            group.Children.Add(new GeometryModel3D(CreateMesh(point5, point4, point0, point1), material));    // back
            group.Children.Add(new GeometryModel3D(CreateMesh(point4, point7, point3, point0), material));    // left
            group.Children.Add(new GeometryModel3D(CreateMesh(point4, point5, point6, point7), material));    // top
            group.Children.Add(new GeometryModel3D(CreateMesh(point1, point0, point3, point2), material));    // bottom
 
            var cubeModel = new ModelVisual3D();
            cubeModel.Content = group;
            viewport.Children.Add(cubeModel);
            
            // cube2
            var group2 = new Model3DGroup();
            var br2 = new SolidColorBrush(Colors.Blue);
            br2.Opacity = 0.5;
            var material2 = new DiffuseMaterial(br2);
            group2.Children.Add(new GeometryModel3D(CreateMesh(point7, point6, point2, point3), material2));    // front
            group2.Children.Add(new GeometryModel3D(CreateMesh(point6, point5, point1, point2), material2));    // right
            group2.Children.Add(new GeometryModel3D(CreateMesh(point5, point4, point0, point1), material2));    // back
            group2.Children.Add(new GeometryModel3D(CreateMesh(point4, point7, point3, point0), material2));    // left
            group2.Children.Add(new GeometryModel3D(CreateMesh(point1, point0, point3, point2), material2));    // bottom
            group2.Children.Add(new GeometryModel3D(CreateMesh(point4, point5, point6, point7), material2));    // top
            var cubeModel2 = new ModelVisual3D();
            cubeModel2.Content = group2;
            var trans2 = new Transform3DGroup();
            trans2.Children.Add(new TranslateTransform3D(-3, -3, 3));
            cubeModel2.Transform = trans2;
            viewport.Children.Add(cubeModel2);
 
            // cube3
            var group3 = new Model3DGroup();
            var br3 = new SolidColorBrush(Colors.Green);
            br3.Opacity = 0.5;
            var material3 = new DiffuseMaterial(br3);
            group3.Children.Add(new GeometryModel3D(CreateMesh(point7, point6, point2, point3), material3));    // front
            group3.Children.Add(new GeometryModel3D(CreateMesh(point6, point5, point1, point2), material3));    // right
            group3.Children.Add(new GeometryModel3D(CreateMesh(point5, point4, point0, point1), material3));    // back
            group3.Children.Add(new GeometryModel3D(CreateMesh(point4, point7, point3, point0), material3));    // left
            group3.Children.Add(new GeometryModel3D(CreateMesh(point1, point0, point3, point2), material3));    // bottom
            group3.Children.Add(new GeometryModel3D(CreateMesh(point4, point5, point6, point7), material3));    // top
            var cubeModel3 = new ModelVisual3D();
            cubeModel3.Content = group3;
            var trans3 = new Transform3DGroup();
            trans3.Children.Add(new TranslateTransform3D(-3, 3, -3));
            cubeModel3.Transform = trans3;
            viewport.Children.Add(cubeModel3);
        }
 
        private static MeshGeometry3D CreateMesh(Point3D p0, Point3D p1, Point3D p2, Point3D p3)
        {
            var mesh = new MeshGeometry3D();
            mesh.Positions = new Point3DCollection(new Point3D[] { p0, p1, p2, p3 });
            mesh.TriangleIndices = new Int32Collection(new int[] { 0, 2, 1, 0, 3, 2 });
            mesh.TextureCoordinates = new PointCollection(new Point[] { new Point(0, 0), new Point(1, 0), new Point(1, 1), new Point(0, 1) });
            return mesh;
        }
    }
}

実行結果は以下のとおり。 wpf3d-6
よく見ると、真ん中の写楽の立方体の向こうにある青と緑の立方体が透けて見えない。また、カメラを動かして青い立方体を手前に持ってくると、写楽の立方体は透けて見えるのに、緑の立方体は透けて見えない。

実は WPF の半透明(透明も)をうまく描画するためには、奥にあるものから順に書いていく必要があるようだ。。。

つづきは次回。

WPF で 3D(その5)

今回は球を作り、テクスチャを貼ってみる。

球体となると点や面が多くなるため、計算で出す必要があるが、以下の MSDN のサンプル「3-D ソリッドのサンプル」に球体を作るソースコードが含まれているのでこれを利用する。
http://msdn.microsoft.com/ja-jp/library/ms771784%28v=vs.90%29.aspx

サンプルをビルドするとクラスライブラリ Primitive3DSurfaces.dll を生成するのでこれを使ってもいいが、今回は 「sphere3d.cs」と「primitive3d.cs」をプログラムに取り込んで使った。そのまま使うと、テクスチャ画像が左右反転されてしまったので、Sphere3D クラスの GetTextureCoordinate メソッドを以下のように少し書き換えた。

   private Point GetTextureCoordinate(double t, double y)
        {
            Matrix TYtoUV = new Matrix();
            TYtoUV.Scale(1 / (2 * Math.PI), -0.5);
 
            //Point p = new Point(t, y);
            Point p = new Point(1 - t, y);
            p = p * TYtoUV;
 
            return p;
        }

MainWindow.xaml.cs は以下のとおり。テクスチャ画像は Wikipedia の以下のページのランベルト正積円筒図法の場合の図を拝借した。有名なメルカトル図法は Sphere3D クラスのテクスチャの貼り方にはマッチしない。
テイソーの指示楕円

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;
 
namespace sample1
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Init();
        }
 
        private void Init()
        {
            var root = this.Content as Grid;
 
            // trackball
            var td = new _3DTools.TrackballDecorator();
            root.Children.Add(td);
 
            // viewport
            var viewport = new Viewport3D();
            td.Content = viewport;
 
            // camera
            var camera = new PerspectiveCamera();
            camera.Position = new Point3D(14, 13, 12);
            camera.LookDirection = new Vector3D(-14, -13, -12);
            camera.UpDirection = new Vector3D(0, 1, 0);
            viewport.Camera = camera;
 
            // light
            var light = new DirectionalLight();
            light.Color = Colors.White;
            light.Direction = new Vector3D(-2, -3, -1);
            var lightModel = new ModelVisual3D();
            lightModel.Content = light;
            viewport.Children.Add(lightModel);
 
            var light2 = new AmbientLight();
            light2.Color = Color.FromRgb(128, 128, 128);
            var lightModel2 = new ModelVisual3D();
            lightModel2.Content = light2;
            viewport.Children.Add(lightModel2);
 
            // axis
            var xAxis = new _3DTools.ScreenSpaceLines3D();
            xAxis.Points.Add(new Point3D(-100, 0, 0));
            xAxis.Points.Add(new Point3D(100, 0, 0));
            xAxis.Color = Colors.Red;
            xAxis.Thickness = 1;
            var yAxis = new _3DTools.ScreenSpaceLines3D();
            yAxis.Points.Add(new Point3D(0, -100, 0));
            yAxis.Points.Add(new Point3D(0, 100, 0));
            yAxis.Color = Colors.Green;
            yAxis.Thickness = 1;
            var zAxis = new _3DTools.ScreenSpaceLines3D();
            zAxis.Points.Add(new Point3D(0, 0, -100));
            zAxis.Points.Add(new Point3D(0, 0, 100));
            zAxis.Color = Colors.Blue;
            zAxis.Thickness = 1;
            var axis = new ModelVisual3D();
            axis.Children.Add(xAxis);
            axis.Children.Add(yAxis);
            axis.Children.Add(zAxis);
            viewport.Children.Add(axis);
 
            // sphere
            var sphere = new Sphere3D();
            var imageSrc = new BitmapImage(new Uri(@"C:\800px-Tissot_indicatrix_world_map_Lambert_cyl_equal-area_proj.svg.png"));
            var material = new DiffuseMaterial(new ImageBrush(imageSrc));
            sphere.Material = material;
            var trans = new Transform3DGroup();
            trans.Children.Add(new ScaleTransform3D(2.5, 2.5, 2.5));
            trans.Children.Add(new TranslateTransform3D(2.5, 2.5, 2.5));
            sphere.Transform = trans;
            viewport.Children.Add(sphere);
        }
    }
}

実行結果は以下のとおり。極あたりの歪みが激しいのが分かる。
wpf3d-5

以上。

WPF で 3D(その4)

今回は立方体を作り、すべての面にテクスチャを貼ってみる。

とりあえず、前回作った1面の生成処理を、6面すべてに行う。前回作った面は XZ平面上にあり、上(Y軸の正の方向)が正面と考えていたが、今回は XZ平面上の面は底にあたり、下(Y軸の負の方向)が正面となるので注意。そのためには TriangleIndices の指定を逆向きにする。

また、DirectionalLight だけだと陰になる部分が真っ暗になってしまうため、暗めの AmbientLight を追加している。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;
 
namespace sample1
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Init();
        }
 
        private void Init()
        {
            var root = this.Content as Grid;
 
            // trackball
            var td = new _3DTools.TrackballDecorator();
            root.Children.Add(td);
            
            // viewport
            var viewport = new Viewport3D();
            td.Content = viewport;
 
            // camera
            var camera = new PerspectiveCamera();
            camera.Position = new Point3D(14, 13, 12);
            camera.LookDirection = new Vector3D(-14, -13, -12);
            camera.UpDirection = new Vector3D(0, 1, 0);
            viewport.Camera = camera;
 
            // light
            var light = new DirectionalLight();
            light.Color = Colors.White;
            light.Direction = new Vector3D(-2, -3, -1);
            var lightModel = new ModelVisual3D();
            lightModel.Content = light;
            viewport.Children.Add(lightModel);

            // light2 
            var light2 = new AmbientLight();
            light2.Color = Color.FromRgb(128, 128, 128);
            var lightModel2 = new ModelVisual3D();
            lightModel2.Content = light2;
            viewport.Children.Add(lightModel2);
 
            // axis
            var xAxis = new _3DTools.ScreenSpaceLines3D();
            xAxis.Points.Add(new Point3D(-100, 0, 0));
            xAxis.Points.Add(new Point3D(100, 0, 0));
            xAxis.Color = Colors.Red;
            xAxis.Thickness = 1;
            var yAxis = new _3DTools.ScreenSpaceLines3D();
            yAxis.Points.Add(new Point3D(0, -100, 0));
            yAxis.Points.Add(new Point3D(0, 100, 0));
            yAxis.Color = Colors.Green;
            yAxis.Thickness = 1;
            var zAxis = new _3DTools.ScreenSpaceLines3D();
            zAxis.Points.Add(new Point3D(0, 0, -100));
            zAxis.Points.Add(new Point3D(0, 0, 100));
            zAxis.Color = Colors.Blue;
            zAxis.Thickness = 1;
            var axis = new ModelVisual3D();
            axis.Children.Add(xAxis);
            axis.Children.Add(yAxis);
            axis.Children.Add(zAxis);
            viewport.Children.Add(axis);
 
            // cube
            var point0 = new Point3D(0, 0, 0);    // bottom-back-left
            var point1 = new Point3D(5, 0, 0);    // bottom-back-right
            var point2 = new Point3D(5, 0, 5);    // bottom-front-right
            var point3 = new Point3D(0, 0, 5);    // bottom-front-left
            var point4 = new Point3D(0, 5, 0);    // top-back-left
            var point5 = new Point3D(5, 5, 0);    // top-back-right
            var point6 = new Point3D(5, 5, 5);    // top-front-right
            var point7 = new Point3D(0, 5, 5);    // top-front-left
 
            //       Y
            //       | 
            //    p4 +------------------+ p5
            //      /|                 /|
            //     / |                / |
            // p7 /  |            p6 /  |
            //   +------------------+   |
            //   |   +--------------|---+---------- X
            //   |  / p0            |  / p1
            //   | /                | /
            //   |/                 |/
            //   +------------------+
            //  / p3                p2
            // Z
 
            var squareMesh0 = new MeshGeometry3D();        // front
            var squareMesh1 = new MeshGeometry3D();        // right
            var squareMesh2 = new MeshGeometry3D();        // back
            var squareMesh3 = new MeshGeometry3D();        // left
            var squareMesh4 = new MeshGeometry3D();        // top
            var squareMesh5 = new MeshGeometry3D();        // bottom
 
            squareMesh0.Positions = new Point3DCollection(new Point3D[] { point7, point6, point2, point3 });        // front
            squareMesh1.Positions = new Point3DCollection(new Point3D[] { point6, point5, point1, point2 });        // right
            squareMesh2.Positions = new Point3DCollection(new Point3D[] { point5, point4, point0, point1 });        // back
            squareMesh3.Positions = new Point3DCollection(new Point3D[] { point4, point7, point3, point0 });        // left
            squareMesh4.Positions = new Point3DCollection(new Point3D[] { point4, point5, point6, point7 });        // top
            squareMesh5.Positions = new Point3DCollection(new Point3D[] { point1, point0, point3, point2 });        // bottom
 
            squareMesh0.TriangleIndices = new Int32Collection(new int[] { 0, 2, 1, 0, 3, 2 });        // front
            squareMesh1.TriangleIndices = new Int32Collection(new int[] { 0, 2, 1, 0, 3, 2 });        // right
            squareMesh2.TriangleIndices = new Int32Collection(new int[] { 0, 2, 1, 0, 3, 2 });        // back
            squareMesh3.TriangleIndices = new Int32Collection(new int[] { 0, 2, 1, 0, 3, 2 });        // left
            squareMesh4.TriangleIndices = new Int32Collection(new int[] { 0, 2, 1, 0, 3, 2 });        // top
            squareMesh5.TriangleIndices = new Int32Collection(new int[] { 0, 2, 1, 0, 3, 2 });        // bottom
 
            squareMesh0.TextureCoordinates = new PointCollection(new Point[] { new Point(0, 0), new Point(1, 0), new Point(1, 1), new Point(0, 1) });    // front
            squareMesh1.TextureCoordinates = new PointCollection(new Point[] { new Point(0, 0), new Point(1, 0), new Point(1, 1), new Point(0, 1) });    // right
            squareMesh2.TextureCoordinates = new PointCollection(new Point[] { new Point(0, 0), new Point(1, 0), new Point(1, 1), new Point(0, 1) });    // back
            squareMesh3.TextureCoordinates = new PointCollection(new Point[] { new Point(0, 0), new Point(1, 0), new Point(1, 1), new Point(0, 1) });    // left
            squareMesh4.TextureCoordinates = new PointCollection(new Point[] { new Point(0, 0), new Point(1, 0), new Point(1, 1), new Point(0, 1) });    // top
            squareMesh5.TextureCoordinates = new PointCollection(new Point[] { new Point(0, 0), new Point(1, 0), new Point(1, 1), new Point(0, 1) });    // bottom
 
            var imageSrc = new BitmapImage(new Uri(@"C:\syaraku.JPG"));
            var material = new DiffuseMaterial(new ImageBrush(imageSrc));
 
            var group = new Model3DGroup();
            group.Children.Add(new GeometryModel3D(squareMesh0, material));    // front
            group.Children.Add(new GeometryModel3D(squareMesh1, material));    // right
            group.Children.Add(new GeometryModel3D(squareMesh2, material));    // back
            group.Children.Add(new GeometryModel3D(squareMesh3, material));    // left
            group.Children.Add(new GeometryModel3D(squareMesh4, material));    // top
            group.Children.Add(new GeometryModel3D(squareMesh5, material));    // bottom
 
            var cubeModel = new ModelVisual3D();
            cubeModel.Content = group;
            viewport.Children.Add(cubeModel);
        }
    }
}

上では、TriangleIndices、TextureCoordinates の指定が同じになるよう Positions の指定のしかたを調整したので、TriangleIndices、TextureCoordinates の指定部分をメソッド化みる。ずいぶんすっきりするはず。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;
 
namespace sample1
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Init();
        }
 
        private void Init()
        {
            var root = this.Content as Grid;
 
            // trackball
            var td = new _3DTools.TrackballDecorator();
            root.Children.Add(td);
 
            // viewport
            var viewport = new Viewport3D();
            td.Content = viewport;
 
            // camera
            var camera = new PerspectiveCamera();
            camera.Position = new Point3D(14, 13, 12);
            camera.LookDirection = new Vector3D(-14, -13, -12);
            camera.UpDirection = new Vector3D(0, 1, 0);
            viewport.Camera = camera;
 
            // light
            var light = new DirectionalLight();
            light.Color = Colors.White;
            light.Direction = new Vector3D(-2, -3, -1);
            var lightModel = new ModelVisual3D();
            lightModel.Content = light;
            viewport.Children.Add(lightModel);
 
            var light2 = new AmbientLight();
            light2.Color = Colors.White;
            //light2.Direction = new Vector3D(-2, -3, -1);
            var lightModel2 = new ModelVisual3D();
            lightModel2.Content = light2;
            viewport.Children.Add(lightModel2);
 
            // axis
            var xAxis = new _3DTools.ScreenSpaceLines3D();
            xAxis.Points.Add(new Point3D(-100, 0, 0));
            xAxis.Points.Add(new Point3D(100, 0, 0));
            xAxis.Color = Colors.Red;
            xAxis.Thickness = 1;
            var yAxis = new _3DTools.ScreenSpaceLines3D();
            yAxis.Points.Add(new Point3D(0, -100, 0));
            yAxis.Points.Add(new Point3D(0, 100, 0));
            yAxis.Color = Colors.Green;
            yAxis.Thickness = 1;
            var zAxis = new _3DTools.ScreenSpaceLines3D();
            zAxis.Points.Add(new Point3D(0, 0, -100));
            zAxis.Points.Add(new Point3D(0, 0, 100));
            zAxis.Color = Colors.Blue;
            zAxis.Thickness = 1;
            var axis = new ModelVisual3D();
            axis.Children.Add(xAxis);
            axis.Children.Add(yAxis);
            axis.Children.Add(zAxis);
            viewport.Children.Add(axis);
 
            // cube
            var point0 = new Point3D(0, 0, 0);    // bottom-back-left
            var point1 = new Point3D(5, 0, 0);    // bottom-back-right
            var point2 = new Point3D(5, 0, 5);    // bottom-front-right
            var point3 = new Point3D(0, 0, 5);    // bottom-front-left
            var point4 = new Point3D(0, 5, 0);    // top-back-left
            var point5 = new Point3D(5, 5, 0);    // top-back-right
            var point6 = new Point3D(5, 5, 5);    // top-front-right
            var point7 = new Point3D(0, 5, 5);    // top-front-left
 
            //       Y
            //       | 
            //    p4 +------------------+ p5
            //      /|                 /|
            //     / |                / |
            // p7 /  |            p6 /  |
            //   +------------------+   |
            //   |   +--------------|---+---------- X
            //   |  / p0            |  / p1
            //   | /                | /
            //   |/                 |/
            //   +------------------+
            //  / p3                p2
            // Z
 
            var imageSrc = new BitmapImage(new Uri(@"C:\syaraku.JPG"));
            var material = new DiffuseMaterial(new ImageBrush(imageSrc));
 
            var group = new Model3DGroup();
            group.Children.Add(new GeometryModel3D(CreateMesh(point7, point6, point2, point3), material));    // front
            group.Children.Add(new GeometryModel3D(CreateMesh(point6, point5, point1, point2), material));    // right
            group.Children.Add(new GeometryModel3D(CreateMesh(point5, point4, point0, point1), material));    // back
            group.Children.Add(new GeometryModel3D(CreateMesh(point4, point7, point3, point0), material));    // left
            group.Children.Add(new GeometryModel3D(CreateMesh(point4, point5, point6, point7), material));    // top
            group.Children.Add(new GeometryModel3D(CreateMesh(point1, point0, point3, point2), material));    // bottom
 
            var cubeModel = new ModelVisual3D();
            cubeModel.Content = group;
            viewport.Children.Add(cubeModel);
        }
 
        private static MeshGeometry3D CreateMesh(Point3D p0, Point3D p1, Point3D p2, Point3D p3)
        {
            var mesh = new MeshGeometry3D();
            mesh.Positions = new Point3DCollection(new Point3D[] { p0, p1, p2, p3 });
            mesh.TriangleIndices = new Int32Collection(new int[] { 0, 2, 1, 0, 3, 2 });
            mesh.TextureCoordinates = new PointCollection(new Point[] { new Point(0, 0), new Point(1, 0), new Point(1, 1), new Point(0, 1) });
            return mesh;
        }
    }
}

実行結果は以下のとおり。
wpf3d-4

以上。

WPF で 3D(その3)

今回はメッシュにテクスチャを貼ってみる。

WPF のメッシュは三角形だが、一般的にテクスチャ画像は長方形だと思うので、2枚のメッシュで四角形を構成して、そこにテクスチャ画像を貼り付けることになる。

using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;
 
namespace sample1
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Init();
        }
 
        private void Init()
        {
            var root = this.Content as Grid;
 
            // trackball
            var td = new _3DTools.TrackballDecorator();
            root.Children.Add(td);
            
            // viewport
            var viewport = new Viewport3D();
            td.Content = viewport;
 
            // camera
            var camera = new PerspectiveCamera();
            camera.Position = new Point3D(14, 13, 12);
            camera.LookDirection = new Vector3D(-14, -13, -12);
            camera.UpDirection = new Vector3D(0, 1, 0);
            viewport.Camera = camera;
 
            // light
            var light = new DirectionalLight();
            light.Color = Colors.White;
            light.Direction = new Vector3D(-2, -3, -1);
            var lightModel = new ModelVisual3D();
            lightModel.Content = light;
            viewport.Children.Add(lightModel);
 
            // axis
            var xAxis = new _3DTools.ScreenSpaceLines3D();
            xAxis.Points.Add(new Point3D(-100, 0, 0));
            xAxis.Points.Add(new Point3D(100, 0, 0));
            xAxis.Color = Colors.Red;
            xAxis.Thickness = 1;
            var yAxis = new _3DTools.ScreenSpaceLines3D();
            yAxis.Points.Add(new Point3D(0, -100, 0));
            yAxis.Points.Add(new Point3D(0, 100, 0));
            yAxis.Color = Colors.Green;
            yAxis.Thickness = 1;
            var zAxis = new _3DTools.ScreenSpaceLines3D();
            zAxis.Points.Add(new Point3D(0, 0, -100));
            zAxis.Points.Add(new Point3D(0, 0, 100));
            zAxis.Color = Colors.Blue;
            zAxis.Thickness = 1;
            var axis = new ModelVisual3D();
            axis.Children.Add(xAxis);
            axis.Children.Add(yAxis);
            axis.Children.Add(zAxis);
            viewport.Children.Add(axis);
 
            // square
            var squareMesh = new MeshGeometry3D();
            var point0 = new Point3D(0, 0, 0);
            var point1 = new Point3D(5, 0, 0);
            var point2 = new Point3D(5, 0, 5);
            var point3 = new Point3D(0, 0, 5);
            squareMesh.Positions.Add(point0);
            squareMesh.Positions.Add(point1);
            squareMesh.Positions.Add(point2);
            squareMesh.Positions.Add(point3);
            squareMesh.TriangleIndices.Add(0);
            squareMesh.TriangleIndices.Add(3);
            squareMesh.TriangleIndices.Add(2);
            squareMesh.TriangleIndices.Add(0);
            squareMesh.TriangleIndices.Add(2);
            squareMesh.TriangleIndices.Add(1);
            squareMesh.TextureCoordinates.Add(new Point(0, 0));
            squareMesh.TextureCoordinates.Add(new Point(1, 0));
            squareMesh.TextureCoordinates.Add(new Point(1, 1));
            squareMesh.TextureCoordinates.Add(new Point(0, 1));
            var imageSrc = new BitmapImage(new Uri(@"C:\syaraku.JPG"));
            var material = new DiffuseMaterial(new ImageBrush(imageSrc));
            var model2 = new GeometryModel3D(squareMesh, material);
            var squareModel = new ModelVisual3D();
            squareModel.Content = model2;
            viewport.Children.Add(squareModel);
        }
    }
}

まず、メッシュで四角形を作るためには、Positions で4つの点を指定し、さらに TriangleIndices で三角形に区切る。このときの区切り方や開始点は自由だが、正面に向かって反時計周りに点を指定するよう気をつける必要がある。

テクスチャを貼る部分は、TextureCoordinates の指定を行い、DiffuseMaterial の引数を ImageBrush に変えている。TextureCoordinates はテクスチャ画像が (0,0) - (1,1) の範囲にあると想定して指定する。指定した順番は、Positions に指定した点と対応するため、指定する順番を間違えるとテクスチャ画像が違う向きになったり反転したりする。

以下、実行結果。
wpf3d-3

以上。

WPF で 3D(その2)

3D 空間に何かを表示しても座標系との位置関係が分かりづらいので、座標系を表示してみる。

ただ、残念なことに WPF はそのままでは 3D 空間に線を引く機能を持っていない。Microsoft が WPF の 3D 機能を拡張する dll を出しているので、それを使うことにする。以下からダウンロードでき、解凍したら、3DTools.dll をプロジェクトに追加する。
http://3dtools.codeplex.com/releases/view/2058

3D 空間に線を引くのは、ScreenSpaceLines3D クラスを使う。

また、3DTools には他にも有用な機能が含まれているが、今回は TrackballDecorator クラスも使う。これは、マウスで画面をドラッグすることによってカメラを操作できるというもの。左クリックしながらドラッグするとカメラ位置を変更、右クリックしながらドラッグすると拡大縮小となる。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;
 
namespace sample1
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Init();
        }
 
        private void Init()
        {
            var root = this.Content as Grid;
 
            // trackball
            var td = new _3DTools.TrackballDecorator();
            root.Children.Add(td);
            
            // viewport
            var viewport = new Viewport3D();
            td.Content = viewport;
 
            // camera
            var camera = new PerspectiveCamera();
            camera.Position = new Point3D(14, 13, 12);
            camera.LookDirection = new Vector3D(-14, -13, -12);
            camera.UpDirection = new Vector3D(0, 1, 0);
            viewport.Camera = camera;
 
            // light
            var light = new DirectionalLight();
            light.Color = Colors.White;
            light.Direction = new Vector3D(-2, -3, -1);
            var lightModel = new ModelVisual3D();
            lightModel.Content = light;
            viewport.Children.Add(lightModel);
 
            // axis
            var xAxis = new _3DTools.ScreenSpaceLines3D();
            xAxis.Points.Add(new Point3D(-100, 0, 0));
            xAxis.Points.Add(new Point3D(100, 0, 0));
            xAxis.Color = Colors.Red;
            xAxis.Thickness = 1;
            var yAxis = new _3DTools.ScreenSpaceLines3D();
            yAxis.Points.Add(new Point3D(0, -100, 0));
            yAxis.Points.Add(new Point3D(0, 100, 0));
            yAxis.Color = Colors.Green;
            yAxis.Thickness = 1;
            var zAxis = new _3DTools.ScreenSpaceLines3D();
            zAxis.Points.Add(new Point3D(0, 0, -100));
            zAxis.Points.Add(new Point3D(0, 0, 100));
            zAxis.Color = Colors.Blue;
            zAxis.Thickness = 1;
            var axis = new ModelVisual3D();
            axis.Children.Add(xAxis);
            axis.Children.Add(yAxis);
            axis.Children.Add(zAxis);
            viewport.Children.Add(axis);
 
            // triangle
            var triangleMesh = new MeshGeometry3D();
            var point0 = new Point3D(0, 0, 0);
            var point1 = new Point3D(5, 0, 0);
            var point2 = new Point3D(0, 0, 5);
            triangleMesh.Positions.Add(point0);
            triangleMesh.Positions.Add(point1);
            triangleMesh.Positions.Add(point2);
            triangleMesh.TriangleIndices.Add(0);
            triangleMesh.TriangleIndices.Add(2);
            triangleMesh.TriangleIndices.Add(1);
            var material = new DiffuseMaterial(new SolidColorBrush(Colors.LightGreen));
            var model2 = new GeometryModel3D(triangleMesh, material);
            var triangleModel = new ModelVisual3D();
            triangleModel.Content = model2;
            viewport.Children.Add(triangleModel);
        }
    }
}

実行結果は以下のとおり。座標系が表示された。マウスでカメラ位置も調整できる。
wpf3d-2

赤がX軸、緑がY軸、青がZ軸である。WPF の 3D 空間は右手座標系というもので、右がX軸の正方向、上がY軸の正方向、手前がZ軸の正方向となる。

以上。

アクセスカウンター
  • 今日:
  • 昨日:
  • 累計: