路径追踪管线
对于每个像素,发射32条随机射线,每条射线最多会反弹6次,用俄罗斯轮盘赌提前终止一些射线。路径追踪结束后,得到累积的Radiance并取平均值来计算最终结果。

Cornell Box中进行路径追踪

Sponza中进行路径追踪
float3 PathTracing(in float3 startPos, in float3 startDir, inout uint seed)
{
float3 radiance = 0.0f;
float3 attenuation = 1.0f;
RayDesc ray = Ray(startPos, startDir, rayTmin, rayTmax);
RayPayload prd;
prd.seed = seed;
prd.rayDepth = 0;
while(prd.rayDepth < maxPathLength)
{
TraceRay(scene, 0, ~0, 0, 1, 0, ray, prd);
//Russian roulette
float probContinue = max(prd.attenuation.r, max(prd.attenuation.g, prd.attenuation.b));
if (prd.rayDepth > 3)
{
if (rnd(seed) > probContinue)
break;
prd.attenuation /= probContinue;
}
radiance += attenuation * prd.radiance;
attenuation *= prd.attenuation;
ray.Origin = prd.hitPos;
ray.Direction = prd.bounceDir;
++prd.rayDepth;
}
seed = prd.seed;
return radiance;
}
路径追踪的 HLSL 代码
PBR
对于 PBR,通过 Cook-Torrance BRDF 来计算 BRDF 的镜面部分。
对于BRDF中的DFG项,D为Trowbridge-Reitz,G为Smith,F为Fresnel-Schlick。

场景中的 PBR 物体

场景中的 PBR 物体
我实现了Fresnel-Schlick 反射、基于折射率 (IOR) 的折射和全内反射 (TIR)。通过菲涅尔系数以概率方式确定光线是反射还是折射,从而确保真实的光线相互作用。

场景中的玻璃材质

场景中的玻璃材质
DXR 管线
我将 DirectX 光线追踪管线集成到我的引擎中。在我的光追管线中,
- Build加速结构
- 设置RayGen、Miss、HitGroup
- 设置根签名
- 设置SBT
- DispatchRays

DirectX 光线追踪管线
void DXRPathTracer::BuildRaytracingPipeline()
{
m_rtPipeline.setDXRLib(&m_dxrLib);
m_rtPipeline.setGlobalRootSignature(&m_globalRS);
m_rtPipeline.addHitGroup(HitGroup(L"hitGp", L"closestHit", nullptr));
m_rtPipeline.addHitGroup(HitGroup(L"hitGpGlass", L"closestHitGlass", nullptr));
m_rtPipeline.addLocalRootSignature(LocalRootSignature(&m_hitGroupRS, { L"hitGp", L"hitGpGlass" }));
m_rtPipeline.setMaxPayloadSize(sizeof(float) * 16);
m_rtPipeline.setMaxRayDepth(2);
m_rtPipeline.build();
}
void DXRPathTracer::SetupShaderTable()
{
ShaderIdentifier* rayGenID = m_rtPipeline.getIdentifier(L"rayGen");
ShaderIdentifier* missRayID = m_rtPipeline.getIdentifier(L"missRay");
ShaderIdentifier* missShadowID = m_rtPipeline.getIdentifier(L"missShadow");
ShaderIdentifier* hitGpID = m_rtPipeline.getIdentifier(L"hitGp");
ShaderIdentifier* hitGpGlassID = m_rtPipeline.getIdentifier(L"hitGpGlass");
uint numObjs = m_scene->GetObjectsNum();
mShaderTable.create(recordSize, numObjs + 3);
HitGroupRecord* table = (HitGroupRecord*) mShaderTable.map();
table[0].data.shaderIdentifier = *rayGenID;
table[1].data.shaderIdentifier = *missRayID;
table[2].data.shaderIdentifier = *missShadowID;
auto& mtlArr = m_scene->GetMaterialArray();
for (uint i = 0; i < numObjs; ++i)
{
if(mtlArr[ m_scene->GetObject(i).m_materialIdx ].type == Glass)
table[3 + i].data.shaderIdentifier = *hitGpGlassID;
else
table[3 + i].data.shaderIdentifier = *hitGpID;
table[3 + i].data.objConsts.objectIdx = i;
}
mShaderTable.uploadData(m_cmdList);
}
void DXRPathTracer::BuildAccelerationStructure()
{
uint numObjs = m_scene->GetObjectsNum();
Array gpuMeshArr(numObjs);
Array transformArr(numObjs);
D3D12_GPU_VIRTUAL_ADDRESS vtxAddr = m_vertexBuffer.getGpuAddress();
D3D12_GPU_VIRTUAL_ADDRESS tdxAddr = m_tridexBuffer.getGpuAddress();
for (uint objIdx = 0; objIdx < numObjs; ++objIdx)
{
const SceneObject& obj = m_scene->GetObject(objIdx);
gpuMeshArr[objIdx].numVertices = obj.m_numVertices;
gpuMeshArr[objIdx].vertexBufferVA = vtxAddr + obj.m_vertexOffset * sizeof(VertexPNU);
gpuMeshArr[objIdx].numTridices = obj.m_numTridices;
gpuMeshArr[objIdx].tridexBufferVA = tdxAddr + obj.m_tridexOffset * sizeof(Tridex);
transformArr[objIdx] = obj.m_modelMatrix;
}
m_accelerationStructure.build(m_cmdList, gpuMeshArr, transformArr,
sizeof(VertexPNU), 1, m_buildMode, m_buildFlags);
ThrowFailedHR(m_cmdList->Close());
ID3D12CommandList* cmdLists[] = { m_cmdList };
m_cmdQueue->ExecuteCommandLists(1, cmdLists);
m_fence.waitCommandQueue(m_cmdQueue);
ThrowFailedHR(m_cmdAllocator->Reset());
ThrowFailedHR(m_cmdList->Reset(m_cmdAllocator, nullptr));
}
DXR 初始化引擎代码
截图
