Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Engine/common_dx/include/DXSkybox.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class DXSkybox

void EquirectangularToCube();
void CalculateIrradiance();
// Spherical Harmonics based irradiance calculation
void CalculateIrradiance(const std::vector<glm::vec3>& E_lm);
void PrefilteredEnvironmentMap();
void BRDFLUT();

Expand Down Expand Up @@ -177,4 +179,11 @@ class DXSkybox
glm::lookAtLH(glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 1.f), glm::vec3(0.f, 1.f, 0.f)),
glm::lookAtLH(glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 0.f, -1.f), glm::vec3(0.f, 1.f, 0.f))
};

// Spherical Harmonics Coefficients for diffuse irradiance
struct SHCoefficients
{
glm::vec4 E_lm[9];
};
std::vector<glm::vec3> CalculateSHCoefficients(const float* hdrData, int width, int height);
};
13 changes: 3 additions & 10 deletions Engine/common_dx/include/DXTexture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,18 @@ class DXTexture
std::function<void(UINT)> deallocator,
const ComPtr<ID3D12Fence>& fence,
const HANDLE& fenceEvent,
bool isHDR, const std::filesystem::path& path_, std::string name_, bool flip);
void LoadSkyBox(
bool isHDR,
const std::filesystem::path& right,
const std::filesystem::path& left,
const std::filesystem::path& top,
const std::filesystem::path& bottom,
const std::filesystem::path& front,
const std::filesystem::path& back
);
bool isHDR, const std::filesystem::path& path_, const std::string& name_, bool flip);
void SetTextureID(const int id) { texID = id; }

[[nodiscard]] void* GetTextureData() const { return m_textureData; }
[[nodiscard]] int GetWidth() const { return width; }
[[nodiscard]] int GetHeight() const { return height; }
[[nodiscard]] glm::vec2 GetSize() const { return glm::vec2{ width, height }; }
[[nodiscard]] std::string GetName() const { return name; }
[[nodiscard]] int GetTextrueId() const { return texID; }
private:
ComPtr<ID3D12Resource> m_texture;
void* m_textureData;

int width, height;
int texID;
Expand Down
4 changes: 2 additions & 2 deletions Engine/common_dx/source/DXMipmapGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void DXMipmapGenerator::Generate(const ComPtr<ID3D12Device>& device, const ComPt
for (uint32_t arraySlice = 0; arraySlice < texDesc.DepthOrArraySize; ++arraySlice)
{
uint32_t dstSubresource = D3D12CalcSubresource(dstMip, arraySlice, 0, texDesc.MipLevels, texDesc.DepthOrArraySize);
barriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition(texture.Get(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, dstSubresource));
barriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition(texture.Get(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, dstSubresource));
}
commandList->ResourceBarrier(static_cast<UINT>(barriers.size()), barriers.data());

Expand Down Expand Up @@ -121,7 +121,7 @@ void DXMipmapGenerator::Generate(const ComPtr<ID3D12Device>& device, const ComPt
cpuHandle.Offset(1, m_descriptorSize);
gpuHandle.Offset(1, m_descriptorSize);

float texelSize[2] = { 1.0f / dstWidth, 1.0f / dstHeight };
float texelSize[2] = { 1.0f / static_cast<float>(dstWidth), 1.0f / static_cast<float>(dstHeight) };
commandList->SetComputeRoot32BitConstants(0, 2, texelSize, 0);

UINT dispatchX = (dstWidth + 7) / 8;
Expand Down
232 changes: 230 additions & 2 deletions Engine/common_dx/source/DXSkybox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ void DXSkybox::Initialize(const std::filesystem::path& path,
faceSize = m_equirectangularMap->GetHeight();

EquirectangularToCube();
CalculateIrradiance();
//CalculateIrradiance();
// Spherical Harmonics based irradiance calculation
// Should free texture data after calculating SH coefficients
std::vector<glm::vec3> E_lm = CalculateSHCoefficients(static_cast<float*>(m_equirectangularMap->GetTextureData()), m_equirectangularMap->GetWidth(), m_equirectangularMap->GetHeight());
CalculateIrradiance(E_lm);
PrefilteredEnvironmentMap();
BRDFLUT();
}
Expand Down Expand Up @@ -213,11 +217,14 @@ void DXSkybox::EquirectangularToCube()
rtvHandle.ptr += m_rtvDescriptorSize;
}

barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_cubemap.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_cubemap.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
m_commandList->ResourceBarrier(1, &barrier);

m_mipmapGenerator->Generate(m_device, m_commandList, m_cubemap.Get());

barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_cubemap.Get(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
m_commandList->ResourceBarrier(1, &barrier);

ExecuteCommandList();

D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
Expand Down Expand Up @@ -372,6 +379,227 @@ void DXSkybox::CalculateIrradiance()
m_device->CreateShaderResourceView(m_irradianceMap.Get(), &srvDesc, m_srvHandles[2].first);
}

std::vector<glm::vec3> DXSkybox::CalculateSHCoefficients(const float* hdrData, int width, int height)
{
std::vector<glm::vec3> L_lm(9, glm::vec3(0.0f));

float PI = glm::pi<float>();
float dTheta = PI / static_cast<float>(height);
float dPhi = 2.0f * PI / static_cast<float>(width);

for (int j = 0; j < height; ++j)
{
float theta = PI * (static_cast<float>(j) + 0.5f) / static_cast<float>(height);
float sinTheta = std::sin(theta);
float cosTheta = std::cos(theta);

for (int i = 0; i < width; ++i)
{
float phi = 2.0f * PI * (static_cast<float>(i) + 0.5f) / static_cast<float>(width);
float sinPhi = std::sin(phi);
float cosPhi = std::cos(phi);

// Y-axis points up, Z-axis points forward, X-axis points right
// Convert from spherical coordinates to Cartesian coordinates
// Minus sign is needed to flip the direction of the vector to match the cubemap's coordinate system
float x = -sinTheta * cosPhi;
float y = -cosTheta;
float z = -sinTheta * sinPhi;

int index = (j * width + i) * 4;
glm::vec3 color(hdrData[index], hdrData[index + 1], hdrData[index + 2]);

float dArea = sinTheta * dTheta * dPhi;

// Z-axis points up, Y-axis points forward, X-axis points right
//float Y00 = 0.5f * std::sqrt(1.0f / PI);
//float Y1_1 = 0.5f * std::sqrt(3.0f / PI) * y;
//float Y10 = 0.5f * std::sqrt(3.0f / PI) * z;
//float Y11 = 0.5f * std::sqrt(3.0f / PI) * x;
//float Y2_2 = 0.5f * std::sqrt(15.0f / PI) * x * y;
//float Y2_1 = 0.5f * std::sqrt(15.0f / PI) * y * z;
//float Y20 = 0.25f * std::sqrt(5.0f / PI) * (3.0f * z * z - 1.0f);
//float Y21 = 0.5f * std::sqrt(15.0f / PI) * x * z;
//float Y22 = 0.25f * std::sqrt(15.0f / PI) * (x * x - y * y);

// Reorder to match the cubemap's coordinate system where Y-axis points up, Z-axis points forward, X-axis points right
float Y00 = 0.5f * std::sqrt(1.0f / PI);
float Y1_1 = 0.5f * std::sqrt(3.0f / PI) * z;
float Y10 = 0.5f * std::sqrt(3.0f / PI) * y;
float Y11 = 0.5f * std::sqrt(3.0f / PI) * x;
float Y2_2 = 0.5f * std::sqrt(15.0f / PI) * x * z;
float Y2_1 = 0.5f * std::sqrt(15.0f / PI) * z * y;
float Y20 = 0.25f * std::sqrt(5.0f / PI) * (3.0f * y * y - 1.0f);
float Y21 = 0.5f * std::sqrt(15.0f / PI) * x * y;
float Y22 = 0.25f * std::sqrt(15.0f / PI) * (x * x - z * z);

L_lm[0] += color * Y00 * dArea;
L_lm[1] += color * Y1_1 * dArea;
L_lm[2] += color * Y10 * dArea;
L_lm[3] += color * Y11 * dArea;
L_lm[4] += color * Y2_2 * dArea;
L_lm[5] += color * Y2_1 * dArea;
L_lm[6] += color * Y20 * dArea;
L_lm[7] += color * Y21 * dArea;
L_lm[8] += color * Y22 * dArea;
}
}

float A0 = PI;
float A1 = 2.0f / 3.0f * PI;
float A2 = 0.25f * PI;

std::vector<glm::vec3> E_lm(9);
E_lm[0] = L_lm[0] * A0;

E_lm[1] = L_lm[1] * A1;
E_lm[2] = L_lm[2] * A1;
E_lm[3] = L_lm[3] * A1;

E_lm[4] = L_lm[4] * A2;
E_lm[5] = L_lm[5] * A2;
E_lm[6] = L_lm[6] * A2;
E_lm[7] = L_lm[7] * A2;
E_lm[8] = L_lm[8] * A2;

return E_lm;
}

// Spherical Harmonics based irradiance calculation
void DXSkybox::CalculateIrradiance(const std::vector<glm::vec3>& E_lm)
{
// Create Cubemap Resource
D3D12_RESOURCE_DESC texDesc = {};
texDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
texDesc.Width = static_cast<UINT64>(irradianceSize);
texDesc.Height = static_cast<UINT>(irradianceSize);
texDesc.DepthOrArraySize = 6;
texDesc.MipLevels = 1;
texDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
texDesc.SampleDesc.Count = 1;
texDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;

D3D12_CLEAR_VALUE clearValue = {};
clearValue.Format = texDesc.Format;
memcpy(clearValue.Color, clearColor, sizeof(float) * 4);

CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_DEFAULT);
#if USE_NSIGHT_AFTERMATH
std::string eventMarker = "CalculateIrradiance()";
AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_SetEventMarker(m_hAftermathCommandListContext, (void*)eventMarker.c_str(), (unsigned int)eventMarker.size() + 1));
#endif
DXHelper::ThrowIfFailed(m_device->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&texDesc,
D3D12_RESOURCE_STATE_COMMON,
&clearValue,
IID_PPV_ARGS(&m_irradianceMap)
));
DXHelper::ThrowIfFailed(m_irradianceMap->SetName(L"Skybox Irradiance Map Resource"));

D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = m_rtvHeap->GetCPUDescriptorHandleForHeapStart();
for (UINT f = 0; f < 6; ++f)
{
D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
rtvDesc.Format = texDesc.Format;
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
rtvDesc.Texture2DArray.MipSlice = 0;
rtvDesc.Texture2DArray.FirstArraySlice = f;
rtvDesc.Texture2DArray.ArraySize = 1;
m_device->CreateRenderTargetView(m_irradianceMap.Get(), &rtvDesc, rtvHandle);
rtvHandle.ptr += m_rtvDescriptorSize;
}

// Prepare Pipeline
CD3DX12_ROOT_PARAMETER1 rootParameters[2];
rootParameters[0].InitAsConstants(32, 0, 0, D3D12_SHADER_VISIBILITY_VERTEX);
rootParameters[1].InitAsConstantBufferView(1, 0, D3D12_ROOT_DESCRIPTOR_FLAG_NONE, D3D12_SHADER_VISIBILITY_PIXEL);

CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc = {};
rootSignatureDesc.Init_1_1(_countof(rootParameters), rootParameters, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

ComPtr<ID3DBlob> signature, error;
D3D12SerializeVersionedRootSignature(&rootSignatureDesc, &signature, &error);
DXHelper::ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignatures[1])));
DXHelper::ThrowIfFailed(m_rootSignatures[1]->SetName(L"Skybox Spherical Harmonics Irradiance Root Signature"));

// Create Pipeline State Object (PSO)
struct VA
{
glm::vec3 position;
};

DXAttributeLayout positionLayout{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(VA, position), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA };

std::vector<DXGI_FORMAT> rtvFormats = { texDesc.Format };
m_pipelines[1] = DXPipeLineBuilder(m_device, m_rootSignatures[1])
.SetShaders("../Engine/shaders/hlsl/Cubemap.vert.hlsl", "../Engine/shaders/hlsl/SphericalHarmonicsIrradiance.frag.hlsl")
.SetLayout(std::initializer_list<DXAttributeLayout>{ positionLayout })
.SetRasterizer(D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_NONE, false)
.SetDepthStencil(false, false)
.SetRenderTargets(rtvFormats)
.Build();

SHCoefficients shData;
for (int i = 0; i < 9; ++i)
{
shData.E_lm[i] = glm::vec4(E_lm[i], 0.0f);
}
DXConstantBuffer<SHCoefficients> shConstantBuffer(m_device, 1);
shConstantBuffer.UpdateConstant(&shData, sizeof(SHCoefficients), 0);

// Record Command List
DXHelper::ThrowIfFailed(m_commandAllocator->Reset());
DXHelper::ThrowIfFailed(m_commandList->Reset(m_commandAllocator.Get(), m_pipelines[1]->GetPipelineState().Get()));

D3D12_VIEWPORT viewport = { 0.f, 0.f, static_cast<FLOAT>(irradianceSize), static_cast<FLOAT>(irradianceSize), 0.f, 1.f };
D3D12_RECT scissorRect = { 0, 0, static_cast<LONG>(irradianceSize), static_cast<LONG>(irradianceSize) };
m_commandList->RSSetViewports(1, &viewport);
m_commandList->RSSetScissorRects(1, &scissorRect);

m_commandList->SetGraphicsRootSignature(m_rootSignatures[1].Get());

m_commandList->SetGraphicsRootConstantBufferView(1, shConstantBuffer.GetGPUVirtualAddress(0));

D3D12_VERTEX_BUFFER_VIEW vbv = m_skyboxVertexBuffer->GetView();
m_commandList->IASetVertexBuffers(0, 1, &vbv);
m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_irradianceMap.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_RENDER_TARGET);
m_commandList->ResourceBarrier(1, &barrier);

rtvHandle = m_rtvHeap->GetCPUDescriptorHandleForHeapStart();
for (int f = 0; f < 6; ++f)
{
m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);

WorldToNDC worldToNDC = { views[f], projection };
//matrixConstantBuffer.UpdateConstant(&worldToNDC, 0);
//m_commandList->SetGraphicsRootConstantBufferView(0, matrixConstantBuffer.GetGPUVirtualAddress(0));
m_commandList->SetGraphicsRoot32BitConstants(0, 32, &worldToNDC, 0);

m_commandList->DrawInstanced(36, 1, 0, 0);

rtvHandle.ptr += m_rtvDescriptorSize;
}

barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_irradianceMap.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
m_commandList->ResourceBarrier(1, &barrier);

ExecuteCommandList();

D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Format = texDesc.Format;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.TextureCube.MipLevels = 1;

// Store Irradiance texture in third array of m_srvDescriptorIndices offset
m_device->CreateShaderResourceView(m_irradianceMap.Get(), &srvDesc, m_srvHandles[2].first);
}

void DXSkybox::PrefilteredEnvironmentMap()
{
// Create Cubemap Resource
Expand Down
24 changes: 4 additions & 20 deletions Engine/common_dx/source/DXTexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ void DXTexture::LoadTexture(
std::function<void(UINT)> deallocator,
const ComPtr<ID3D12Fence>& fence,
const HANDLE& fenceEvent,
bool isHDR, const std::filesystem::path& path_, std::string name_, bool flip)
bool isHDR, const std::filesystem::path& path_, const std::string& name_, bool flip)
{
m_srvHandle = srvHandle;
m_deallocator = std::move(deallocator);
Expand All @@ -34,13 +34,8 @@ void DXTexture::LoadTexture(
auto path = path_;
int texChannels;
//Read in image file
void* data{ nullptr };
if (isHDR)
{
data = stbi_loadf(path.string().c_str(), &width, &height, &texChannels, STBI_rgb_alpha);
}
else
data = stbi_load(path.string().c_str(), &width, &height, &texChannels, STBI_rgb_alpha);
m_textureData = isHDR ? m_textureData = stbi_loadf(path.string().c_str(), &width, &height, &texChannels, STBI_rgb_alpha) :
m_textureData = stbi_load(path.string().c_str(), &width, &height, &texChannels, STBI_rgb_alpha);

ComPtr<ID3D12Resource> textureUploadHeap;

Expand Down Expand Up @@ -81,7 +76,7 @@ void DXTexture::LoadTexture(
));

D3D12_SUBRESOURCE_DATA textureData = {};
textureData.pData = data;
textureData.pData = m_textureData;
textureData.RowPitch = static_cast<INT64>(width) * (isHDR ? 16 : 4); // 16 bytes for R32G32B32A32_FLOAT, 4 bytes for R8G8B8A8_UNORM
textureData.SlicePitch = textureData.RowPitch * static_cast<UINT>(height);

Expand Down Expand Up @@ -119,14 +114,3 @@ void DXTexture::LoadTexture(
}
//CloseHandle(fenceEvent);
}

void DXTexture::LoadSkyBox(bool isHDR, const std::filesystem::path& right, const std::filesystem::path& left, const std::filesystem::path& top, const std::filesystem::path& bottom, const std::filesystem::path& front, const std::filesystem::path& back)
{
isHDR;
right;
left;
top;
bottom;
front;
back;
}
Loading
Loading