본문 바로가기
백엔드기술/개발언어

3D 기초 강좌 - 1

by RevFactory 2009. 2. 6.

안녕하세요!

레브민호 입니다.

 

틈틈히 강좌를 하나 진행해볼까 합니다.

강좌 주제는 3D기초 강좌 입니다. 써먹을데가없습니다. 공부용입니다..ㅎㅎ

 

1. 호도법과 라디안

2. 사인과 코사인

3. 2D의 이해

4. 애니메이션

4. X-Y-Z축의 이해

5. 원근감

6. 3D공간 평면 회전

7. 3D공간 큐브 회전

 

이렇게 대충 목차를 정해보았습니다.

모든 강좌는 실버라이트 기반으로 작성되어질 예정입니다.

코드설명은 주석으로 대체할 예정이며 주로 이론을 정리할까 합니다.

 

저도 미숙하지만 공부한다는 생각으로 준비해보겠습니다.

, 마지막 강좌까지 얼마나 걸릴지 저도 장담은 못합니다. ^^;

 

=========================================================

1. 호도법과 라디안

2. 사인과 코사인

3. 2D의 이해

4. 애니메이션

4. X-Y-Z축의 이해

5. 원근감

6. 3D공간 평면 회전

7. 3D공간 큐브 회전

 

 

1. 호도법과 코사인

일상 생활에서 가장 널리 쓰이고 있는 것은 60분법이죠. 일반적으로 “45, 90이렇게

사용하는 방식입니다. 다시 말해 원 둘레를 360등분하여 각 원호의 중심각 크기를 1도로

하고 이것을 단위로 각을 측정합니다.

 

수학이나 공학에서는 60분법이 아닌 호도법을 사용하죠.

호도법은 반지름 r인 원에서 길이가 r인 원호에 대한 중심각 크기를 1호도 또는 1라디안

이라 하고 기호로 1rad로 씁니다. (그림생략-인터넷참조)

 

반지름 r인 원의 둘레는 2πr 이고 이것은 반지름의배란 의미가 되므로 원의 중심각은

2π rad 입니다. 이때 원의 중심각은 60분법에 의해 360° 이므로

2π rad = 360°

, π rad = 180° 입니다.

 

프로그래밍시 π Math.PI 로 사용할 수 있습니다.

 

첫 강좌.. 쉬워서 더 쓸 게 없군요. ^^;

 

 

그럼 예제 코드 들어갑니다.

 

CustomEllipse.xaml 공과 공 주변에 각을 표시합니다.

<Canvas x:Name="LayoutRoot" >

        <!--축구공-->

        <Image Source="ball.png" Width="200" Height="200" />

        <!--축구공 중앙 십자라인-->

        <Path Width="200" Height="200" Fill="#FFFFFFFF" Stretch="Fill" Stroke="#FF000000" Data="M0,0 L1,0" Canvas.Left="0" Canvas.Top="100"/>

        <Path Width="200" Height="200" Fill="#FFFFFFFF" Stretch="Fill" Stroke="#FF000000" Data="M0,0 L0,1" Canvas.Left="100" Canvas.Top="0"/>

        <!--축구공 주변 각도표시-->

        <TextBlock Width="40" Height="20" Canvas.Left="-30" Canvas.Top="95" Text="270˚" TextAlignment="Center" TextWrapping="Wrap">

            <TextBlock.RenderTransform>

                <RotateTransform CenterX="20" CenterY="10" Angle="90" />

            </TextBlock.RenderTransform>

        </TextBlock>

        <TextBlock Width="40" Height="20" Canvas.Left="75" Canvas.Top="-20" Text="180˚" TextAlignment="Center" TextWrapping="Wrap">

        <TextBlock.RenderTransform>

            <RotateTransform CenterX="20" CenterY="10" Angle="180" />

        </TextBlock.RenderTransform>

        </TextBlock>

        <TextBlock Width="40" Height="20" Canvas.Left="190" Canvas.Top="95" Text="90˚" TextAlignment="Center" TextWrapping="Wrap">

        <TextBlock.RenderTransform>

            <RotateTransform CenterX="20" CenterY="10" Angle="270" />

        </TextBlock.RenderTransform>

        </TextBlock>

        <TextBlock Width="40" Height="20" Canvas.Left="80" Canvas.Top="200" Text="0˚" TextAlignment="Center" TextWrapping="Wrap"/>

    </Canvas>

 

 

 

Page.xaml

<UserControl x:Class="lecture01.Page"

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

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

    xmlns:user="clr-namespace:lecture01"         

    Width="800" Height="300" Loaded="UserControl_Loaded">

    <Canvas x:Name="LayoutRoot" Background="Beige">

 

        <!--원과 라인-->

        <user:CustomEllipse x:Name="MyEllipse" Canvas.Left="20" Canvas.Top="20">

            <user:CustomEllipse.RenderTransform>

                <TransformGroup>

                    <RotateTransform CenterX="100" CenterY="100" Angle="0" />

                </TransformGroup>

            </user:CustomEllipse.RenderTransform>

        </user:CustomEllipse>          

        <Path x:Name="MyEllipseline" Width="100" Height="100" Fill="#FFFFFFFF" Stretch="Fill" StrokeThickness="2" Stroke="#FF000000" Data="M0,0 L1,0" Canvas.Left="120" Canvas.Top="220"/>

   

        <!--재생시간-->

        <TextBlock Text="시간:" Canvas.Left="10" Canvas.Top="235"/>

        <TextBox x:Name="BoxSecond" Width="30" Height="22" Canvas.Left="40" Canvas.Top="232" Text="3" TextAlignment="Center" KeyUp="BoxSecond_KeyUp"/>

        <TextBlock Text="" Canvas.Left="73" Canvas.Top="235"/>

       

        <!--제어버튼-->

        <Button x:Name="BtnStart" Content="Start" Canvas.Left="10" Canvas.Top="260" Width="80" Height="30" Click="BtnStart_Click" />

        

        <!--상태표시-->

        <TextBlock x:Name="TxtDistance" Canvas.Left="100" Canvas.Top="250" Width="100" Height="20" Text="Distance : 0"/>

        <TextBlock x:Name="TxtAngle" Canvas.Left="100" Canvas.Top="270" Width="100" Height="20" Text="Angle : 0˚"/>

        <TextBlock x:Name="TxtRadian" Canvas.Left="190" Canvas.Top="270" Width="100" Height="20" Text="Radian : 0 rad"/>

       

        <!--설명-->

        <TextBlock Canvas.Left="300" Canvas.Top="230" Width="100" Height="20"

Text="1 radian = 2*r (diameter)"/>

        <TextBlock Canvas.Left="300" Canvas.Top="245" Width="100" Height="20" Text="360˚(degree) = 3.14 * 2 * r"/>

        <TextBlock Canvas.Left="300" Canvas.Top="260" Width="100" Height="20" Text="radian : π(3.14) = degree : 180"/>

        <TextBlock Canvas.Left="300" Canvas.Top="275" Width="100" Height="20"

Text="radian = degree * PI / 180"/>

    </Canvas>

</UserControl>

 

 

 

 

 

Page.xaml.cs

public partial class Page : UserControl

    {

        int direct = 1;           // 객체 회전 방향 (1:오른쪽, -1: 왼쪽)

        bool IsMove;            // 객체 회전

        bool IsPause;            // 객체 회전 잠시 중지       

        double girth;            // 객체 둘레 (2*PI*r)

        double r = 200 / 2;      // 객체의 반지름

        double beginningPostion; // 객체의 최초 Canvas.Left

        Storyboard currentMove;  // 이동,회전 애니메이션

        Storyboard statusChange; // 객체 상태값을 보여주기 위함

 

        public Page()

        {

            InitializeComponent();

        }

 

        private void UserControl_Loaded(object sender, RoutedEventArgs e)

        {          

            girth = 2 * Math.PI * r;

            beginningPostion = (double)MyEllipse.GetValue(Canvas.LeftProperty);

            MyEllipseline.Width = girth;      // 객체 아래 라인의 길이를 객체 둘레만큼 변경

           

            // 객체 상태값을 보여준다.

            statusChange = new Storyboard();

            statusChange.Completed += new EventHandler(status_Completed);

            statusChange.Begin();

        }

 

        /// <summary>

        /// 객체의 상태 값에따라 Textblock 값을 변경한다.

        /// </summary>

        void status_Completed(object sender, EventArgs e)

        {                       

            TransformCollection myTransforms = MyEllipse.RenderTransform.GetValue(TransformGroup.ChildrenProperty) as TransformCollection;

            RotateTransform roate = myTransforms[0] as RotateTransform;

            TxtAngle.Text = string.Format("Angle : {0:F1}˚", roate.Angle);

            TxtRadian.Text = string.Format("Radian : {0:F1}rad", roate.Angle * Math.PI / 180);

            TxtDistance.Text = string.Format("Distance : {0:F2}", (double)MyEllipse.GetValue(Canvas.LeftProperty) -20);

 

            statusChange.Begin();            // 계속 반복

        }

 

        private void BtnStart_Click(object sender, RoutedEventArgs e)

        {

            // 이동중일때

            if (IsMove)

            {

                // 잠시 멈춤 상태면 재개한다

                if (IsPause)

                {

                    currentMove.Resume();

                    IsPause = false;

                    BtnStart.Content = "Pause";

                }

                // 잠시 멈춘다

                else

                {

                    currentMove.Pause();

                    IsPause = true;

                    BtnStart.Content = "Resume";

                }

            }

            // 이동이 끝났을때

            else

            {              

                int playTime = int.Parse(BoxSecond.Text);

                // 오른쪽으로 이동 시작

                if (direct == 1)

                {

                    currentMove =

ObjectMove(MyEllipse, beginningPostion + girth, 360, playTime);

                    currentMove.Begin();

                }

                // 왼쪽으로 이동 시작

                else if (direct == -1)

                {

                    currentMove = ObjectMove(MyEllipse, beginningPostion, 0, playTime);

                    currentMove.Begin();

                }

                BtnStart.Content = "Pause";

                IsMove = true;

            }           

        }

 

        /// <summary>

        /// 애니매이션을 위한 스토리보드 객체 생성

        /// </summary>

        /// <param name="element">이동할 객체</param>

        /// <param name="targetPostion">이동할 X </param>

        /// <param name="targetAngle">회전할 angle</param>

        /// <param name="durationTime">재생 시간</param>

        /// <returns></returns>

        private Storyboard ObjectMove(FrameworkElement element, double targetPostion, double targetAngle, double durationTime)

        {

            // 스토리 보드 객체와 재생 시간

            Storyboard sb = new Storyboard();

            Duration duration = new Duration(TimeSpan.FromSeconds(durationTime));

            sb.Duration = duration;

 

            // 객체의 수평 이동

            DoubleAnimation myDoubleAnimation = new DoubleAnimation();           

            myDoubleAnimation.Duration = duration;           

            sb.Children.Add(myDoubleAnimation);

            Storyboard.SetTarget(myDoubleAnimation, element);

            Storyboard.SetTargetProperty(myDoubleAnimation,

new PropertyPath("(Canvas.Left)"));

            myDoubleAnimation.To = targetPostion;

 

            // 객체의 회전

            DoubleAnimation rotateAnimation = new DoubleAnimation();

            rotateAnimation.Duration = duration;

            sb.Children.Add(rotateAnimation);

            Storyboard.SetTarget(rotateAnimation, element);

            Storyboard.SetTargetProperty(rotateAnimation,

                new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"));

            rotateAnimation.To = targetAngle;

 

            sb.Completed += new EventHandler(sb_Completed); //애니메이션이 끝났을때 이벤트

            return sb;

        }

 

        /// <summary>

        /// 애니메이션이 끝나면 방향을 바꾸고 플래그 값을 변경한다.

        /// </summary>

        void sb_Completed(object sender, EventArgs e)

        {

            direct *= -1;

            IsMove = false;

            BtnStart.Content = "Start";

        }

 

 

        /// <summary>

        /// 임시 예외처리

        /// </summary>

        private void BoxSecond_KeyUp(object sender, KeyEventArgs e)

        {

            if (e.Key >= Key.A && e.Key <= Key.Z)

                (sender as TextBox).Text = "";

        }

    }