WPF 강좌와TIP 게시판입니다. - 시삽보기

제목: XAML로 스피어-메시 생성
글쓴이: 이광수
평점: 없음
조회: 1086
첨부파일

 

제가 월간 마이크로소프트웨어 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) 사용하려고 한다.

 

Collapse

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 원을 동일하게 나누는데 사용하는 점의 ).

 

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

 

Collapse

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()

        { }

    }

}

 

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

 

Collapse

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 데이터 바인딩 메커니즘을 사용하여 처리하도록 한다.

 

Collapse

<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

 

 

문서는 날개달기에 의해 번역되었습니다.

작성자 정보

글등록 +12 408
덧글등록 +3 17
이름: 이광수(magicbullet )

Level 19
 [EXP.9/70 ]


메일: webwings골뱅이gmail.com

홈페이지: www.paewang.net
자기소개
글 공유하기 |
 tweet facebook
2006-12-26 오후 2:43:23
IP: 221.150.217.80
        
트랙백 주소 : http://www.hoons.kr/4419/BoardTrackback.aspx ()
등록된 트랙백 0

나도한마디
태그로 엮인글 리스트


사용자 정보

close