'WPF'에 해당되는 글 2건

  1. 2006/12/26 XAML로 스피어-메시 생성
  2. 2006/11/25 WPF 관련 자료

XAML로 스피어-메시 생성

View Comments

A Korean translation of 「 Generating a sphere-mesh in XAML


제가 월간 마이크로소프트웨어 2007년 1월호에 기고한 글에 나오는 WPF 예제입니다.

사용자 삽입 이미지


























소개


일전에 WPF에서 제공하는 3D 기능을 재미있게 다루는 방법에 대해 생각해 보았는데, 2D 그래픽과 애니메이션 자료 밖에 없어서 MSDN 뒤져봤다. MSDN에서 3D 그래픽을 소개하는 내용을 찾아냈고, 코드프로젝트에서는 XAML 3D 구현하는데 도움이 될만한 3D in XAML이란 아티클을 찾아냈다. 아티클에서는 카메라 기초상식, 메시, 등에 대한 설명은 하고 있지 않다.


MSDN
에서 "지금은 스피어와 큐빅 같은 미리 정의된 3-D 도형은 지원하지 않는다 "라는 내용을 읽었을 적잖이 실망했다. WPF에서 제공하는
MeshGeometry3D 클래스는 삼각형 목록과 같은 도형을 빌드하는데 그칠 뿐이어서, WPF 3D 미니프로젝트에서는 스피어를 보여주는 메시를 생성하는 알고리즘을 구현하기록 결심했다.


아쉽게도
필자는 3D 그래픽을 구현하는데 능숙하지 못하다. 그래서, 삼각형 메시에서 스피어를 생성하는 비교적 간단한 알고리즘을 구현하려고 하며, 이를 위해 오픈소스 3D 모델러 블렌더(Blender)에서 제공하는 UVSphere 메시를 사용하려고 한다.


사용자 삽입 이미지













(
소스: 위키: Grundkörper)


그림에서 보이는 대로, 스피어를 선분(segment) (ring)으로 나누었다. 결과 부분과 아래 부분에는 (삼각형 개로 나눌 있는) 정사각형과 삼각형 모형이 보이게 된다. 블렌더의 Icosphere( 자세한 내용은 위키iki: Ikosaeder 참조) XAML 메시에 적합하긴 하지만, 필자는 UVSphere 시작하려고 한다.


스피어는
단지 라운드 메시(round mesh) 아니며, 원을 선분으로 나눌 생성되기도 한다. 그래서 3D 공간에서 원을 구현하는데 추상 기본 클래스(abstract base class) 사용하려고 한다.

using System;

using System.Windows.Media;

using System.Windows.Media.Media3D;

namespace Sphere3D

{

  abstract class RoundMesh3D

  {

       protected int n = 10;

       protected int r = 20;

       protected Point3DCollection points;

       protected Int32Collection triangleIndices;

       public virtual int Radius

       {

           get { return r; }

           set { r = value; CalculateGeometry(); }

       }

       public virtual int Separators

       {

           get { return n; }

           set { n = value; CalculateGeometry(); }

       }

       public Point3DCollection Points

       {

           get { return points; }

       }

       public Int32Collection TriangleIndices

       {

           get { return triangleIndices; }

       }

       protected abstract void CalculateGeometry();

  }

}


r
그물 모양(mesh) 반지름을 나타내고, n 원을 개의 선분으로 나눌 건지를 나타낸다(4*n+4 원을 동일하게 나누는데 사용하는 점의 ).


번째 테스트는 원반 구현으로, 아래에 코드가 있다. 삼각함수에 대한 내용일 뿐이라 그다지 어렵지는 않다.

using System;

using System.Windows.Media;

using System.Windows.Media.Media3D;

using System.Diagnostics;

namespace Sphere3D

{

  class DiscGeometry3D : RoundMesh3D

  {

       protected override void  CalculateGeometry()

       {

           int numberOfSeparators = 4 * n + 4;

           points = new Point3DCollection(numberOfSeparators + 1);

           triangleIndices = new Int32Collection((numberOfSeparators + 1) * 3);

           points.Add(new Point3D(0, 0, 0));

           for (int divider = 0; divider < numberOfSeparators; divider++)

           {

               double alpha = Math.PI / 2 / (n + 1) * divider;

               points.Add(new Point3D(r * Math.Cos(alpha),

                          0, -1 * r * Math.Sin(alpha)));

               triangleIndices.Add(0);

               triangleIndices.Add(divider + 1);

               triangleIndices.Add((divider ==

                 (numberOfSeparators-1)) ? 1 : (divider + 2));

           }

       }

       public DiscGeometry3D()

       { }

  }

}


스피어를
생성하는 코드는 조금 길지만, 스피어를 점으로 나누는 정말 간단하다. 삼각형 생성이 어렵긴 하지만 말이다.

using System;

using System.Windows.Media;

using System.Windows.Media.Media3D;

using System.Diagnostics;

namespace Sphere3D

{

  class SphereGeometry3D : RoundMesh3D

  {

       protected override void CalculateGeometry()

       {

           int e;

           double segmentRad = Math.PI / 2 / (n + 1);

           int numberOfSeparators = 4 * n + 4;

           points = new Point3DCollection();

           triangleIndices = new Int32Collection();

           for (e = -n; e <= n; e++)

           {

               double r_e = r * Math.Cos(segmentRad * e);

               double y_e = r * Math.Sin(segmentRad * e);

               for (int s = 0; s <= (numberOfSeparators - 1); s++)

               {

                   double z_s = r_e * Math.Sin(segmentRad * s) * (-1);

                   double x_s = r_e * Math.Cos(segmentRad * s);

                  points.Add(new Point3D(x_s, y_e, z_s));

               }

           }

           points.Add(new Point3D(0, r, 0));

           points.Add(new Point3D(0, -1 * r, 0));

           for (e = 0; e < 2 * n; e++)

           {

               for (int i = 0; i < numberOfSeparators; i++)

               {

                   triangleIndices.Add(e * numberOfSeparators + i);

                   triangleIndices.Add(e * numberOfSeparators + i +

                                       numberOfSeparators);

                   triangleIndices.Add(e * numberOfSeparators + (i + 1) %

                                       numberOfSeparators + numberOfSeparators);

                   triangleIndices.Add(e * numberOfSeparators + (i + 1) %

                                      numberOfSeparators + numberOfSeparators);

                   triangleIndices.Add(e * numberOfSeparators +

                                      (i + 1) % numberOfSeparators);

                   triangleIndices.Add(e * numberOfSeparators + i);

               }

           }

           for (int i = 0; i < numberOfSeparators; i++)

           {

               triangleIndices.Add(e * numberOfSeparators + i);

               triangleIndices.Add(e * numberOfSeparators + (i + 1) %

                                  numberOfSeparators);

               triangleIndices.Add(numberOfSeparators * (2 * n + 1));

           }

           for (int i = 0; i < numberOfSeparators; i++)

           {

               triangleIndices.Add(i);

              triangleIndices.Add((i + 1) % numberOfSeparators);

               triangleIndices.Add(numberOfSeparators * (2 * n + 1) + 1);

           }

       }

       public SphereGeometry3D()

       { }

  }

}


예제에서는 아티클 위에 있는 그림처럼 스피어 개와 배경의 멋진 그림을 보여주려고 한다. 그래서
SphereGeometry3D에서 상속 받는 클래스 개를 생성하기로 했다.

namespace Sphere3D

{

  class BigPlanet : SphereGeometry3D

  {

       BigPlanet()

       {

           Radius = 30;

           Separators = 5;

     }

  }

  class SmallPlanet : SphereGeometry3D

  {

       SmallPlanet()

       {

           Radius = 5;

           Separators = 5;

       }

  }

}


마지막으로
, 위에서 그림처럼 실행되도록 하기 위해서는
MeshGeometry3D Positions TriangleIndices 바인딩 해야 한다. 여기에서는 XAML 데이터 바인딩 메커니즘을 사용하여 처리하도록 한다.

<Window x:Class="Sphere3D.Window1"

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  xmlns:local="clr-namespace:Sphere3D"

  Title="Labyrinth3d" Height="600" Width="600"

  >

  <Window.Background>

       <ImageBrush Stretch="UniformToFill"

                   ImageSource="Images/Pleiades.jpg"/>

  </Window.Background>

  <Grid VerticalAlignment="Stretch"

            HorizontalAlignment="Stretch" x:Name="Grid1">

       <Grid.Resources>

           <local:BigPlanet x:Key="SphereGeometrySource1"/>

           <local:SmallPlanet x:Key="SphereGeometrySource2"/>

           <MeshGeometry3D x:Key="SphereGeometry1"

                 Positions="{Binding Source={StaticResource

                            SphereGeometrySource1}, Path=Points}"

               TriangleIndices="{Binding Source={StaticResource

                                 SphereGeometrySource1},

                                 Path=TriangleIndices}"/>

           <MeshGeometry3D x:Key="SphereGeometry2"

                   Positions="{Binding Source={StaticResource

                             SphereGeometrySource2}, Path=Points}"

               TriangleIndices="{Binding Source={StaticResource

                                SphereGeometrySource2},

                                Path=TriangleIndices}"/>

       </Grid.Resources>

      

       <Grid.ColumnDefinitions>

           <ColumnDefinition Width="20"/>

           <ColumnDefinition Width="*"/>

           <ColumnDefinition Width="20"/>

       </Grid.ColumnDefinitions>

       <Grid.RowDefinitions>

           <RowDefinition Height="20"/>

           <RowDefinition Height="*"/>

           <RowDefinition Height="20"/>

       </Grid.RowDefinitions>

       <Viewport3D Grid.Column="1" Grid.Row="1"

                   VerticalAlignment="Stretch"

                  HorizontalAlignment="Stretch" Name="Viewport1">

           <Viewport3D.Camera>

               <PerspectiveCamera x:Name="myCamera" Position="100 30 0"

                     LookDirection="-50 -33 0"

                     UpDirection="0,1,0" FieldOfView="90"/>

               <!--<OrthographicCamera x:Name="myCamera"

                     Position="200 0 0" LookDirection="-1 0 0"

                     Width="180" UpDirection="0,1,0"/>-->

           </Viewport3D.Camera>

           <ModelVisual3D>

               <ModelVisual3D.Content>

                   <Model3DGroup>

                       <DirectionalLight Color="#FFFFFF"

                                Direction="0 -30 0" />

                       <DirectionalLight Color="#FFFFFF"

                               Direction="0 +30 0" />

                       <GeometryModel3D

                              Geometry="{StaticResource SphereGeometry1}">

                           <GeometryModel3D.Material>

                               <MaterialGroup>

                                   <DiffuseMaterial>

                                       <DiffuseMaterial.Brush>

                                           <SolidColorBrush Color="Orange"/>

                                       </DiffuseMaterial.Brush>

                                   </DiffuseMaterial>

                               </MaterialGroup>

                           </GeometryModel3D.Material>

                       </GeometryModel3D>

                       <GeometryModel3D

                             Geometry="{StaticResource SphereGeometry2}">

                           <GeometryModel3D.Material>

                               <DiffuseMaterial>

                                   <DiffuseMaterial.Brush>

                                      <SolidColorBrush Color="Yellow"/>

                                   </DiffuseMaterial.Brush>

                               </DiffuseMaterial>

                           </GeometryModel3D.Material>

                          <GeometryModel3D.Transform>

                               <TranslateTransform3D

                                    x:Name="Sphere2Translation" OffsetZ="50" />

                           </GeometryModel3D.Transform>

                       </GeometryModel3D>

                   </Model3DGroup>

               </ModelVisual3D.Content>

           </ModelVisual3D>

       </Viewport3D>

  </Grid>

</Window>


출처
: http://www.codeproject.com/WPF/XamlUVSphere.asp


관련 파일 :



PDF 다운로드 :



이 글은 고수닷넷, 데브피아, 훈스닷넷에도 게시되었습니다.

2006/12/26 15:25 2006/12/26 15:25

댓글0 Comments (+add yours?)

트랙백0 Tracbacks (+view to the desc.)

WPF 관련 자료

View Comments

[MSDN 웹 캐스트] .NET Framework 3.0, WPF의 물결 웹 캐스트 시리즈
http://www.microsoft.com/korea/events/WPF1/default.mspx

WPF(windows presentation foundation) 소개
http://network.hanbitbook.co.kr/view.php?bi_id=1307

템플릿을 사용한 WPF 컨트롤 사용자 지정
http://msdn.microsoft.com/msdnmag/issues/07/01/Foundations/default.aspx?loc=ko

Expression Blend Beta Preview
http://movielibrary.lynda.com/html/modPage.asp?ID=359&ref=swf
2006/11/25 17:01 2006/11/25 17:01

댓글0 Comments (+add yours?)

트랙백0 Tracbacks (+view to the desc.)

Newer Entries Older Entries