struct LightColor
{
Vec4 diffuse;
Vec4 ambient;
Vec4 specular;
};
struct LightInfo
{
LightColor color;
Vec4 position;
Vec4 direction;
int32 lightType;
float range;
float angle;
int32 padding;
};
struct LightParams
{
uint32 lightCount;
Vec3 padding;
LightInfo lights[50];
};
Light 컴포넌트는 위와 같은 구조체들을 가지고 있는데 MaterialParams와 달리 물체마다 다른 material을 가지고 있는 것이 아니라 존재하는 LightInfo들을 모두 가지고 있다. 그리고 Material 같은 물체마다 있는 정보들은 TableDescriptorHeap에 담아 저장하고 Light는 Root CBV에 담는다.
CD3DX12_DESCRIPTOR_RANGE ranges[] =
{
CD3DX12_DESCRIPTOR_RANGE(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, CBV_REGISTER_COUNT - 1, 1), // b1~b4
CD3DX12_DESCRIPTOR_RANGE(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, SRV_REGISTER_COUNT, 0), // t0~t4
};
CD3DX12_ROOT_PARAMETER param[2];
param[0].InitAsConstantBufferView(static_cast<uint32>(CBV_REGISTER::b0)); // b0
param[1].InitAsDescriptorTable(_countof(ranges), ranges);
그러기 위해서 RootSignature에서 b0~b4까지 사용하던 레지스터중 b0을 비워주고 그 자리에 CBV를 설정한다.
레지스터 개수가 바뀌었으므로 TableDescriptorHeap에 가서 그에 맞게 코드를 변경해줘야 한다.
CreateConstantBuffer(CBV_REGISTER::b0, sizeof(LightParams), 1);
CreateConstantBuffer(CBV_REGISTER::b1, sizeof(TransformParams), 256);
CreateConstantBuffer(CBV_REGISTER::b2, sizeof(MaterialParams), 256);
void ConstantBuffer::SetGlobalData(void* buffer, uint32 size)
{
assert(_elementSize == ((size + 255) & ~255));
::memcpy(&_mappedBuffer[0], buffer, size);
CMD_LIST->SetGraphicsRootConstantBufferView(0, GetGpuVirtualAddress(0));
}
Engine에서 다시 레지스터에 맞게 CreateConstantBuffer을 다시 작성해주고 b0 레지스터의 CBV는 PushData가 아닌 SetGlobalData를 사용한다. 데이터 개수가 정해져 있기 때문에 Index를 사용하지 않고 바로 memcpy로 복사해준다.
void Scene::PushLightData()
{
LightParams lightParams = {};
for (auto& gameObject : _gameObjects)
{
if (gameObject->GetLight() == nullptr)
continue;
const LightInfo& lightInfo = gameObject->GetLight()->GetLightInfo();
lightParams.lights[lightParams.lightCount] = lightInfo;
lightParams.lightCount++;
}
CONST_BUFFER(CONSTANT_BUFFER_TYPE::GLOBAL)->SetGlobalData(&lightParams, sizeof(lightParams));
}
Scene에서 gameobject들의 light 컴포넌트의 lightinfo를 모아 lightparams에 저장하고 SetGlobalData를 한다. 그리고 쉐이더 코드에도 LightColor, LightInfo 를 작성하는데 여기서 diffuse, ambient, specular 등을 왜 float4로 작성했는지 알 수 있다.
// 2 x 16byte elements
cbuffer IE
{
float4 Val1;
float2 Val2; // starts a new vector
float2 Val3;
};
// 3 x 16byte elements
cbuffer IE
{
float2 Val1;
float4 Val2; // starts a new vector
float2 Val3; // starts a new vector
};
hlsl packing rule이라고 쉐이더 코드를 작성할 때 규칙이 있는데, 데이터는 16바이트의 경계선을 넘을 수 없다. 그러니까 첫번째처럼 16바이트 val1로 꽉채우고 그 뒤 8바이트 두개 데이터가 들어가면 2x16바이트만 사용하지만, 두번쨰처럼 8바이트 한번, 16바이트 한번 8바이트 한번 들어가면 16바이트 val2가 va1이 쓰고 남은 8바이트 공간부터 사용하는 것이 아니라 그 다음 공간부터 사용하기 때문에 공간의 낭비가 일어난다. 그래서 C++에서도 hlsl 규칙에 맞춰 16바이트에 맞춰 타입을 사용하는 것이다.
'그래픽스' 카테고리의 다른 글
| [DX12] Skybox (0) | 2022.07.26 |
|---|---|
| [DX12] Lighting 코드 #2 (0) | 2022.07.25 |
| [DX12] Camera (0) | 2022.07.24 |
| [DX12] Comoponent (0) | 2022.07.24 |
| [DX12] Material (0) | 2022.07.23 |