Graphics/DirectX

Direct X - 7. 3D 공간에 3D 삼각형 출력

MOLOKINI 2014. 6. 1. 21:09
실행했을때에는 4, 5강과 같은 똑같은 모양의 삼각형이 보이지만,
사실은 3차원공간에 서있는 삼각형이다^^

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;

        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;

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

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

            this._device.Transform.View = Matrix.LookAtLH(new Vector3(0.0f, 0.0f, -10.0f),                 new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f));

            this._device.Transform.Projection = Matrix.PerspectiveFovLH(Geometry.DegreeToRadian(60.0f), (float)this._device.Viewport.Width / (float)this._device.Viewport.Height, 1.0f, 100.0f);


            // 삼각형 다각형을 표시하기 위한 정점 버퍼를 작성
            this._vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored),
                3, this._device, Usage.None, CustomVertex.PositionColored.Format, Pool.Managed);

            // 3점의 정보를 보관하기 위한 메모리를 확보
            CustomVertex.PositionColored[] vertices = new CustomVertex.PositionColored[3];

            // 각 정점을 설정
            vertices[0] = new CustomVertex.PositionColored(0.0f, 5.0f, 0.0f, Color.Red.ToArgb());
            vertices[1] = new CustomVertex.PositionColored(4.0f, -3.0f, 0.0f, Color.Blue.ToArgb());
            vertices[2] = new CustomVertex.PositionColored(-4.0f, -3.0f, 0.0f, Color.Green.ToArgb());

            // 정점 버퍼를 잠근다
            using (GraphicsStream data = this._vertexBuffer.Lock(0, 0, LockFlags.None))
            {
                // 정점 데이터를 정점 버퍼에 씁니다
                data.Write(vertices);

                // 정점 버퍼의 락을 해제합니다
                this._vertexBuffer.Unlock();
            }

            this._device.RenderState.Lighting = false;

            return true;
        }

        /// <summary>
        /// 메인 루프 처리
        /// </summary>
        public void MainLoop()
        {
            // 화면을 단색(파랑색)으로 클리어
            this._device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkBlue, 1.0f, 0);

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

            // 정점 버퍼를 디바이스의 데이터 스트림에 바인드
            this._device.SetStreamSource(0, this._vertexBuffer, 0);

            // 그릴려는 정점의 포맷을 세트
            this._device.VertexFormat = CustomVertex.PositionColored.Format;

            // 렌더링(그리기)
            this._device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
                        
            // 좌표 x=0 y=0 의 위치에 흰색으로 출력
            this._font.DrawText(null, "3차원 공간에 3D 삼각형 출력", 0, 0, Color.White);
            
            // 화면출력은 여기까지
            this._device.EndScene();

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

        }

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

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

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

 
        private VertexBuffer _vertexBuffer = null;
정점버퍼선언, 두번이나 나왔으니까 넘어가자
 
 
            PresentParameters pp = new PresentParameters();
            pp.Windowed = true;
            pp.SwapEffect = SwapEffect.Discard;
            pp.EnableAutoDepthStencil = true;
            pp.AutoDepthStencilFormat = DepthFormat.D16;
까먹었을까봐 다시한번 써본다.
PresentParameters : 디바이스를 작성할 때 필수적으로 써 주어야 해, 어떤 환경에서 디바이스를 사용할 것인지 결정하는 파라미터야.
 
            pp.EnableAutoDepthStencil = true;
            pp.AutoDepthStencilFormat = DepthFormat.D16;
이거 두개를 다시한번 보자.
EnableAutoDepthStencil = true : 이제까지 false였지만, Z버퍼(심도버퍼)를 사용하기위해 true
AutoDepthStencilFormat = DepthFormat.D16 : 심도버퍼의 정도, D16은 정도가 좀 떨어지긴 하지만, 많은 시스템 환경에서 사용할 수 있다, 컴퓨터의 성능이 좋다면 D24로 써도 된다
 
 
this._device.Transform.View = Matrix.LookAtLH(new Vector3(0.0f, 0.0f, -10.0f),
                 new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f));
이것은 뷰 좌표변화에 해당되는 것으로, 
카메라의 위치, 카메라의 주시점, 카메라의 위쪽 방향이 필요하다.
이 변환을 하는데 사용되는 것이 바로 Matrix.LookAtLH 함수,
이 함수를 사용하게 되면, 왼손 좌표계 뷰 행렬이 생성된다.
기본적으로 좌표변화라고하면 4x4의 행렬(매트릭스)이 사용됩니다. 왜 매트릭스가 사용되는지는 다음에 알아보기로하고, 메서드의 인자의 역할을 보도록합시다
 
생성된 뷰 좌표변화 매트릭스를 Device.Transform.View에 세트하는 것
Matrix.LookAtLH
cameraPosition : 카메라의 위치
cameraTarget : 카메라의 주시점
cameraUpVector : 월드의 위쪽
맨 앞 카메라의 위치부분을 수정해보면, Vector3(0.0f, 0.0f, -10.0f)
앞수치는 좌우방향, 중앙수치는 앞뒤방향, 맨뒤는 대각선방향에서 삼각형을 보는 위치로 수정되는데, 이것역시 왼손좌표계의 영향이다.
 
그러니까 저 수치들로 해서 보고있는 카메라의 방향은 아래 사진처럼 되는거야.
 
 
this._device.Transform.Projection = Matrix.PerspectiveFovLH(Geometry.DegreeToRadian(60.0f), (float)this._device.Viewport.Width / (float)this._device.Viewport.Height, 1.0f, 100.0f);
이번에는 투영좌표변화 처리
Matrix.PerspectiveFovLH 메서드를 사용하는 것으로 왼손좌표계 원근 투영 행렬을 생성한다. 그리고 이것을 Device.Transform.Projection에 건네주고 있다. 이것에 의해 출력시 이 매트릭스가 투영변환으로 사용되게 되는것!
 
Matrix.PerspectiveFovLH
fieldOfViewY : Y축 방향의 시야각, 각도는 라디안으로 건네줘야하지만, 라디안은 직감적으로 이해하기 힘들어서 Geometry.DegreeToRadian을 써서 건네주고있다.
aspectRatio : 어스펙트비, 스크린의 폭과 세로의 비율, 기본적으로 폭÷높이를 지정하면 제대로 표시됩니다, 그래서 여기서도 그렇게 했꾸, 요게 바뀌면 화면안의 물체가 늘어나게됭, 이번에는 뷰포트의 사이즈를 사용해서 계산하고있어
znearPlane : 카메라에 가까운 뷰면의 클리핑 Z치
zfarPlane : 카메라로부터 먼 뷰면의 클리핑 Z치
 
 
   this._vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored), 3, this._device, Usage.None, CustomVertex.PositionColored.Format, Pool.Managed);
전에 나왔던거랑 얼핏 같아보이지만 사실 다르다.
TransformedColored에서 PositionColored로 바뀌었다. 구조체가 변한것이다.
전과 같이 위치와 색 정보를 갖게되고, 전에는 좌표변화가 끝난 정점에 대해서, 이번에는 좌표변화를 실시하지 않은 정점이 되는것
그러니까, 다시말하자면
전에는 좌표변화가 이미 끝난 상태이기 때문에 스크린에 직접 위치를 지정할 수 있었지만, 이번에는 아직 좌표변화하고있지 않은 정점이므로 3D공간상에 배치되게 되는 것
3D에서는 좌표가 수시로 바뀌기 때문
 
 
     CustomVertex.PositionColored[] vertices = new CustomVertex.PositionColored[3];
 
    vertices[0] = new CustomVertex.PositionColored(0.0f, 5.0f, 0.0f, Color.Red.ToArgb());
    vertices[1] = new CustomVertex.PositionColored(4.0f, -3.0f, 0.0f, Color.Blue.ToArgb());
    vertices[2] = new CustomVertex.PositionColored(-4.0f, -3.0f, 0.0f, Color.Green.ToArgb());
전과 비슷하지만, 구조체가 바뀌었다는것, 참고하자.
CustomVertex.PositionColored
X : 3D공간상의 X위치
Y : 3D공간상의 Y위치
Z : 3D공간상의 Z위치
Color : 정점의 색
 
 
            using (GraphicsStream data = this._vertexBuffer.Lock(0, 0, LockFlags.None))
            {
                data.Write(vertices);
                this._vertexBuffer.Unlock();
            }
            this._device.RenderState.Lighting = false;
전과 같지만, 맨 마지막 라이팅을 꺼주는게 다르다.
뭐 아직 안쓰니까 일단 False, 이걸 키면 다각형이 까맣게 보일지도 모릅니다.
 
 
this._device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkBlue, 1.0f, 0);
            this._device.BeginScene();
            this._device.SetStreamSource(0, this._vertexBuffer, 0);
            this._device.VertexFormat = CustomVertex.PositionColored.Format;
            this._device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
이제 클리어할 때 Z버퍼도 쓰니까 Z버퍼도 클리어를 시켜줘야해 (ClearFlags.Target | ClearFlags.ZBuffer) ㅇㅋ?
이거를 클리어 안하면, Z버퍼는 맨앞 다각형밖에 못그려가지구 맨 마지막엔 아무것도 못쓰는 상황이 생길 수 있어요,, Z수치는 0.0 ~ 1.0의 범위
나머지부분은 같아보이지만,
정점 포맷이 PositionColored로 변형된다는거 기억하시구요요요
 
카메라가 지금은 정면에서 비춘다! 그래서 전의 2D와는 차이가 없는 모양이라는 것 알아두자