|
在前两节,通过分析几个导入C++源码的Slang源码文件,大致了解了slang源码的结构与作用。
这里,我们重点关注C++和Slang源码是如何进行交互的。
GeneratePaths.cs.slang
先来回忆一下这个Slang文件干了什么:
路径生成,用gScene.camera.computeRayPinhole(pixel, params.frameDim)来计算从相机出发的光线路径。
用前缀和来计算样本偏移量查找表gSamplesOffset。
如果光线与场景没有交点:如果每个像素只固定采样一个,那么直接把颜色写入outputColor;否则,像素偏移量为outIdx,样本索引为i,那么写入sampleColor[outIdx+i]。
那么,在C++源码中是如何调用这个文件,让它起效果,同时管理它的数据,便于之后步骤使用的呢?
接下来更加仔细地分析一下PathTracer::generatePaths():
void PathTracer::generatePaths(RenderContext* pRenderContext, const RenderData& renderData)
{
...
// Bind resources.
auto var = mpGeneratePaths->getRootVar()["CB"]["gPathGenerator"];
setShaderData(var, renderData, false);
// ---------- Part 1 over ----------
mpGeneratePaths["gScene"] = mpScene->getParameterBlock();
if (mpRTXDI) mpRTXDI->setShaderData(mpGeneratePaths->getRootVar());
// ---------- Part 2 over ----------
// Launch one thread per pixel.
// The dimensions are padded to whole tiles to allow re-indexing the threads in the shader.
mpGeneratePaths->execute(pRenderContext, { mParams.screenTiles.x * tileSize, mParams.screenTiles.y, 1u });
// ---------- Part 3 over ----------
}
变量管理 - getRootVar()
/** PathTracer::generatePaths() **/
// Bind resources.
auto var = mpGeneratePaths->getRootVar()["CB"]["gPathGenerator"];
setShaderData(var, renderData, false);
看第一行这里:mpGeneratePaths->getRootVar()["CB"]["gPathGenerator"]。一个很合理的推想,这个CB正是三十六、NVIDIA Falcor - slang源码分析(一)中提到的cbuffer,里面正声明了一个变量gPathGenerator。大胆推测,getRootVar()之后可以用方括号来对应Slang源码中的变量。
也就是说,gPathGenerator(Slang)被绑定到了var(C++)中,并且作为setShaderData的输入传入。继续看setShaderData(),就豁然开朗:
void PathTracer::setShaderData(const ShaderVar& var, const RenderData& renderData, bool useLightSampling) const
{
// Bind static resources that don't change per frame.
if (mVarsChanged)
{
if (useLightSampling && mpEnvMapSampler) mpEnvMapSampler->setShaderData(var["envMapSampler"]);
var["sampleOffset"] = mpSampleOffset; // Can be nullptr
var["sampleColor"] = mpSampleColor;
var["sampleGuideData"] = mpSampleGuideData;
}
// Bind runtime data.
setNRDData(var["outputNRD"], renderData);
Texture::SharedPtr pViewDir;
if (mpScene->getCamera()->getApertureRadius() > 0.f)
{
pViewDir = renderData.getTexture(kInputViewDir);
if (!pViewDir) logWarning("Depth-of-field requires the '{}' input. Expect incorrect rendering.", kInputViewDir);
}
Texture::SharedPtr pSampleCount;
if (!mFixedSampleCount)
{
pSampleCount = renderData.getTexture(kInputSampleCount);
if (!pSampleCount) throw RuntimeError("PathTracer: Missing sample count input texture");
}
var["params"].setBlob(mParams); // --- 分析见下
var["vbuffer"] = renderData.getTexture(kInputVBuffer);
var["viewDir"] = pViewDir; // Can be nullptr
var["sampleCount"] = pSampleCount; // Can be nullptr
var["outputColor"] = renderData.getTexture(kOutputColor);
if (useLightSampling && mpEmissiveSampler)
{
// TODO: Do we have to bind this every frame?
mpEmissiveSampler->setShaderData(var["emissiveSampler"]);
}
}
很显然这里var方括号取出的都是Slang中PathGenerator这个结构体的成员。顺带一提,setBlob()就是设置这块变量的二进制数据。
绑定Scene
mpGeneratePaths["gScene"] = mpScene->getParameterBlock();
if (mpRTXDI) mpRTXDI->setShaderData(mpGeneratePaths->getRootVar());
我们看到这部分绑定和上面绑定PathGenerator的情况不尽相同。
这里,gScene是定义在Falcor提供的Slang库文件中的:
// Falcor/Scene/Scene.slang
ParameterBlock<Scene> gScene;
一个猜想是,用ParameterBlock声明的变量均不用getRootVar()来绑定,而是直接把ParameterBlock赋过去。
执行Slang脚本
// Launch one thread per pixel.
// The dimensions are padded to whole tiles to allow re-indexing the threads in the shader.
mpGeneratePaths->execute(pRenderContext, { mParams.screenTiles.x * tileSize, mParams.screenTiles.y, 1u });
TracePass.rt.slang
上面说,mpGeneratePaths执行过一次setShaderData(),即把PathGenerator中的各变量进行了绑定。
对于mpPathTracerBlock,同样也执行过一次setShaderData()。之后,mpPathTracerBlock会被绑定到tracePass.pVars->getRootVar()[&#39;gPathTracer&#39;]中。
在这里,不会在C++源码里执行类似tracePass.execute()的代码。回忆三十六、NVIDIA Falcor - slang源码分析(二),这里定义的shader组应该是到光线追踪的某个阶段被调用,而不是我们去主动调用它。需要提供的shader包括:raygen,miss,hit group shader。其中hit group包括any, closest, intersection shader。
最终执行:
// Full screen dispatch.
mpScene->raytrace(pRenderContext, tracePass.pProgram.get(), tracePass.pVars, uint3(mParams.frameDim, 1));
绑定关系
C++ | slang | pProgram的各shader | TracePass.rt.slang | mpPathTracerBlock | gPathTracer | 同时,setShaderData()会把一些变量绑定到TracePass与GeneratePaths中,包括:sampleOffset, sampleColor, sampleCount, outputColor等,它们同样会被绑定到ResolvePass中。另有一些变量包括:vbuffer, viewDir。
总结
来仔细回想一下我们的PathTracer是怎么工作的。
通过C++代码控制Slang代码,并且为它绑定变量,实现不同Slang步骤的联系。
PathTracer最终输出其中的一个端口是kOutputColor,正是在上面绑定到Slang源码中去的。
首先生成路径并且生成偏移量查找表。
在TracePass中,实际去追踪路径。
最终在ResolvePass中决定输出颜色。
相关链接
目录:
C++源码分析部分:
- 三十三、NVIDIA Falcor源码分析(一) - PathTracer概述
- 三十三、NVIDIA Falcor源码分析(二) - Pass与Program
- 三十三、NVIDIA Falcor源码分析(三) - 缓冲区与资源绑定
- 三十三、NVIDIA Falcor源码分析(四) - Var
- 三十三、NVIDIA Falcor源码分析(五) - 总结
Slang源码分析部分:
- 三十六、NVIDIA Falcor - slang源码分析(一)
- 三十六、NVIDIA Falcor - slang源码分析(二)
- 三十六、NVIDIA Falcor - slang源码分析(三)
参考
- NVIDIAGameWorks/Falcor: Real-Time Rendering Framework (github.com)
|
|