D3DRaytracing
Table of Contents
D3DRaytracing note.
<!– more –>
D3D
Conception
CBV constant buffer view
SRV shader resource view
UAV unordered access view
D3DRaytracing
hello world
Acceleration Structures
加速结构是不透明的数据结构,其表示了场景几何。渲染时,该结构用于射线相交检测。有两类加速结构:Bottom-Level Acceleration Structure, Top-Level Acceleration Structure。BLAS 表示 local-space mesh, 其不包含顶点的世界空间位置信息或实例信息。TLAS 表示整个场景,TLAS 会引用 BLAS,并存储了每个引用的 BLAS 对应的 local-to-world 变换矩阵。
Raytracing Pipeline State
和光栅化一样,Raytracing 也需要创建一个 pipleline state 来控制 fixed-function units,以及描述将会使用哪些 shader。
Shader-Libraries
dxcompiler 是新的 SM6.x 的编译器,其引入了 Shader-libraries 这个新的概念。Libraries 允许我们编译一个包含多个 shaders 的文件,并且不指定一个 entry point。
Ray-Tracing Shaders
DXR 引入了 5 种新的 shader 类型:ray-generation, miss, closet-hit, any-hit, intersection。
ray-generation shader
ray-generation shader 是 ray-tracing pipline 的第一个阶段。每一个 work item 会执行一次 ray-generation shader。在该 shader 中,用户可以生成 primary-rays,发布 ray-query calls。
miss-shader
当 raytrace query 没有 hit 到 TLAS 中的任何对象时,就会调用 miss-shader
hit-group
一个 hit group 是 Closest-Hit,Any-Hit,Intersection Shaders 的集合。其描述了如何进行相交检测,以及当检测到射线相交后的行为。
- any hit shader :在遍历过程中,发现一个 intersection 时,就会调用该 shader。其主要用途是用于确定一个 intersection 是否应该被接受。例如,对于 alpha-tested 几何体,如果 alpha test 失败,则我们应该忽略该 intersection。
Tips 1: 当创建加速结构时使用了 D3D12_RAYTRACING_GEOMETRY_FLAG_OPAQUE 标记,会忽略 any-hit shader。
Tips 2: 当找到多个 intersection 时,any-hit shaders 的执行顺序是不定的。即 第一个调用的可能不是离原点最近的 intersection。而且对于指定的 ray,该 shader 调用的次数也可能会变化。 - closest hit shader : 每次遍历中,只会为最近的 intersection 调用一次该 shader.
- intersection shader : 当 primitive 类型为轴对齐包围盒时,才会调用该 shader。对于 triangles,无论是否指定了一个 intersection shader 都只会使用一个内部的 triangle-intersection shader。
Creating RT Pipeline State Object
创建 RT Pipeline State Object 和 Pipeline State Object 不同,ID3D12StateObjectPtr 对象是新的 DXR 的 state interface。没有使用类似 D3D12_GRAPHICS_PIPELINE_STATE_DESC 这样的结构体,来构建 RTPSO,我们需要构建一个 D3D12_STATE_SUBOBJECT 数组。每个 sub-object 描述了单个元素的状态。
- DxilLibray
对 D3D12_STATE_SUBOBJECT_TYPE_DXIL_LIBRARY 类型 sub-object 的抽象 - HitProgram
对 D3D12_STATE_SUBOBJECT_TYPE_HIT_GROUP 类型 sub-object 的抽象 - LocalRootSignature
DXR 引入了新的概念 Local Root Signature. 在 graphics 和 compute pipelines, 我们有单个全局的 root-signatrue 被所有 programs 所使用。对于 ray-tracing, 除此之外,我们可以创建 local root-signatures 并将他们绑定到特定 shaders。root-signature 的 size 会影响 Shader Binding Table 的 size,Local Root Signature 允许我们优化 Shader Binding Table. - ExportAssociation
对 D3D12_STATE_SUBOBJECT_TYPE_SUBOBJECT_TO_EXPORTS_ASSOCIATION 类型 sub-object 的抽象。将一个 sub-object 绑定到 shaders 和 hit-groups。 - ShaderConfig
对 D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_SHADER_CONFIG 类型 sub-object 的抽象 - PipelineConfig
对 D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_PIPELINE_CONFIG 类型 sub-object 的抽象 - GlobalRootSignature
对 D3D12_STATE_SUBOBJECT_TYPE_GLOBAL_ROOT_SIGNATURE 类型 sub-object 的抽象 - CreateStateObject
创建 ID3D12StateObject
D3D12_STATE_OBJECT_DESC desc; desc.NumSubobjects = index; desc.pSubobjects = subobjects.data(); desc.Type = D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE; d3d_call(mpDevice->CreateStateObject(&desc, IID_PPV_ARGS(&mpPipelineState)));
ShaderTable
Shader-Table 是 GPU 可见的 buffer,该 buffer 由 application 所拥有和管理(由 application 进行分配、数据上传等)。shader-table 是 records 的数组,其有两个职责:
- 描述场景几何体和被执行的程序之间的关系。我们有多个 hit 和 miss 程序关联到 state object,当射线碰到一个几何体后我们需要知道执行哪个 shader 程序。
- 绑定资源到 pipeline。我们可以使用不同的 local root signature 来创建每一个 shader 程序。每个几何体可能需要不同的资源(vertex-buffer, textures, 等等) 集合
Tips: 当个 DispatchRays() 调用中,可以使用多个 shader-tables。
Shader Table Records
每个 shader table record 有两段。一个不透明的 shader program identifier,加一个 root table,root table 中包含了该 shader 的资源绑定。
这里的 root table 和 光栅化 pipeline 中的 root table 非常类似。不同点在于,我们直接设置 root table 中的条目,而不是使用 setter 方法。
root table 中不同条目的尺寸限制如下:
- Root Constants 4 Bytes
- Root Descriptors 8 Bytes
- Descriptor Tables 8 Bytes (这和正常的 root signature 所需的尺寸不同)
Tips: root-descriptors 必须被存储到 8 字节对其的地址中。
Shader Table Layout
shader-table 是 shader-table records 的数组。没有规则限定 records 应该如何排布。有一系列参数可以决定 indexing 如何进行。
shader-table 中所有的 shader-table records 必须有相同的尺寸, 因此我们需要按照最大的 record 条目来确定 shader-table 的尺寸。
Raytrace
Shader Resources
BasicShaders
A Very Simplified Execution Description
在 ray-generation shader 中,创建 rays。每条 ray 会和 acceleration structure 做相交测试。如果 ray 没有碰到任何物体,miss shader 将会被调用。如果 ray 碰到了物体,则会为最近的 intersection 调用 closest hit shader。遍历和相交测试发生在 fixed-function unit。
Tracing Rays
DXR 引入了新的 HLSL 结构体-RayDesc, 以及新的内置函数 TraceRay(),该函数用于初始化一个 ray-tracing query.
struct RayDesc { float3 Origin; // 世界空间中ray 的原点 float TMin; float3 Direction; // 世界空间中ray的方向 float TMax; };
TraceRay 参数解释:
1 Top Level Acceleration structure
2 ray flags. 这些 flags 可用于控制遍历行为,如 开启背面剔除
3 ray masks. 可用于整体剔除物体
4 RayContributionToHitGroupIndex 5 MultiplierForGeometryContributionToHitGroupIndex 用于索引 shader-table
6 miss-shader index。index 相对于调用 DispatchRays() 传递的 miss-shader index。我们只有 1 个 miss-shader, 因此为 0
7 RayDesc
8 用于 raytracing shader 各个阶段之间传递数据, 因此需要保证各个阶段所使用的定义一致
TraceRay 其实是一个模板函数,其会根据不同 RayPayload 生成不同函数。Payload 牵涉到 c++ 代码。当创建 ray-tracing 程序时,我们需要设置 payload 的最大尺寸到 D3D12_RAYTRACING_SHADER_CONFIG 中, 我们需要为单个 pipeline-state 中的所有程序使用相同的 maximum payload size。
下面为整个 raytracing 的 shader:
// TLAS RaytracingAccelerationStructure gRtScene : register(t0); // UAV RWTexture2D<float4> gOutput : register(u0); float3 linearToSrgb(float3 c) { // Based on http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html float3 sq1 = sqrt(c); float3 sq2 = sqrt(sq1); float3 sq3 = sqrt(sq2); float3 srgb = 0.662002687 * sq1 + 0.684122060 * sq2 - 0.323583601 * sq3 - 0.0225411470 * c; return srgb; } struct RayPayload { float3 color; }; [shader("raygeneration")] void rayGen() { // 发射 Rays 的索引 uint3 launchIndex = DispatchRaysIndex(); // 发射 Rays 的范围(width,height,depth) uint3 launchDim = DispatchRaysDimensions(); float2 crd = float2(launchIndex.xy); float2 dims = float2(launchDim.xy); float2 d = ((crd/dims) * 2.f - 1.f); float aspectRatio = dims.x / dims.y; RayDesc ray; ray.Origin = float3(0, 0, -2); ray.Direction = normalize(float3(d.x * aspectRatio, -d.y, 1)); ray.TMin = 0; ray.TMax = 100000; RayPayload payload; TraceRay( gRtScene, 0 /*rayFlags*/, 0xFF /*rayMasks*/, 0 /* ray index*/, 0, 0 /* miss-shader idx */, ray, payload ); float3 col = linearToSrgb(payload.color); gOutput[launchIndex.xy] = float4(col, 1); } [shader("miss")] void miss(inout RayPayload payload) { payload.color = float3(0.4, 0.6, 0.2); } [shader("closesthit")] void chs(inout RayPayload payload, in BuiltInTriangleIntersectionAttributes attribs) { float3 barycentrics = float3(1.0 - attribs.barycentrics.x - attribs.barycentrics.y, attribs.barycentrics.x, attribs.barycentrics.y); const float3 A = float3(1, 0, 0); const float3 B = float3(0, 1, 0); const float3 C = float3(0, 0, 1); payload.color = A * barycentrics.x + B * barycentrics.y + C * barycentrics.z; }
Instancing
ConstantBuffer
PerInstanceConstantBuffer
SecondGeometry
PerGeometryHitShader
SecondRayType
Refit
参考资料
- https://github.com/NVIDIAGameWorks/DxrTutorials
- https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html
- https://docs.microsoft.com/en-us/samples/microsoft/directx-graphics-samples/d3d12-raytracing-samples-win32/
- https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Samples/Desktop/D3D12Raytracing
- A Gentle Introduction To DirectX Raytracing http://cwyman.org/code/dxrTutors/dxr_tutors.md.html
- D3D12 RootSignature https://zhuanlan.zhihu.com/p/388534044