오늘은 빌보드에 대해 복습하고 예제 코드를 분석해보자
빌보드는 오브젝트가 항상 플레이어를 바라보도록 하는 것이다. 이전에 빌보드는 구현할 때 여러 오브젝트를 생성 할때
정점 4개를 두고 VS단계에서 고쳐주고 늘리면서 사용하게 했다. 이 방법보다 더 나은 방법이 있는데 바로 Geometry Shader를 사용하는 방법이다.
Pipeline 중간 아래쪽에 보면 Geometry Shader가 있다. 이 단계의 역할은 정점을 인위적으로 늘리고 줄일 수 있는 정점
분리기이다. 이전에 4개를 복사하던 것에서 1개를 두고 GS단계에서 고쳐주고 늘려주면 되는 것이다.
코드를 살펴보기 전에 예제를 먼저 실행해보자.
기존에 나무가 추가되어 있는 화면에서 나무가 추가되어있는 모습을 볼 수 있다. 이 나무 하나하나가 모두 빌보드인 것이다. 사각형에 2D를 그려서 이 사각혀잉 플레이어를 바라보게 만들어 준 것이다.
코드를 살펴보자
우선 정점을 만드는 부분을 살펴보자면 TreePointSprite라는 위치와 크기만을 가진 구조체로 값을 받아주고 있으며
struct TreePointSprite
{
XMFLOAT3 pos;
XMFLOAT2 size;
};
이를 랜덤하게 배치하지만 언덕위에만 배치할 수 있도록 x y z 값을 설정해준다. 이 정점을 설정해준 개수 만큼 만들어주고 버퍼를 통해 쉐이더에 전달해준다. 정점하나당 나무 하나가 만들어 지는 것이다. 이 정점 하나를 GS단계에서 동적으로 여러 정점으로
void TreeBillboardDemo::BuildTreeSpritesBuffer()
{
Vertex::TreePointSprite v[TreeCount];
for (UINT i = 0; i < TreeCount; ++i)
{
float x = MathHelper::RandF(-35.0f, 35.0f);
float z = MathHelper::RandF(-35.0f, 35.0f);
float y = GetHillHeight(x, z);
// Move tree slightly above land height.
y += 10.0f;
v[i].pos = XMFLOAT3(x, y, z);
v[i].size = XMFLOAT2(24.0f, 24.0f);
}
D3D11_BUFFER_DESC vbd;
vbd.Usage = D3D11_USAGE_IMMUTABLE;
vbd.ByteWidth = sizeof(Vertex::TreePointSprite) * TreeCount;
vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = 0;
vbd.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA vinitData;
vinitData.pSysMem = v;
HR(_device->CreateBuffer(&vbd, &vinitData, _treeSpritesVB.GetAddressOf()));
}
코드를 보면 기존의 VS단계에서 PS단계로 바로 넘어가는 것이 아니라 GS단계를 이제 거치는 것을 볼 수 있다. GS단계에서 정점을 4개로 불려주고 이 값을 PS단계로 전달하게 된다.
struct GeoOut
{
float4 PosH : SV_POSITION;
float3 PosW : POSITION;
float3 NormalW : NORMAL;
float2 Tex : TEXCOORD;
uint PrimID : SV_PrimitiveID;
};
// 최대 정점4개
[maxvertexcount(4)]
//정점 하나가 들어옴 도형의 넘버링 입력받은 값을 연산하고 저장해줄 곳
void GS(point VertexOut gin[1], uint primID : SV_PrimitiveID, inout TriangleStream<GeoOut> triStream)
{
//
// 플레이어를 바라보게
//
float3 up = float3(0.0f, 1.0f, 0.0f);
float3 look = gEyePosW - gin[0].CenterW;
look.y = 0.0f; // y-axis aligned, so project to xz-plane
look = normalize(look);
float3 right = cross(up, look);
//
// 4개 정점 만들기
//
float halfWidth = 0.5f*gin[0].SizeW.x;
float halfHeight = 0.5f*gin[0].SizeW.y;
float4 v[4];
v[0] = float4(gin[0].CenterW + halfWidth*right - halfHeight*up, 1.0f);
v[1] = float4(gin[0].CenterW + halfWidth*right + halfHeight*up, 1.0f);
v[2] = float4(gin[0].CenterW - halfWidth*right - halfHeight*up, 1.0f);
v[3] = float4(gin[0].CenterW - halfWidth*right + halfHeight*up, 1.0f);
//
// 만든 정점을 결과값 저장하는 곳에 밀어넣기
//
GeoOut gout;
[unroll]
for(int i = 0; i < 4; ++i)
{
gout.PosH = mul(v[i], gViewProj);
gout.PosW = v[i].xyz;
gout.NormalW = look;
gout.Tex = gTexC[i];
gout.PrimID = primID;
triStream.Append(gout);
}
}
굳이 정점을 이렇게 연산해서 넘겨주는 이유는 4개를 넘겨주는 것보다는 1개를 가지고 연산하는 것이 비용을 아껴줄 수 있기 때문이다.
'게임공부 > Directx11(물방울책)' 카테고리의 다른 글
[Directx11][C++][물방울]9. Tessellation (5) | 2024.10.29 |
---|---|
[Directx11][C++][물방울]8. Compute Shader (0) | 2024.10.22 |
[Directx11][C++][물방울]6. Stencil (1) | 2024.10.21 |
[Directx11][C++][물방울]5. 블렌딩 (1) | 2024.10.19 |
[Directx11][C++][물방울]4. 텍스쳐 (1) | 2024.10.18 |