A Gentle Introduction to DirectX Raytracing 10



In Tutorial 9, we added a Lambertian shading model with ray traced shadows. Before moving on to global illumination, we want to load and render with environment map light probes, which easily allow much more complex lighting.

教程 9中,我们添加了带有光线追踪阴影的 Lambertian 模型。在进入全局光照之前,我们想要加载和渲染环境贴图 光照探针,这样可以轻松实现更复杂的光照。

For this tutorial, we assume our environment maps are parameterized using a lat-long projection, for instance the free environment maps available here. I encourage you to use high dynamic range environments (e.g., with a .hdr filename), since they provide more realistic lighting with larger variations in light intensity.

对于本教程,我们假设我们的环境贴图使用经纬度投影进行参数化,例如此处提供的免费环境贴图。我鼓励您使用 高动态范围 的环境(例如,使用 .hdr 文件),因为它们提供更逼真的光照和更大的光强度变化。

Environment Maps in our Tutorials

Unlike most of the functionality we have added in our tutorials, using an environment map does not necessarily add a new RenderPass. Instead, it simply modifies existing ones, changing the behavior of our miss shaders as rays hit a more complex environment. In more complex pipelines, every RenderPass may need to use the environment map.

与我们在教程中添加的大多数功能不同,使用环境贴图不一定会添加新的 RenderPass。相反,它只是简单地修改现有的,在光线照射到更复杂的环境时改变我们的未命中着色器的行为。在更复杂的管道中,每个 RenderPass 都可能需要使用环境贴图。

To simplify this tutorial to focus on exactly how environment maps are used, we add an environment to our ThinLensGBufferPass from Tutorial 8. Instead of storing a constant background color to our G-buffer when we hit the background, we will now store a color from our environment map.

为了简化本教程以准确关注环境贴图的使用方式,我们 ThinLensGBufferPass教程 8中添加了一个环境。当我们击中背景时,我们不再将恒定的背景颜色存储到 G 缓冲区,而是存储环境贴图中的颜色。

To do this, in Tutor10-LightProbeEnvironmentMap.cpp, we swap out the ThinLensGBufferPass for our new LightProbeGBufferPass, which handles jittered antialiasing, a thin lens camera model, and rendering an environment mapped background all in a single, more complex RenderPass.

为此,在 Tutor10-LightProbeEnvironmentMap.cpp 中,我们将替换 ThinLensGBufferPass 为新的 LightProbeGBufferPass,它可以处理抖动的抗锯齿、薄镜头相机模型和渲染环境映射背景,所有这些都在一个更复杂的 RenderPass

Setting up a Render Pass For Environment Mapping

Look in LightProbeGBufferPass.h. This header looks almost identical to the ThinLensGBufferPass.h except for the following addition:

进去看看 LightProbeGBufferPass.hThinLensGBufferPass.h 除了以下添加之外, 此头文件看起来几乎相同:

bool usesEnvironmentMap() override { return true; }

The method usesEnvironmentMap() overrides an inherited method from RenderPass and tells the pipeline that our passes plan to use an environment light probe. Our tutorial framework does a couple things with this information. First, it adds a control to the GUI allowing you to dynamically load new environment maps. Second, it tells our ResourceManager to allocate a default environment map texture.

该方法 usesEnvironmentMap() 覆盖 RenderPass 的方法,并告诉管道我们的通道计划使用环境光探测器。我们的教程框架利用这些信息做了一些事情。首先,它向 GUI 添加了一个控件,允许您动态加载新的环境贴图。其次,它告诉我们 ResourceManager 分配一个默认的环境贴图纹理。

Loading an Environment Map

Falcor and our tutorial framework have various built-in texture loading utilites. After adding the usesEnvironmentMap() override above, you can dynamically load new light probes using the GUI. However, to specify the environment map loaded on initialization, we add the following line in LightProbeGBufferPass::initialize():

Falcor 和我们的教程框架有各种内置的纹理加载工具。添加上述usesEnvironmentMap() 覆盖后,您可以使用 GUI 动态加载新的光照探针。但是,要指定初始化时加载的环境映射,我们需要哦添加以下行 LightProbeGBufferPass::initialize()

mpResManager->updateEnvironmentMap( "MonValley_G_DirtRoad_3k.hdr" );

This tells the resource manager to creates an environment map and initialize it with the texture in "MonValley_G_DirtRoad_3k.hdr". Note that if updateEnvironmentMap() is called multiple times (e.g., in different RenderPass::initialize() methods), the last one called wins.

这告诉资源管理器创建一个环境贴图并使用 "MonValley_G_DirtRoad_3k.hdr". 请注意,如果 updateEnvironmentMap() 多次调用(例如,以不同 RenderPass::initialize() 的方法),以最后一次调用为准。

Falcor uses FreeImage, so you can load any image formats it supports, including .hdr, .exr, .dds, .png, and .jpg (among many others).

Falcor 使用 FreeImage,因此您可以加载它支持的任何图像格式,包括 .hdr、.exr、.dds、.png 和 .jpg(以及许多其他格式)。

Sending an Evironment Map to a Miss Shader

Look down in LightProbeGBufferPass::execute() and you will see the following new lines related to environment mapping:

往下看 LightProbeGBufferPass::execute(),你会看到以下与环境映射相关的新行:

// Pass our environment map down to our miss shader
auto missVars = mpRays->getMissVars(0);
missVars["gEnvMap"] = mpResManager->getTexture(ResourceManager::kEnvironmentMap);
missVars["gMatDif"] = mpResManager->getTexture("MaterialDiffuse");

This sends down two variables (gEnvMap and gMatDif) for access in miss shader #0. Note: these variables will only be accessible in miss shader #0. (However, since the code also sets "gMatDif" as a hit shader variable later on, if is also accessible to hit group #0).

这会发送两个变量 (gEnvMapgMatDif) 以供在未命中着色器 #0 中访问。注意:这些变量只能在未命中着色器 #0 中访问。(但是,由于代码稍后设置"gMatDif" 为命中着色器变量,因此命中组#0 也可以访问 if)。

Using an Evironment Map in our Miss Shader

Now that we loaded an environment map and sent it to our miss shader, we can index into it as follows (see lightProbeGBuffer.rt.hlsl):

现在我们加载了一个环境贴图并将其发送到我们的未命中着色器,我们可以按如下方式对其进行索引(请参阅参考资料 lightProbeGBuffer.rt.hlsl):

// The texture containing our environment map
Texture2D<float4> gEnvMap;

void PrimaryMiss() {
float2 texDims;
gEnvMap.GetDimensions( texDims.x, texDims.y );

float2 uv = wsVectorToLatLong( WorldRayDirection() );
gMatDif[ DispatchRaysIndex() ] = gEnvMap[ uint2(uv * texDims) ];

To start, we need to declare out HLSL texture gEnvMap to load our texture. The first two lines of PrimaryMiss() query the DirectX texture object for its size. The third line converts our ray direction into a (u,v) coordinate to lookup a color in light probe. Finally, we grab the color from our environment map and store it into our output buffer in the appropriate location for the current pixel.

首先,我们需要声明 HLSL 纹理 gEnvMap 以加载我们的纹理。前两行 PrimaryMiss() 查询 DirectX 纹理对象的大小。第三行将我们的光线方向转换为 (u,v) 坐标以在光探针中查找颜色。最后,我们从环境贴图中获取颜色并将其存储到输出缓冲区中当前像素的适当位置。

Converting from a vector to a lat-long projection is fairly straightforward, using the following code


float2 wsVectorToLatLong(float3 dir)
float3 p = normalize(dir);
float u = (1.f + atan2(p.x, -p.z) * M_1_PI) * 0.5f;
float v = acos(p.y) * M_1_PI;
return float2(u, v);

Note: The texture lookup code in PrimaryMiss() uses nearest-neighbor indexing into the environment map. Instead of calling gEnvMap[...], other DirectX texture lookups, like gEnvMap.Sample(gTexSampler, uv), can be used if you prefer to use linear texture interpolation. Of course, this requires defining a TextureSampler gTexSampler; and passing it in from your C++ code.

注意:纹理查找代码在 PrimaryMiss() 环境贴图中使用最近邻索引。如果您更喜欢使用线性纹理插值,则可以使用 gEnvMap[...],其他 DirectX 纹理查找,而不是调用 ,例如 gEnvMap.Sample(gTexSampler, uv)。当然,这需要定义一个 TextureSampler gTexSampler; 并将其从您的 C++ 代码中传入。

It is fairly straightforward to use a more complex texture sampler, but this is not in this tutorial:


// Need to do stuff to configure mySampler as desired.
Sampler::SharedPtr mySampler = Sampler::create( ... );

missVars["gTexSampler"] = mySampler;

What Does it Look Like?

That covers the important points of this tutorial. When running, you get the following result, though you need to rotate the camera a bit to see the background:


Hopefully, this tutorial demonstrated how to use load an environment map, send it to your HLSL shader, and index into it in your miss shader.

希望本教程演示了如何使用加载环境贴图,将其发送到您的 HLSL 着色器,并在您的未命中着色器中对其进行索引。

When you are ready, continue on to Tutorial 11, which takes the Lambertian material shading pass from Tutorial 9 and shoots only a simple, random shadow ray for each pixel. This reduces rendering cost in scenes with many lights, but adds some noise in exchange.

准备好后,继续教程 11,它采用教程 9中的 Lambertian 材质着色通道,并且只为每个像素拍摄简单的随机阴影光线。这降低了具有许多灯光的场景中的渲染成本,但作为交换增加了一些噪点。