Graphics/DirectX

Direct X - 11. 키보드로 3D 카메라 시점 움직이기

MOLOKINI 2014. 6. 8. 21:03
키보드로 3D 이미지의 카메라 시점을 변경해보자




using System;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Windows.Forms;
using System.Drawing;

namespace MDXSample
{
    /// <summary>
    /// 메인 샘플 클래스
    /// </summary>
    public partial class MainSample : IDisposable
    {

        /// <summary>
        /// 정점 버퍼
        /// </summary>
        private VertexBuffer _vertexBuffer = null;

        private Texture _texture = null;

        private float _lensPosTheta = 270.0f;
        private float _lensPosPhi = 0.0f;
 
        public bool InitializeApplication(MainForm topLevelForm)
        {
            // 폼의 참조를 보관 유지
            this._form = topLevelForm;

            PresentParameters pp = new PresentParameters();
            pp.Windowed = true;
            pp.SwapEffect = SwapEffect.Discard;
            pp.EnableAutoDepthStencil = true;
            pp.AutoDepthStencilFormat = DepthFormat.D16;

            topLevelForm.KeyDown += new KeyEventHandler(this.form_KeyDown);
            topLevelForm.KeyUp += new KeyEventHandler(this.form_KeyUp);

            try
            {
                // Direct3D 디바이스 작성
                this.CreateDevice(topLevelForm, pp);
                // 폰트의 작성
                this.CreateFont();

            }
            catch (DirectXException ex)
            {
                // 예외 발생
                MessageBox.Show(ex.ToString(), "에러", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }

            this.SettingCamera();
            this.MyFace();

            this._device.RenderState.Lighting = false;
            this._device.RenderState.CullMode = Cull.None;

            return true;
        }

        /// <summary>
        /// 메인 루프 처리
        /// </summary>
        public void MainLoop()
        {
            if (this._keys[(int)Keys.Left])
            {
                this._lensPosTheta -= 3.0f;
            }
            if (this._keys[(int)Keys.Right])
            {
                this._lensPosTheta += 3.0f;
            }
            if (this._keys[(int)Keys.Up])
            {
                this._lensPosPhi += 3.0f;
            }
            if (this._keys[(int)Keys.Down])
            {
                this._lensPosPhi -= 3.0f;
            }

            if (this._lensPosPhi >= 90.0f)
            {
                this._lensPosPhi = 89.9999f;
            }
            if (this._lensPosPhi <= -90.0f)
            {
                this._lensPosPhi = -89.9999f;
            }

            float radius = 10.0f;
            float theta = Geometry.DegreeToRadian(this._lensPosTheta);
            float phi = Geometry.DegreeToRadian(this._lensPosPhi);
            Vector3 lensPosition = new Vector3((float)(radius * Math.Cos(theta) * Math.Cos(phi)), 
                (float)(radius * Math.Sin(phi)), (float)(radius * Math.Sin(theta) * Math.Cos(phi)));
            
            this.SettingCamera(lensPosition);
            
            
            // 화면을 단색(파랑색)으로 클리어
            this._device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkBlue, 1.0f, 0);

            // 「BeginScene」와「EndScene」의 사이에 화면에 출력할 내용을 코딩
            this._device.BeginScene();

            this.RenderMyFace();
                        
            // 좌표 x=0 y=0 의 위치에 흰색으로 출력
            this._font.DrawText(null, "텍스쳐 입혀보기", 0, 0, Color.White);
            
            // 화면출력은 여기까지
            this._device.EndScene();

            // 실제의 디스플레이에 출력
            this._device.Present();

        }

        private void form_KeyDown(object sender, KeyEventArgs e)
        {
            if ((int)e.KeyCode < this._keys.Length)
            {
                this._keys[(int)e.KeyCode] = true;
            }
        }

        private void form_KeyUp(object sender, KeyEventArgs e)
        {
            if ((int)e.KeyCode < this._keys.Length)
            {
                this._keys[(int)e.KeyCode] = false;
            }
        }

        /// <summary>
        /// 자원의 파기
        /// </summary>
        public void Dispose()
        {
            if (this._texture != null)
            {
                this._texture.Dispose();
            }
            // 정점 버퍼를 해제
            if (this._vertexBuffer != null)
            {
                this._vertexBuffer.Dispose();
            }

            // 폰트 자원을 해제
            if (this._font != null)
            {
                this._font.Dispose();
            }

            // Direct3D 디바이스의 자원 해제
            if (this._device != null)
            {
                this._device.Dispose();
            }
        }
    }
}


        private float _lensPosTheta = 270.0f;
        private float _lensPosPhi = 0.0f;
내 사진을 기준으로 카메라가 그 주변을 비추게 하고 있다, 그 계산으로 삼차원 극좌표의 공식을 사용하고있다는데 
파이와 세타는 쉽게
파이 : 상하이동
세타 : 좌우이동ㅇㅋ?
반경 R은 고정

 카메라의 초기 위치는 -Z방향(0.0, 0.0, -x)의 위치에 배치, 원점(0.0, 0.0, 0.0)을 보고 있는 형상이다.



            this.SettingCamera();
            this.MyFace();
카메라와 내 얼굴 텍스쳐를 메서드화 시켰다
카메라설정하는건 6, 7강을 다시한번 참고하시고요
텍스쳐는 9강을 보면됩니다.
그리구 카메라설정부분은 메서드오버로딩 했다 


            this._device.RenderState.CullMode = Cull.None;
이거는 다각형 그러니까 내 사진의 뒷면을 보이게 할것이냐 말것이냐를 결정한다
이렇게 하면 뒷면이 보인다
하지만, 이걸 안쓰면 뒷면이 안보이는데, 3차원 입체 형상은 2차원이 6면체로 둘러싸는거잖아? 그렇타면 굳이 2차원의 뒷면을 볼 필요가 없잖아? 그치? 그러면 이 한줄을 없애버리므로서 연산의 오버헤드가 줄어든다,,
이거는 단면으로 했을 때, 어느면이 앞면이 되고 어느면이 뒷면이 되는가 하는것.
정점이 어느방향으로 세트되어있느냐에따라 앞면과 뒷면이 달라.
2번 정점이 잇는 방향으로 돌리면 그게바로 뒷면이 되는것. 


            if (this._keys[(int)Keys.Left])
            {
                this._lensPosTheta -= 3.0f;
            }
            if (this._keys[(int)Keys.Right])
            {
                this._lensPosTheta += 3.0f;
            }
            if (this._keys[(int)Keys.Up])
            {
                this._lensPosPhi += 3.0f;
            }
            if (this._keys[(int)Keys.Down])
            {
                this._lensPosPhi -= 3.0f;
            }

            if (this._lensPosPhi >= 90.0f)
            {
                this._lensPosPhi = 89.9999f;
            }
            if (this._lensPosPhi <= -90.0f)
            {
                this._lensPosPhi = -89.9999f;
            }

키입력에따라 세타와 파이값을 변경하고
맨 아래 두 if문은 파이값으 최대최소값을 90으로 잡은거야
왜 90일까, 이미지가 90도 회전하면 안보이겠지요? 2차원이미지를 3차원 공간에서 보는거니

 ㅇㅋ?



            float radius = 10.0f;
            float theta = Geometry.DegreeToRadian(this._lensPosTheta);
            float phi = Geometry.DegreeToRadian(this._lensPosPhi);
            Vector3 lensPosition = new Vector3((float)(radius * Math.Cos(theta) * Math.Cos(phi)), 
                (float)(radius * Math.Sin(phi)), (float)(radius * Math.Sin(theta) * Math.Cos(phi)));
카메라의 위치는 X, Y, Z의 위치로 고쳐야 하기 때문에 R, 세타, 파이로부터 삼차원 극좌표의 공식에따라 변환한다
또한, 각도는 라디안으로 계산해야하기 때문에, 세타와 파이를 Geometry.DegreeToRadian 메서드로부터 라디안으로 변환하구있다

공식이야
X = 라디안 * 코사인세타 * 코사인파이
Y = 라디안 * 사인파이
Z = 라디안 * 사인세타 * 코사인파이
요공식을 대입하면 카메라의 위치 벡터를 구해낼 수 있다,, 


            this.SettingCamera(lensPosition);
메서드 오버로딩을 통해서 카메라의 위치를 변경하구있다
        private void SettingCamera(Vector3 lensPosition)
        {
            this._device.Transform.View = Matrix.LookAtLH(lensPosition,
                new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f));
// 아래삭제
        }
메서드의 일부야 lensPosition 변수를 받아서 카메라의 위치를 왼손좌표를 기준으로 해서 바꾸고있어
lensPosition 변수는 위에 공식에 보이지요?

키보드로 약간 틀어봤다,