directX9 필기

manmarru·2024년 5월 9일

c++

목록 보기
15/24

용책 100p 에서 시작

버텍스

  • 모든 물체는 삼각형 매쉬의 묘사로 출력된다.
  • 버텍스

버텍스 포맷 만들기

  • 버텍스 구조체 만들기
struct ColorVertex
{
	float _x, _y, _z; //위치
    DWORD _Color;
};
//또는
struct NormalTexVertex
{
	float _x, _y, _z;
    float _nx, _ny, _nz; //법선
    float _u, _v;//텍스쳐 좌표
};

{위치, 색상} 또는 {위치, 법선벡터, 텍스쳐 좌표}

  • 포맷 플래그 조합을 이용해 포맷팅되는 방법을 지정하기
#define FVF_COLOR (D3DFVF_XYZ | D3DFVF_DEFFUSE)
// 또는
#define FVF_NORMAL_TEX (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1)
  • 주의 : 버텍스 구조체에 정의된 데이터의 순서와 FVF의 정의된 순서는 반드시 일치해야 한다.

  • 두르기 순서 : 도형을 만들 때 버텍스를 지정하는 순서

카메라

  • 화면이 네모니깐..사각뿔의 절두체 공간
  • 클리핑 : 카메라 공간에 포함되지 않는 데이터는 출력에서 제외 (오브젝트 컬링같은거)
  • 투영 윈도우 : 2D 카메라 공간, min = (-1, -1)과 max = (1,1)로 정의하여 만든다.

랜더링 파이프라인

  • 3D 장면을 구성한 뒤 모니터에 2D로 표현하는 과정을 랜더링 파이프라인이라고 한다.

    랜더링 파이프라인 순서

    • 로컬 스페이스
    • 월드 스페이스
    • 뷰 스페이스
    • 후면 추려내기
    • 조명
    • 클리핑
    • 투영
    • 뷰포트
    • 래스터라이즈

    1. 로컬 스페이스

  • 우리가 삼각형 리스트를 정의하는데 사용하는 좌표공간

    2. 월드 스페이스

  • 다수의 오브젝트를 구성한 뒤 로컬에서 월드 변환을 통해 월드 좌표로 옮긴 공간
    여기서 말하는 변환은 선형변환을 말한다.(크자이공부 순서로 해야됨)

    3. 뷰 스페이스

  • 월드 스페이스를 카메라를 기준으로 변환한 공간

D3DXMATRIX* D3DXMatrixLookAtLH
{
D3DXMATRIX* pOut, // 결과 행렬을 받을 포인터
CONST D3DXVECTOR3* pEye, // 월드에서 카메라 위치
CONST D3DXVECTOR3* pAt // 카메라가 보는 지점
CONST D3DXVECTOR3* pUp //월드의 업 벡터 - (0,1,0)
};

//사용할때
Device->SetTransform(D3DTS_VIEW, &V);

4. 후면 추려내기

  • 카메라는 절대 폴리곤의 후면을 볼 수 없다.
  • 폴리곤의 Look벡터와 카메라의 방향 벡터의 관계를 통해 후면폴리곤과 전면폴리곤을 구분한다.
Device->SetRenderState(D3DTS_CULLMODE, Value);

5. 조명

  • 월드 스페이스에서 정의해서 뷰 스페이스로 변환된다.

6. 클리핑

  • 오브젝트 컬링이라고 생각하면 될듯?
    시아 볼륨 외부의 물체들을 추려내는 과정

    완전한 내부 : 완전히 카메라 절두체 내부에 있으면 통과
    완전한 외부 : 절두체 밖이면 추려내짐
    부분적 내부 : 부분적으로 내부에 있으면 그만큼 삼각형을 분리해서 부분적으로 추려낸다.

7. 투영

  • n - 1 차원을 얻는 과정
  • 원근 투영을 사용한다.
  • D3D에서 투영 윈도우는 z=1 평면으로 정의하고 있다.
D3DXMATRIX* D3DXMatrixPerspectiveFovLH(
	D3DXMATRIX* pOut, //투영 행렬을 리턴
    FLOAT forY, 	  // 시야각의 수직 영역(라디안)
    FLOAT Aspect,     //종횡비 = 너비/높이
    FLOAT zn,	      // 가까운 평면까지의 거리
    FLOAT zf, 	      // 먼 평면까지의 거리
)
/*
D3DTS_PROJECTION에 변환 타입을 전달하고 
IDirect3DDevice9::SetTrensform 매서드를 호출하면 투영 행렬을 얻을 수 있다.
*/
D3DXMATRIX proj;
D3DXmatrixPerspectiveFovLH(
		&proj, PI * 0.5f,
        (float)width / (float)height, 1.0, 1000.0f);
Device->SetTransform(D3DTS_PROJECTION, &proj);
  • 종횡비 : 정사각형에서 직사각형으로 투영하면서 생기는 왜곡을 보정하는 용도

8. 뷰포트

  • 프로젝트 윈도우의 좌표를 뷰포트라고 하는 화면의 직사각형으로 변환하는 과정
    게임 창, 전체 화면 등이 된다.
typedef struct _D3DVIEWPORT9{
	DWORD X;
    DWORD Y;
    DWORD Width;
    DWORD Height;
    DWORD MinZ; // 최소 깊이 버퍼 (D3D 에서 버퍼 범위 = [0,1])
    DWORD MaxZ; // 최대 깊이 버퍼 ([0,1])
} D3DVIEWPORT9;

D3DVIEWPORT9 vp = {0, 0, 640, 480, 0, 1};
Device->SetViewport(&vp);


출처 용책

9. 래스터라이즈

  • 각각의 삼각형을 그리는 데 필요한 픽셀 컬러들을 계산하는 과정
  • 작업량때문에 그래픽 하드웨어에서 처리해야 한다.

D3D 렌더링

버텍스 / 인덱스 버퍼

  • 버텍스 버퍼 : 버텍스 데이터를 보관하는 연속적인 메모리 덩어리
  • 인덱스 버퍼 : 인덱스 ""
  • 비디오 메모리에 저장하기 위해 버퍼에 저장하는것이다!
// 버텍스 버퍼
HRESULT IDirect3DDevice9 :: CreateVertexBuffet(
	UNIT Length, // 버퍼에 할당할 바이트 수
    DWORD Usage, // 버퍼 이용 옵션
    DWORD FVF,	// 버퍼에 보관될 버텍스의 유연한 버텍스 포멧
    D3DPOOL Pool // 버퍼가 위치할 메모리 풀
    IDirect3DVertexBuffer9** ppVertxBuffer, // 만들어질 버텍스 버퍼를 받을 포인터
    HANDLE* pSharedHandle // 안쓰는거임 (0 넣으면 됨)
    );

// 인덱스 버퍼
HERSULT IDirect3DDevice9::CreateIndexBuffer(
	UNIT Length,
    DWORD Usage,
    DWORD FVF,
    D3DFIRMAT Format, // 인덱스 크기
    D3DPOOL Pool
	IDirect3DVertexBuffer9** ppVertxBuffer, //만들어질 인덱스 버퍼를 받을 포인터
    HANDLE* pSharedHandle
  • 버퍼 이용 옵션
  • 0 : 옵션 없음
  • D3DUSAGE_DYNAMIC : 버퍼를 동적으로 만든다.
  • D3DUSAGE_POINTS : 버퍼가 포인트 기본형을 보관함.
  • D3DUSAGE_SOFTWARPROCESSING : 버텍스 프로세싱을 소프트웨어로 처리함
  • D3DUSAGE_WRITEONLY : 쓰기 전용
    정적 버퍼 : 연산이 빠르지만 갱신이 느림 -> 잘 안 변하는 지형 등에 씀
    동적 버퍼 : 갱신이 빠르지만 비디오 메로리로 전송해야 돼서 연산이 느림 : 자꾸 변하는 플레이어 등의 오브젝트에 씀

버퍼 메모리 접근

  • Lock 매서드를 통해 내부 메모리 컨텐트로의 포인터를 얻으면 버퍼의 정보를 읽고 쓸 수 있게 된다. 끝난 다음에는 Unlock 매서드를 통해 버퍼의 잠금을 풀어줘야 한다.
  • 읽기 전용 플래그가 설정된 버텍스/인덱스는 절대 버퍼를 읽으면 안 된다(터짐)

버텍스/인덱스 버퍼에 대한 정보 얻기

D3DVERTEXBUFFER_DESC vbDescription;
_vertexBuffer->GetDesc(&vbDescription); // 버텍스 버퍼 정보를 얻는다.

D3DINDEXBUFFER_DESC ibDescription;
_indexBuffer->GetDesc(&ibDescription); // 인덱스 버퍼 정보를 얻는다.

렌더 상태 변경

  • 디폴트 값이 있으르모 필요할 때만 변경하면 되고 다시 바꾸기 전까지 쭉 유지된다.
HRESULT IDirect3DDevice9::SetRenderState(
	D3DRENDERSTATETYPE State, // 변경할 상태
    DWORD Value //새로운 상태 값

드로잉 마지막 준비

  1. 스트림 소스 지정 IDirect3DDevice9::SetStreamSource
  2. 버텍스 포맷 지정 _device->SetFVF
  3. 인덱스 버퍼 지정
    한 번에 하나의 인덱스 버퍼만 이용할 수 있다. 따라서 다른 인덱스 버퍼를 써야 되면 인덱스 버퍼를 전환하는 과정이 필요하다. _device->SetIndices(_ib)

버텍스/인덱스 버퍼를 이용한 드로잉

  • 위의 과정을 마쳤으면 기하정보를 렌더링 파이프라인으로 보내는 실질적인 드로잉을 수행할 수 있다.
  • 버텍스 스트림에서 버텍스를, 인덱스 버퍼에서 인덱스롤 얻어온다.

인덱스 정보를 안 쓰는 기본형 그리기

  • 그냥 버텍스 버퍼에 들어있는 버텍스 순서대로 쭉 그려나가는고야
HRESULT IDirect3DDevice9::DrawPrimitive(
	D3DPRIMITIVETYPE PrimitiveType, //그릴 기본형 타입
    UNIT StartVertex, //버텍스 읽기를 시작할 버텍스 스트림 요소로의 인덱스
    UNIT PrimitiveCount // 그릴 기본형의 수
);

인덱스 정보를 쓰는 기본형 그리기

  • 버텍스 버퍼에 들어있는 버텍스를 인덱스 버퍼에서 알려주는 순서로 삼각형을 그리기 때문에 겹치는 버텍스는 하나만 넣어도 된다!
HRESULT IDirect3DDevice9::DrawIndexedPrimitive(
	D3DPRIMITIVETYPE Type, //그릴 기본형 타입
    INT BaseVertexIndex, // 이번 호툴에 이용할 인덱스에 더해질 기반 번호
    UINT MinIndex, // 참조할 최소 인덱스 값
    UINT NumBertices, // 참조할 버텍스의 수
    UINT StartIndex, // 인덱스 버퍼 내에서 읽을 인덱스
    UINT PrimitiveCount //그릴 기본형 수
);
  • BaseVertexIndex : 이게 그 버텍스 오프셋을 전달해주는거고 인덱스를 갱신할때 다시 계산하는건 D3D 내부에서 알아서 해준다.

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

모든 드로잉은 IDirect3DDevice9::BeginScene 과 IDirect3DDevice9::EndScene 호출 내부에 포함되어야 한다.

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

_device->BeginScene();
mesh->DrawSubset(0); // 매쉬 랜더링
_device->ENdScene();

...
//메쉬 다썼으면 해제하기
_mesh->Release();
_mesh = 0;

컬러

색깔 표현

D3DCOLOR

  • 컬러를 저장하는 32비트 DWORD 구조체
  • 0~255 RGBA 성분을 사용함.
D3DCOLOR birghtRed = D3DCOLOR_ARGB(255, 255, 0, 0);
  • 알파값을 받지 않는 XRGB 매크로가 있다. 이건 알파를 0xff( = 255)로 지정한다.

D3DCOLORVALUE

  • 0~1 RGBA 값을 이용한다.
typedef struct _D3DCOLORVALUE{
float r; float g; float b; float a; //범위 [0,1]
}D3DCOLORVALUE;

D3DXCOLOR

  • 위에꺼랑 똑같이 생겼는데 오버로드된 연산자가 있어서 컬러 관리가 더 편하다.
  • 구조체가 동일하기 때문에 위에꺼랑 형변환도 가능하다.(오버로딩돼있음)

    오버로딩된거

    • 형변환
    • += 같은 할당연산자
    • 단항연산자
    • 이항연산자

버텍스 컬러

  • 기본형의 컬러는 이를 구성하는 버텍스의 컬러에 따라 결정되기때문에 우리의 버텍스 데이터 구조체에 컬러 맴버를 추가해야 한다.
struct ColorVertex
{
float _x, _y, _z;
D3DCOLOR _color;
static const DWORD FVF;
}
const DWORD ColorVertex::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE;

참고로 FVF 는 Flaxible Vertrx Format : 각 정점의 버텍스를 나타내는 방식이라고 함.

셰이딩

  • 기본형을 구성하는 픽셀 컬러를 계산하는데 버텍스 컬러가 이용되는 방식을 결정한다.
  • 레스터라이즈 과정에서 이루어진다.
  • flat shading, Gouraud shading 의 두 가지 방법이 있다.

Flot shading

  • 첫번째 버텍스에 지정된 컬러를 이용해 일관적으로 기본형의 픽셀을 채운다.(나머지 버텍스 컬러는 무시된다.)

Gouraud shading (smooth shading)

  • 기본형의 면을 따라 보간된 컬러가 버텍스에 입혀진다.
///플랫 셰이딩 모드로 지정
Device->SetRenderState(D3DRS_SGDEMODE, D3DSHADE_FLAT);

///그라우드 셰이딩 모드로 지정
Device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);

조명

조명 요소

  • 환경광(Ambient Light) : 다른 표면에 반사되어 전반적인 장면을 밝게 하는 빛을 모델링한다.
  • 난반사광(Diffuse Light) : 특정한 방향으로 진행하며, 표면에 닿으면 모든 방향으로 동일하게 반사된다. 위치와 관계없이 관찰자에 눈에 빛이 도달하며, 관찰자의 위치를 고려할 필요가 없다. -> 빛의 방향과 표면의 형태만 고려함
  • 정반사광(Specular Light) : 특정항 방향으로 진행하며, 표면에 닿으면 한 방향으로 강하게 반사하여 특정한 각도에서만 관찰할 수 있다. -> 빛의 방향 + 표면의 형태 + 관찰자의 시점을 모두 고려해야 함.
    반짝이는 표면에 빛이 반사하는 효과를 모델링할 떄 이용함.
    얜 좀 무거워서 끌 수 있는 함수도 있다 (심지어 디폴트로 꺼져있음)
  • 알파값은 빛의 컬러를 표현할 때 사용하지 않는다.

재질

재질에 따라 흡수하는 색을 정할 수 있게 할 수 있다.

typedef struct _D3DMATRIAL9
{
	D3DCOLORVALUE Diffuse; // 표면이 반사하는 난반사광의 양
    D3DCOLORVALUE Ambient; // 표면이 반사하는 환경광의 양
    D3DCOLORVALUE Specular; // 표면이 반사하는 정반사광의 양
    D3DCOLORVALUE Emissive; // 전반적인 표면의 컬러를 더하는데 사용함.
    float Power; // 정반사광의 sharpness 를 지정하며, 높을수록 하이라이트가 강조됨.
} D3DMATRIAL9;
  • Specular 값은 물체 자체가 빛을 발하는것처럼 좀 더 밝은 물체 효과를 만들어낸다.
  • 모든 빛을 흡수하게 설정하면 검은색으로 나타난다.
  • 매번 이걸 하긴 귀찮으니까 유틸리티 함수 + 전역 재질 함수가 존재함.
  • 버텍스 구조체는 재질 속성이 없어서 대신 SetMaterial 매서드를 이용한다.

버텍스 법선

  • face normal : 면의 법선벡터

  • 버텍스 법선 : ㅋㅋ점에 법선이 어딨냐, 법선이 속한 face 들의 법선들의 평균 정도로 생각하면 된다.(다른 경우도 있긴 한데 뭐..)

  • 앞으로 버텍스의 색을 계산하는 데 조명을 이용할 것이기 때문에 버텍스 구조체에서 컬러 맴버변수는 제거한다.

  • 무슨 도형을 쓸지, 복잡한 경우 어떤 모양일지 아에 모르니까 걍 버텍스 세개 가지고 면의 법선을 구하는 방식이 사용된다.

void ComputeNormal(D3DXVECTOR3* p0,
					D3DXVECTOR3* p1,
                    D3DXVECTOR3* p2,
                    D3DXVECTOR3* out) //시계방향 두르기 유지하기
{
	D3DXVECTOR3 u = *p1 - *p0;
	D3DXVECTOR3 v = *p2 - *p0; // 점 세개로 벡터 두개 구하고 외적할거임
    
    D3DXVec3Cross(out, &u, &v);
    D3DXVec3Normalize(out, out);
}                    

변환 단계에서 버텍스 법선이 왜곡되는 현상이 발생할 수 있으므로, D3DRS_NORMALIZENORMALS 랜더 상태를 활성화하여 변환 단계 이후에 Direct3D가 모든 법선을 다시 정리하도록 하는 것이 안전하다.

Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);

광원

D3D는 세 가지 타입의 광원을 지원한다.

  • 점 광원 : 월드 스페이스 내에 위치를 가지며, 모든 방향으로 빛을 발산한다.
  • 방향성 광원 : 위치는 가지지 않지만, 지정된 방향으로 평행하게 빛을 발산한다.
  • 스포트(spot) 광원 : 손전등 빛처럼 위치를 가지며 특정한 방향으로 원뿔 형태의 빛을 발산한다. 안쪽의 원뿔(Theta)과 바깥쪽의 원뿔(phi) 두 가지 각도에 의해 원뿔의 모양이 결정된다.
typedef sturct _D3DLIGHT9
{
	D3DLIGHTTYPE Type; //만들려는 광원의 타입
    D3DCLOLRVALUE Diffuse; // 난반사광의 색
    D3DCLOLRVALUE Specular; // 정반사광의 색
    D3DCLOLRVALUE Ambient; // 환경광의 색
    D3DVECTOR Position; // 광원의 위치 (없는경우 의미 없음)
    D3DVECTOR Direction; // 빛의 방향 (점 광원에선 안씀)
    float Range;		// 빛이 진행 가능한 최대거리
    float Falloff;		// (소프트 광원에서) 밖과 안 원뿔의 빛 세기 차이
    float Attenuation0;	// 거리에 따라 빛의 세기가 약해지는 정도
    float Attenuation1;
    float Attenuation2;
    float Theta;		//안쪽 원뿔의 각도
    float Phi			//바깥쪽 원뿔의 각도
}D3DLIGHT9;
  • Attenuation 은 점 광원과 스폿 광원에서만 이용한다.
    세 변수는 차례대로 상수감소, 선형감소, 이차감소를 정의한다.

  • D3DLIGHT9 객체를 만든 후 D3D가 관리하는 내부 리스트에 등록해야 한다.

Device->SetLight(
0, // 지정할 광원 리스트 내의 요소, 범위는 0~최대 광원
&light // 설정하려는 D3DLIGHT9 구조체의 주소
)
  • 광원을 끄거나 켤 수 있다.
Device->LightEnavble(
0, // 활성,비활성화 하려는 광원 리스트 내의 요소
true // true = 활성, false = 비활성
);

텍스처

D3D에서는 u = (1,0), v = (0,1) 축을 사용한다. 참고로 윈도우 좌표계랑 동일하게 위에서 v는 아래로 뻗으며, 텍스처 좌표공간이라고도 한다.

  • 버텍스 구조체를 또 수정한다..
struct Vertex
{
	float _x, _y, _z;
    float _nx _ny, _nz;
    flaot _u, _v; // 텍스처 좌표\
    
    static const DWORD FVF;
};
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
// D3DFVF_TEX1  : 버텍스 구조체에 한 쌍의 텍스처 좌표가 조함됨을 지정

이제 이 세 개의 버텍스로 만들어지는 삼각형들은 텍스처 좌표에서의 텍스처 삼각형으로도 정의된다.

  • 레스터라이즈 단계 전까지는 텍스쳐가 입혀지지 않는다!

텍스처의 작성, 활성화

텍스처 데이터 읽기

HRESULT D3DXCreateTextureFromFile(
	LPDIRECT3DDEVICE9 pDevice, //텍스처를 만들어낼 장치
	LPCSTR pSrcFile,			// 이미지를 읽어들일 파일명
    LPDIRECT3DTEXTURE9* ppTexture // 만들어진 텍스처를 받을 포인터
) // 읽혀진 이미지 파일은 IDirect3DTexture9 객체로 반환된다.

현재 텍스처를 지정하는 매서드

HRESULT IDirect3DDevice9::SetTexture(
	DWORD Stage,					// [0,7] 범위의 값으로 텍스처를 지정한다.
    								// stage - 이어지는 노트를 참고한다.
    IDirect3DBaseTexture9* pTexture // 지정할 텍스처의 포인터
)
  • Direct3D에서는 최대 8개의 텍스처를 결합하여 더 자세한 이미지를 만들 수 있으며 그걸 멀티 텍스처링이라 한다.
    위의 Stage값이 결합할 텍스처의 갯수다.

필터

  • 텍스처를 스크린 삼각형에 맞추는 괒어에서 생기는 왜곡을 필터링을 통해 줄이고 부드러운 이미지를 만든다.
    • 근접점(Nearset) 샘플링 : 디폴트 필터링 방식이며 가장 품질이 떨어진다(빠르다)
    • 선형 필터링 : 비교적 높은 품질의 결과물을 만들어내며, 최근 하드웨어를 생각하면 실행도 빠른 편이다.(권장)
    • 비등방성(Anisotropic) 필터링 : 가장 높은 품질 But 느림
//근접점 샘플링
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
Device->SetSamplerState(0, D3DSAMP_MINFILTERM, D3DTEXF_POINT);

//선형 필터링
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_Linear);
Device->SetSamplerState(0, D3DSAMP_MINFILTERM, D3DTEXF_Linear);

//비등방성 필터링
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC);
Device->SetSamplerState(0, D3DSAMP_MINFILTERM, D3DTEXF_ANISOTROPIC);
  • 비등방성 필터링을 사용할 때 필터링의 품질을 결정하는 D3DSMAP_MAXANISOTROPY 레벨을 지정해야 한다.
    D3DCAPS9 구조체를 통해 장치에서 지원하는 값의 범위를 알 수 있다.
Device->SetSamplerState(0, D3DSAMP_MAXANISOTROPY, 4);

참고로 smapler 클래스는 텍스처 구성 정보라고 생각하면 됨. SetSamplerState 함수로 필터 정보를 바꾸는거

밉맵

  • 텍스처의 크기를 알맞게 맞출 때 한번에 크기를 확 바꾸지 않고 여러번에 걸쳐서 크기를 조절하면 덜 깨진다. -> 여러번에 걸쳐서 밉맵을 만든다.
// 밉맵 필터 지정하기
Device->SetSamplerState(0, D3DSAMP_MIPFILTER, Filter);

Filter 매개변수
D3DTEXF_NONE : 밉맵을 이용하지 않는다.
D3DTEXF_POINT : 스크린 삼각형과 가장 비슷한 크기의 밉맵 레벨을 선택한다. 일단 레벨이 선택되면 지정된 min과 mag 태그에 따라 선택된 레벨을 필터링한다.
D3DTEXF_LINER : 가장 비슷한 두 개의 밉맵 레벨에 min과 mag 필터에 따른 필터링을 적용한다. -> 선형적으로 조합된 두 개의 레벨을 이용해 최종 픽셀 컬러를 계산해낸다.

D3D에서 밉맵 자동으로 해주는거라 대충 하면 된다.

어드레스 모드

  • 텍스처 좌표 범위가 [0,1] 밖으로 나가는 경우가 있다.
    이를 처리하는 방법은 D3D 어드레스 모드에 의해 정의된다.
  • wrap, border color, clamp, mirror 네 가지 모드가 있다.

    wrap : 이미지를 넘어가면 이미지를 반복적으로 출력함.
    border color : 지정된 좌표까지만 그리고 넘어가는 부분은 지정된 스크린 색으로 채워진다.(default 검정)
    cloamp : 넘어가면 이미지 끝부분을 쭉 출력한다.
    mirror : 이미지를 넘어가면 대칭으로 이미지를 출력한다.

//wrap 어드레스 모드
if(::GetAsyncKetState('W') & 0x8000f)
{
	Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
    Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
}
// border color 모드
if(::GetAsyncKetState('B') & 0x8000f)
{
	Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
    Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
    Device->SetSamplerState(0, D3DSAMP_BORDERCOLOR, 0x000000ff); // 스크린 색
}
//clamp 모드
if(::GetAsyncKetState('W') & 0x8000f)
{
	Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
    Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
}
//mirror 모드
if(::GetAsyncKetState('W') & 0x8000f)
{
	Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR);
    Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR);
}

블렌딩

  • 현재 계산되고 있는 픽셀(원본픽셀)을 앞서 쓰여진 픽셀값(목적지 픽셀)고 결합하는 방식

    반드시! 블렌딩하지 않는 물체를 먼저 그려야 한다.
    블렌딩하는 물체는 카메라 거리 기반 정렬을 해야 한다.
    멀리 있는 물체부터 그린다. 왜냐면 버퍼 체크 하다가 뒤에 있는 물체를 못 그리게 되면 섞이지 않으니까.

outputPixel = SourcePixel SourceBlendFactor + DestPixel DestBlendFactor

  • 블렌딩은 디폴트로 꺼져있으며, D3DRS_ALPHABLENDENABLE값을 true로 지정해서 활성화한다.
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
  • 블렌딩이 끝나면 반드시 비활성화 해줘야 한다. 그리고 이거 켜는거 무거우니까 가급적이면 켰을때 다 처리해서 한 프레임에 여러 번 켜는건 피하자.

블랜드 인수

  • 원본/목적지 블렌드 인수에 옵션을 넣으면 여러 가지 효과가 나온다.
  • 각각의 인수를 지정하는건 D3DRS_SRCVLEND와 D3DRS_DESTBLEND 렌더 상태를 이용하면 된다.
Device->SetRenderState(D3DRS_SRCBLEND, Source);
Device->SetRenderState(D3DRS_DESTBLEND, Destination);

투명

  • RGBA에서 불투명도를 의미하는 A값으로 투명도를 조절한다. 범위는 [0,255]
    0일때 투명, 255일때 불투명하다.

  • 원본 블렌드 인수를 D3DBLEND_SRCALPHA, 목적지 인수를 D3DBLEND_INVSRCALPHA로 지정해야 한다.

  • 텍스처의 알파 채널에서 알파 정보를 가져올 수도 있다. 각 픽셀마다 들어있는거 꺼내서 쓴다는 말임.

  • 디폴트로, 현재 텍스처가 알파 채널을 가지고 있는 경우 알파 채널에서 알파를 가져오고, 없다면 버텍스 컬러에서 알파를 얻는다.

//셰이드 과정에서 난반사 컬러로 알파를 계산한다.
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);

//알파 채널에서 알파를 얻는다.
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);

스텐실

스텐실 버퍼 : 특수한 효과를 위한 오프 스크린 버퍼
일부 영역의 렌더링을 막을 수 있다.

사용과정

  1. Direcr3D를 초기화하는 시점에 스텐실 버퍼 요청하기
    이후 사용시 스텐실 버퍼를 활성해준다.
Device->SetRenderState(D3DRS_STENCILENABLE, true)
false 로 설정하면 비활성화된다.

스텐실 버퍼를 디폴트 값으로 되돌리기 -> iDirect3DDevice9::Clear

Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL,
0xff000000,
1.0f,
0);// 스텐실 버퍼를 소거하는데 이용될 값을 지정
  1. 스텐실 버퍼 요청
  • 깊이 버퍼를 만들 때 같이 만들 수 있고, 버퍼의 포멧 지정도 같이 할 수 있다.
    • D3DFMT_D24S8 - 32비트 깊이/스텐실 버퍼를 만들고 깊이 버퍼에는 픽셀 당 24비트를. 스텐실 버퍼에는 픽셀 당 8비트를 할당한다.
    • D3DFMT_D24X4S4 - 32비트 깊이/스텐실 버퍼를 만들고 깊이 버퍼에는 픽셀 당 24비트를. 스텐실 버퍼에는 픽셀 당 4비트를 할당한다, 나머지 4비트는 안쓴다.
    • D3DFMT_D15S1 - 16비트 깊이/스텐실 버퍼를 만들고 깊이 버퍼에는 픽셀 당 15비트를. 스텐실 버퍼에는 픽셀 당 1비트를 할당한다.
  • 스텐실 버퍼에 비트를 할당하지 않는 포멧도 존재한다.
  • 그래픽 카드에 따라 지원하지 않는 크기의 스텐실 버퍼가 존재할 수 있다.

스텐실 테스트

  • 렌더링을 막을지 여부는
    (참조& 마스트) 비교연산자 (값 & 마스크)
    로 나타나는 스텐실 테스트에 의해 결정된다.
  • 왼쪽 연산 : 어플레케이션이 정의한 스텐실 참조 값과 어플리케이션이 정의한 마스크 값의 && 연산으로 얻어진다.
  • 오른쪽 연산 : 현재 테스트하려는 픽셀의 스텐실 버퍼와 어플리케이션이 정의한 마스크 값의 && 연산으로 얻어진다.

결국 true면 후면 버퍼의 픽셀을 출력, false면 출력하지 않는다. + 깊이 버퍼에도 안 기록된다.

스텐실 제어

  • 스텐실 참조 값은 0이 디폴트지만 1로 바꿀 수 있다.
Device->SetRenderState(D3DRS_STENCILREF, 0x1); //16진수가 비트연산에 좋다.

스텐실 마스크 바꾸기

  • 스텐실 마스크는 참조와 값 변수 양쪽의 비트를 감추는데 이용된다.
    디폴트는 0xffffffff이다(안 감춘다는 뜻)
Device->SetRenderState(D3DRS_STENCILMASK, 0x0000ffff);
  • 스텐실 값 수정 -> 이건 불가능하다.

  • 비교연산자 지정
    D3DRS_STENCILFUNC : 랜더 상태를 이용해서 비교 연산자를 지정할 수 있으며, 다음 열거형의 맴버를 선택해서 사용한다.

//LHS는 좌측 비교연산자, RHS는 우측 비교연산자를  의미함

typedef enum _D3DCMPJUNC
{
	D3DCMP_NEVER = 1, //스텐실 테스트가 항상 실패
    D3DCMP_LESS, // LHS<RHS 일 경우 스텐실 테스트가 성공
    D3DCMP_EQUAL, // LHS == RHS 일 경우 스텐실 테스트가 성공한다.
    D3DCMP_LESSEQUAL, // LHS <= RHS일 경우 스텐실 테스트가 성공한다.
    D3DCMP_GREATER, // LHS > RHS일 경우 스텐실 테스트가 성공한다.
    D3DCMP_NOTEQUAL, // LHS != RHS 일 경우 스텐실 테스트가 성공한다.
    D3DCMP_GREATEREQUAL, //LHS >= RHS 일 경우 스텐실 테스트가 성공한다.
    D3DCMP_ALWAYS, // 테스트가 항상 성공
    D3DCMP_FORCE_DWORD = 0x7fffffff
} D3DCMPFUNC;

스텐실 버퍼 갱신

ㄹㅇ 먼소린지 모르겠음 ㅋㅋ;

  • 스텐실 버퍼에 써지는 모든 값을 마스크하는 쓰기 마스크 설정
Device->SetRenderState(D3Drs_StenCILWRITEMASK, 0x0000ffff);

요약

  • 이것저것 옵션을 붙여서 스텐실 버퍼를 만든다.
  • 후면 버퍼에 나온 결과물들 중에 스텐실 테스트를 통과한 부분만 출력한다
    = 거울에만 반사된 물체가 출력된다
  • 요약은 아닌데 처음 랜더링되는 픽셀만 받게 스텐실 테스트를 구성해서 그림자가 안 겹치게 할 수도 있다.

글꼴

D3DX 는 ID3DXFONT 인터페이스를 제공한다.

ID3DXFONT 만들기

HRESULT D3DXVreateFontIndirect
{
	LPDIRECT3DDEVICE9 pDevice, //글꼴과 연결된 장치
    CONST LOGFONT* pLogFont,	//글꼴을 나타내는 LOGFONT 구조체
    LPD3DXFONT* ppFont			//만들어진 글꼴을 리턴
}
//LOGFONT에 글씨의 굵기 등 글꼴에 대한 정보가 들어있어서 그거 설정한뒤 적용하면 된다.

텍스트 그리기

INT ID3DXFONT::DrawText
(
	LPCSTR pString, //그리고자 하는 문자열
    INT Count,		// 문자열 내에 포함된 문자의 수, 문자열이 null로 끝나는 경우 -1 을 지정할 수 있다.
    LPRECT pRect,	// 텍스트가 그려질 화면 상의 영역을 정의하는 RECT 구조체를 가리키는 포인터
    DWORD Format,	// 텍스트 포맷을 지정하는 플래그
    D3DCOLOR Color // 텍스트 컬러
);

CD3Font

  • Direct3D와 텍스처를 입힌 삼각형을 이용해 텍스트를 렌더링한다.
  • ID3DXFont 와 다르게 Gdi가 아닌 Direct3D를 이용하므로 훨씬 빠르지만 복잡한 글꼴과 포맷팅은 지원하지 않는다.
    따라서 간단한 텍스트를 빠르게 그릴거면 이게 더 좋다.
  • 이거 쓰려면 d3dfont.h, d3dfont.cpp, d3dutil.h, d3dutil.cpp, dxutil.h, dxutil.cpp 파일을 애플리케이션에 추가해야 한다.

CD3Font 구성하기

  • 일반적인 객체처럼 인스턴스화하면 된다.
CD3Font(
	const TCHAR* strFontName, // 글꼴의 이름을 지정하는 null로 끝나는 문자열
	DWORD dwheight, // 글꼴의 높이
	DWORD dwFlogs=0L); // 선택적 생성 플래그들 : ㅋㅋ0말고 넣긴 할거임?
  • 다음으로 해당 메서드들을 순서대로 호출해서 글꼴을 초기화해야 한다.
Font = new CD3DFOnt("Times New Roman", 16, 0); // 인스턴스화
Font->InitDeviceObjects(Device);
Font->RestoreDeviceObjects();

텍스트 그리기

HRESULT CD3DFont::DrawText(
	FLOAT x,		//텍스트 그리기를 시작할 화면의 x축 좌표
    FLOAT y,		//텍스트 그리기를 시작활 화면의 y축 좌표
    DWORD dwColot,	//텍스트의 색
    Const TCHAR* strText,	//그리고자 하는 문자열의 포인터
    DWORD dwFlags=0L)		//선택적 렌더링 플래그들
/*플래그 인자에는 0을 지정하거나
D3DFONT_CENTERED, D3DFONT_TWOSIDED, D3DFONT_FILTERED 플래그들의 조합을 지정할 수 있다.*/

데이터 정리

D3DFont 객체를 삭제하기 전에 약간의 정리 루틴을 호출해야 한다.

Font->InvalidateDeviceObjects();
Font->DeleteDeviceObjects();
delete Font;

D3DCreateText

  • 텍스트의 3D 메쉬를 만드는 데 이용된다.
HRESULT D3DXCreateText(
	LPDIRECT3DDEVICE9 pDevice, //메쉬와 연결될 장치
    HDC hDC,					// 메쉬를 생성하는데 이용될 글꼴 정보를 포함하는 장치 컨텍스트로의 핸들
    LPCTSTR pText,				//메쉬로 만들 텍스트를 포함하는 null로 끝나는 문자열
    FLOAT Deviation,			// true타입 글골 외곽선에서의 최대 편차.
    FLOAT Extrusion,			// -z축 방향으로 잰 글꼴의 깊이
    LPD3DXMESH* ppMesh,			//만들어진 메쉬 리턴
    LPD3DXBUFFER* ppAdjacency,	// 만들어진 메쉬의 근접 정보를 리턴한다. (필요없으면  null)
    LPGLYPHMETRICSFLOAT PGlyphmetrics /* 장식 거리 데이터를 포함하는 
    LPGLYPHMETRICSFLOAT 구조체 배열의 포인터, 장식 거리 데이터가 상관 없으면 0
)

Deviation값은 0 이상이어야 되며, 0일때 편차는 원본 글꼴의 한 디자인 단위와 같아진다.

메쉬

ID3DXMesh 인터페이스는 기능의 상당 부분을 부모인 ID3DXBaseMesh에서 상속받는다. 중요한 사실인데 ID3DXPMesh와 같은 다른 인터페이스들도 ID3DXBaseMesh에서 상속받기 때문.

기하 정보

  • ID3DXBaseMesh 인터페이스는 메쉬의 버텍스를 보관하는 버텍스 버퍼와 이들 버텍스가 어떻게 메쉬의 삼각형들을 구성하는가를 정의하는 인덱스 버퍼를 포함한다.
// 버텍스 버퍼의 포인터 얻어오기
HRESULT ID3DXMesh::GetVertexBuffer(LPDIRECT3DVERTEXBUFFER9* ppVB);
// 인덱스 버퍼 포인터 얻어오기
HRESULT ID3DXMesh::GetIndexBuffer(LPDIRECT3DVERTEXBUFFER9* ppVB);

ID3DXMesh 인터페이스는 순수한 정보를 제공하기 위한 목적으로 기본형 타입의 인덱스화된 삼각형 리스트를 지원한다.

  • 버퍼 잠금
//lock 함수들
HRESULT ID3DXMesh::LockVertexBuffer(DWORD Flags, BYTE** ppData);
HRESULT ID3DXMesh::LockIndexBuffer(DWORD Flags, BYTE** ppData);

//unlock 함수들
HRESULT ID3DXMesh::UnlockVertexBuffer();
HRESULT ID3DXMesh::UnlockIndexBuffer();

기하 관련 정보를 얻을 수 있는 ID3DXMesh 매서드들

DWORD GetFVF();		// 버텍스의 버텍스 포맷을 나타내는 DWORD를 리턴한다.
DWORD GetNumVertices(); 	// 버텍스 버퍼 내의 버텍스의 수를 리턴한다.
DWORD GerNumBytesPerVertex(); // 버텍스 당 바이트의 수를 리턴한다.
DWORD GetNumFaces();		// 메쉬 내의 면(삼각형)의 수를 리턴한다.

서브셋과 속성 버퍼

  • 서브셋 : 동일한 속성을 이용해 렌더링할 수 있는 메쉬 내 삼각형들의 그룹
    (속성 = 재질, 텍스처, 렌더 상태)
    이거 컴포넌트 아님?
  • 각각의 서브셋에는 고유한 양의 정수값을 지정하여 서로 구분한다.
  • 메쉬 내의 삼각형들은 각 삼각형이 위치하는 서브셋을 지정하는 속성ID를 가진다.
    속성 ID는 메쉬의 DWORD 배열인 속성 버퍼 내에 보관된다.
  • 각 면들은 속성 버퍼 내에 하나의 항목을 가지므로, (속성 버퍼 내의 요소 = 메쉬 내의 면의 수) 가 된다. 따라서 속성 버퍼 내의 항목i는 인덱스 버퍼 내의 삼각형 i 와 대응된다.

드로잉

ID3DXMesh 인터페이스는 AttribId 인자로 지정한 특정 서브셋의 삼각형을 그리는 DrawSubset(DWORD AttribId) 메서드를 제공한다.

Mesh->DrawSubset(0);
  • 전체 메쉬를 그리기 위해서는 메쉬의 모든 서브셋을 그려야 한다.
    인덱스i 가 서브셋 i와 대응되도록 구성한는게 편하겠지?
for(int i = 0; i < numSubsets; i++)
{
	Device->SetMaterial(mtrls[i]);
    Device->SetTexture(0, textures[i]);
    Mesh->DrawSubset(i);
}

최적화

메쉬 최적화 : 효과적인 렌더링을 위해 버텍스와 인덱스를 재구성하는 것-

HRESULT ID3DXMesh::OptimizeInplace
(
	DWORD Flags,	// 수행할 최적화의 종류 플래그
    COnst DWORD* pAdjacencyIn, //최적화되지 않은 메쉬의 인접 배열로의 포인터
    DWORD* pAdjacencyOut,
    DWORD* pFaceRemap,
    LPD3DXBUFFER* ppVertexRemap
);
  • DWORD pAdjacencyOut
    최적화된 메쉬의 인접 정보로 채워질 DWORD 배열로의 포인터,
    이 배열은 반드시 ID3DXMesh::GetNumFaces()
    3 수의 요소를 가지고 있어야 한다.
    필요없으면 0 넣으셈.
  • DWORD* pFaceRemap
    면 리맵 정보로 채워질 DOWRD 배열로의 포인터,
    이 배열은 반드시 ID3DXMesh::GetNumFaces() 크기를 가져야 한다.
    메쉬를 최적화하면 메쉬의 면들이 인덱스 버퍼 내에서 이동하게 되며,
    면 리맵 정보는 원래의 면이 어느 곳으로 이동했는지를 알려준다.
    즉, pFaceRemap 내의 i번째 항목은 원래의 i번째 면이 어느 곳으로 이동했는지를 알려주는 면 인덱스를 포함한다.
    필요없으면 0 넣으셈
  • LPD3DXBUFFER* ppVertexRemap
    버택스 리맵 정보로 채워질 ID3DXBuffer 포인터의 주소.
    이 버퍼는 ID3DXMesh::GetNumVertices() 만큼의 항목을 포함해야 한다.
    메쉬를 최적화하면 메쉬의 버텍스들이 버텍스 버퍼 내에서 이동하게 되며,
    버텍스 리맵 정보는 원본 버텍스가 어느 곳으로 이동했는지를 알려준다.
    필요없으면 0 넣으셈

비슷한 메서드로 Optimize가 있다. 얜 매게변수인 메쉬 객체를 최적화해주는게 아니라 최적화된걸 리턴한다.

HRESULT ID3DXMesh::Optimize
(
	DWORD Flags,
    CONST DWORD* pAdjacencyIn,
    DWORD* pAdjacenecyOut,
    DWORD* pFaceRemap,
    LPD3DXBUFFER* ppVertexRemapm,
    LPD3DXMESH* ppOptMesh // 최적화된 메쉬
);

속성 테이블

D3DXMESHOPT_ATTSORT 플래그를 설정하여 메쉬를 초기활화하면 메쉬의 기하정보가 그 속성에 따라 정렬되므로, 버텍스/인덱스 버퍼 내에 있는 특정 부분의 기하정보가 연속된 블록에 위치하게 된다.

0개의 댓글