Billboard
게임 내에서 오브젝트를 보면 항상 카메라를 향하고 있는 오브젝트들이 있다.
이런 오브젝트중 가장 많이 쓰이는 오브젝트인 풀을 이 Billboard를 활용해서 만들어보자
Shader
struct VertexInput
{
float4 Position : Position;
float2 Uv : Uv;
float2 Scale : Scale;
};
struct VertexOutput
{
float4 Position : SV_Position;
float2 Uv : Uv;
};
VertexOutput VS(VertexInput input)
{
VertexOutput output;
float4 position = WorldPosition(input.Position);
float3 up = float3(0, 1, 0);
//float3 forward = float3(0, 0, 1);
float3 forward = position.xyz - ViewPosition();
float3 right = normalize(cross(up, forward));
position.xyz += (input.Uv.x - 0.5f) * right * input.Scale.x;
position.xyz += (1.0f - input.Uv.y - 0.5f) * up * input.Scale.y;
position.w = 1.0f;
output.Position = ViewProjection(position);
output.Uv = input.Uv;
return output;
}
각각 벡터를 계산해준다
forward는 항상 카메마를 바라봐야 하기 때문에 현재 위치에서 ViewPosition을 빼서 방향을 구해준다.
이제 이 벡터들을 활용해서 텍스처의 uv를 계산해준다.
float4 PS(VertexOutput input) : SV_Target
{
float4 diffuse = DiffuseMap.Sample(LinearSampler, input.Uv);
//clip(diffuse.a - 0.3f);
if (diffuse.a < 0.3)
discard;
return diffuse;
}
그리고 각 픽셀의 투명값이 0.3 이하일 경우 제거해준다.
Billboard Class
#pragma once
#define MAX_BILLBOARD_COUNT 10000
class Billboard : public Renderer
{
public:
Billboard(wstring file);
~Billboard();
void Update();
void Render();
void Add(Vector3& position, Vector2& scale);
private:
struct VertexBillboard
{
Vector3 Position;
Vector2 Uv;
Vector2 Scale;
};
private:
VertexBillboard* vertices;
UINT* indices;
UINT drawCount = 0;
UINT prevCount = 0;
Texture* texture;
ID3DX11EffectShaderResourceVariable* sDiffuseMap;
};
Shader에 넘겨줄 VertexBillboard 구조체를 만들어준다.
#include "Framework.h"
#include "Billboard.h"
Billboard::Billboard(wstring file)
: Renderer(L"83_Billboard.fxo")
{
vertexCount = MAX_BILLBOARD_COUNT * 4;
vertices = new VertexBillboard[vertexCount];
vertexBuffer = new VertexBuffer(vertices, vertexCount, sizeof(VertexBillboard), 0, true);
indexCount = MAX_BILLBOARD_COUNT * 6;
indices = new UINT[indexCount];
for (UINT i = 0; i < MAX_BILLBOARD_COUNT; i++)
{
indices[i * 6 + 0] = i * 4 + 0;
indices[i * 6 + 1] = i * 4 + 1;
indices[i * 6 + 2] = i * 4 + 2;
indices[i * 6 + 3] = i * 4 + 2;
indices[i * 6 + 4] = i * 4 + 1;
indices[i * 6 + 5] = i * 4 + 3;
}
indexBuffer = new IndexBuffer(indices, indexCount);
texture = new Texture(file);
sDiffuseMap = shader->AsSRV("DiffuseMap");
}
Billboard::~Billboard()
{
SafeDeleteArray(vertices);
SafeDeleteArray(indices);
SafeDelete(texture);
}
void Billboard::Update()
{
Super::Update();
}
void Billboard::Render()
{
if (drawCount != prevCount)
{
prevCount = drawCount;
D3D11_MAPPED_SUBRESOURCE subResource;
D3D::GetDC()->Map(vertexBuffer->Buffer(), 0, D3D11_MAP_WRITE_DISCARD, 0, &subResource);
{
memcpy(subResource.pData, vertices, sizeof(VertexBillboard) * vertexCount);
}
D3D::GetDC()->Unmap(vertexBuffer->Buffer(), 0);
}
Super::Render();
sDiffuseMap->SetResource(texture->SRV());
shader->DrawIndexed(0, Pass(), drawCount * 6);
}
void Billboard::Add(Vector3 & position, Vector2 & scale)
{
vertices[drawCount * 4 + 0].Position = position;
vertices[drawCount * 4 + 1].Position = position;
vertices[drawCount * 4 + 2].Position = position;
vertices[drawCount * 4 + 3].Position = position;
vertices[drawCount * 4 + 0].Uv = Vector2(0, 1);
vertices[drawCount * 4 + 1].Uv = Vector2(0, 0);
vertices[drawCount * 4 + 2].Uv = Vector2(1, 1);
vertices[drawCount * 4 + 3].Uv = Vector2(1, 0);
vertices[drawCount * 4 + 0].Scale = scale;
vertices[drawCount * 4 + 1].Scale = scale;
vertices[drawCount * 4 + 2].Scale = scale;
vertices[drawCount * 4 + 3].Scale = scale;
drawCount++;
}
그리고 사각형을 받은 Position과 Scale을 만들어 Shader에 넘겨주고 그려준다.
이렇게 하고 이 Billboard를 만들어 실행시키면
각 풀들이 카메라를 향해 계속해서 움직인다.