A Gentle Introduction to DirectX Raytracing 4

引言

开始使用 DXR。创建相同的 G 缓冲区,这次使用 DirectX 光线追踪。

Our first three tutorials focused on getting up to speed with our simple tutorial infrastructure and making simple DirectX rasterization calls (to create a basic G-Buffer). This tutorial finally moves on to actually spawning some ray tracing with DirectX. To focus on how ray tracing and rasterization are different, this tutorial builds a G-Buffer identical to the one created in Tutorial 3.

我们的前三个教程侧重于快速了解我们的简单教程基础结构并进行简单的 DirectX 栅格化调用(以创建基本的G-Buffer)。本教程最后将转到使用 DirectX 实际生成一些光线追踪。为了重点介绍光线追踪和光栅化的不同之处,本教程构建了一个与教程 3中创建的G缓冲区相同的G缓冲区.

Our Basic Ray Tracing Render Pipeline

If you open up Tutor04-RayTracedGBuffer.cpp, you will find the only change is we swaped out SimpleGBufferPass for the new RayTracedGBufferPass we’ll build below. We reuse the same CopyToOutputPass we created in Tutorial 3.

如果您打开 Tutor04-RayTracedGBuffer.cpp,您会发现唯一的变化是我们将在下面构建的新RayTracedGBufferPass SimpleGBufferPass 。我们重用我们在教程 3中创建的相同CopyToOutputPass

// Create our rendering pipeline
RenderingPipeline *pipeline = new RenderingPipeline();
pipeline->setPass(0, RayTracedGBufferPass::create());
pipeline->setPass(1, CopyToOutputPass::create());

For those who skipped the first three tutorials, this code can be read as “create a rendering pipeline with two components: a pass that generates a ray-traced G-buffer followed by a pass that copies a selectable buffer onscreen.”

对于那些跳过前三个教程的人来说,这段代码可以读作“创建一个包含两个组件的渲染管线:一个生成光线追踪G缓冲区的管道,另一个在屏幕上复制可选缓冲区的管道。”

Creating and Launching DirectX Raytracing Work

Start by looking in RayTracedGBufferPass.h. This should look familiar, as the boilerplate is nearly identical to that from the RenderPasses from prior tutorials. The major difference is in our pass’ member variables:

首先查看 RayTracedGBufferPass.h。这应该看起来很熟悉,因为模板几乎与之前教程中的 RenderPasses 样板相同。主要区别在于我们的 pass 的成员变量:

RtScene::SharedPtr      mpScene;     // Falcor scene abstraction 
RayLaunch::SharedPtr mpRays; // Encapsulates DirectX Raytracing pass

The RtScene class encapsultes Falcor’s scene representation, with additions for ray tracing. In the rest of our tutorials, we will use the RtScene class (which derives from the Scene class from Tutorial 3 but adds functionality required for ray tracing).

RtScene 类封装了 Falcor 的场景表示并添加了用于光线追踪的功能。在其余教程中,我们将使用 RtScene 类(该类派生自教程 3 Scene类,但添加了光线追踪所需的功能)。

The RayLaunch is similar to the RasterLaunch class from Tutorial 3, except it exposes methods for setting up all the new HLSL ray tracing shader types and setting their variables.

RayLaunch 类似于教程 3中的 RasterLaunch 类,只不过它公开了用于设置所有新的 HLSL 光线追踪着色器类型和设置其变量的方法。

Initializing our Ray Traced G-Buffer Pass

Our RayTracedGBufferPass::initialize() looks quite similar to the one for SimpleGBufferPass:

我们的 RayTracedGBufferPass::initialize() 看起来与 SimpleGBufferPass 非常相似:

bool RayTracedGBufferPass::initialize(RenderContext::SharedPtr pRenderContext, 
ResourceManager::SharedPtr pResManager)
{
// Stash a copy of our resource manager; tell it what shared texture
// resources we need for this pass.
mpResManager = pResManager;
mpResManager->requestTextureResources({"WorldPosition", "WorldNormal",
"MaterialDiffuse", "MaterialSpecRough",
"MaterialExtraParams"});

// Create our wrapper for our DirectX Raytracing launch.
mpRays = RayLaunch::create("rtGBuffer.hlsl", "GBufferRayGen");
mpRays->addMissShader("rtGBuffer.hlsl", "PrimaryMiss");
mpRays->addHitShader("rtGBuffer.hlsl", "PrimaryClosestHit", "PrimaryAnyHit");

// Compile our shaders and pass in our scene
mpRays->compileRayProgram();
mpRays->setScene(mpScene);
return true;
}

A couple important notes:

  • The method requestTextureResources is identical to calling requiresTextureResource multiple times, with each of the specified names, simplifying the code somewhat.
  • We don’t request a Z-buffer, because ray tracing does not generate one by default. (Of course, if desired you can output a Z-buffer manually.)

一些重要的注意事项:

  • 方法 requestTextureResources 与多次调用 requiresTextureResource 相同,每个名称都指定,在一定程度上简化了代码。
  • 我们不请求 Z 缓冲区,因为默认情况下光线追踪不会生成 Z 缓冲区。(当然,如果需要,您可以手动输出 Z 缓冲区。

We then create our ray tracing wrapper mpRays by pointing it to our shader file rtGBuffer.hlsl and specifying the function where our ray generation shader starts, in this case the function GBufferRayGen() in our HLSL file. Because we plan to launch rays from this shader, we also have to specify the miss shader to use, named PrimaryMiss(), and our closest-hit and any hit shaders, named PrimaryClosestHit() and PrimaryAnyHit().

然后,我们创建光线追踪包装器 mpRays 方法是将其指向我们的着色器文件 rtGBuffer.hlsl 并指定光线生成着色器开始的函数。在本例中为 HLSL 文件中的函数 GBufferRayGen() 由于我们计划从此着色器启动光线,因此我们还必须指定要使用的 miss 着色器,命名为 PrimaryMiss(),以及我们最接近的着色器和任何命中着色器,命名为PrimaryClosestHit()*和 PrimaryAnyHit()

// Create our wrapper for our DirectX Raytracing launch.
mpRays = RayLaunch::create("rtGBuffer.hlsl", "GBufferRayGen");
mpRays->addMissShader("rtGBuffer.hlsl", "PrimaryMiss");
mpRays->addHitShader("rtGBuffer.hlsl", "PrimaryClosestHit", "PrimaryAnyHit");

When ray tracing, we can have multiple ray types. This means we can call addMissShader multiple times. The first time it is called, we specify miss shader #0. The second time, we specify miss shader #1. The next, miss shader #2. Et cetera. Similarly, when called multiple times, addHitShader specifies hit group #0, 1, 2, etc. This integer identifier is important, as calling TraceRay() in HLSL requires you specify the correct IDs.

在光线追踪时,我们可以有多种光线类型。这意味着我们可以多次调用 addMissShader。第一次调用它时,我们指定 miss shader #0。第二次,我们指定 miss 着色器 #1。接下来,miss 着色器#2 等等……同样,当多次调用时 addHitShader 会指定命中组 #0、1、2 等。此整数标识符很重要,因为在 HLSL 中调用 TraceRay() 需要指定正确的 ID。

Launching Ray Tracing and Sending Data to HLSL

Now that we initialized our rendering resources, we can create our ray traced G-buffer. The RayTracedGBufferPass::execute pass is somewhat complex, so let’s split it into multiple pieces:

现在,我们初始化了渲染资源,可以创建光线追踪的 G 缓冲区了。RayTracedGBufferPass::execute 管线有点复杂,所以让我们把它分成多个部分:

void RayTracedGBufferPass::execute(RenderContext::SharedPtr pRenderContext)
{
// Color used to clear our G-buffer
vec4 black = vec4(0, 0, 0, 0);

// Load our textures; ask the resource manager to clear them first
// Note: 'auto' type used for brevity; actually Texture::SharedPtr
auto wsPos = mpResManager->getClearedTexture("WorldPosition", black);
auto wsNorm = mpResManager->getClearedTexture("WorldNormal", black);
auto matDif = mpResManager->getClearedTexture("MaterialDiffuse", black);
auto matSpec = mpResManager->getClearedTexture("MaterialSpecRough", black);
auto matExtra = mpResManager->getClearedTexture("MaterialExtraParams", black);
auto matEmit = mpResManager->getClearedTexture("Emissive", black);

First, we need access the textures we’ll use to write out our G-buffer. Unlike in Tutorials 2 and 3, we do not need to create a framebuffer object. Instead for ray tracing, we’ll write directly into them (using UAVs). We also ask our resource manager to clear them to black prior to returning the textures.

首先,我们需要访问将用于写出 G 缓冲区的纹理。与教程 2 和教程 3不同,我们不需要创建帧缓冲器对象。相反,对于光线追踪,我们将直接写入它们(使用 UAVs)。我们还要求资源管理器在返回纹理之前将它们清除为黑色。

// Pass our background color down to miss shader #0
auto missVars = mpRays->getMissVars(0);
missVars["MissShaderCB"]["gBgColor"] = mBgColor; // Color for background
missVars["gMatDif"] = matDif; // Where to store bg color

Next, we set the HLSL variables for our miss shader #0. These variables are scoped so they are only usable while executing this specific miss shader. This uses the same syntax we used for raster shaders, except we need to specifically ask (getMissVars(0)) for variables for miss shader #0. In this case, if our rays miss we’ll store the specified background color in the "MaterialDiffuse" texture.

接下来,我们为 miss 着色器 #0 设置 HLSL 变量。这些变量的作用域仅在执行此特定未命中着色器时可用。它使用与光栅化着色器相同的语法,只是我们需要专门询问 (getMissVars(0)) 对于 miss 着色器 #0 的变量。在这种情况下,如果我们的光线丢失,我们将以 "MaterialDiffuse" 纹理存储指定的背景色。

// Pass down variables for our hit group #0
for (auto pVars : mpRays->getHitVars(0))
{
pVars["gWsPos"] = wsPos;
pVars["gWsNorm"] = wsNorm;
pVars["gMatDif"] = matDif;
pVars["gMatSpec"] = matSpec;
pVars["gMatExtra"] = matExtra;
}

In this snippet, we are setting the HLSL variables for hit group #0 (i.e, the closest-hit and any-hit shaders). This code is more complex than for miss shaders, since your variables may change for each geometry instance in your scene. This is the cause of the for loop, as we loop over all the instances. In this case, the variables specifed are scoped so they are only visibile by the closest-hit and any-hit shaders executed while intersecting the specified geometry instance.

在此代码段中,我们将为命中组 #0(即最接近命中和任意命中着色器)设置 HLSL 变量。此代码比未命中着色器的代码更复杂,因为场景中的每个几何实例的变量可能会发生变化。这是for循环的原因,因为我们遍历了所有实例。在这种情况下,指定的变量的作用域为,因此它们只能通过与指定几何实例相交时执行的最接近和任意命中着色器可见。

Above, we are specifying the output textures gWsPos, gWsNorm, gMatDif, gMatSpec, and gMatExtra. We will store our G=Buffer output in our closest-hit shader, which can thought of as similar to a pixel shader.

上面,我们指定了输出纹理 gWsPosgWsNormgMatDifgMatSpecgMatExtra。我们将 G=Buffer 输出存储在最接近的着色器中,这可以被视为类似于像素着色器。

  // Launch our ray tracing
mpRays->execute( pRenderContext, mpResManager->getScreenSize() );
}

Finally, we launch our ray tracing pass. execute() requres the DirectX context and the number of rays to launch (often dependant on screen resolution).

最后,我们启动光线追踪管线。execute() 的参数包含 DirectX context 和要启动的光线数(通常取决于屏幕分辨率)。

The DirectX Raytracing HLSL for Our G-Buffer

Just like when starting to read a C++ program it often makes sense to start reading at main(), you should probably start reading HLSL ray tracing code at the ray generation shader. As specified above during our pass initialization, our shader’s ray generation shader is named GBufferRayGen():

就像开始读取C++程序一样,从 main() 开始读取通常是有意义的,您可能应该在光线生成着色器处开始读取 HLSL 光线追踪代码。如上所述,在管线初始化期间,着色器的光线生成着色器被命名为 GBufferRayGen() :

[shader("raygeneration")]
void GBufferRayGen()
{
// Convert our ray index into a ray direction in world space.
float2 currenPixelLocation = DispatchRaysIndex() + float2(0.5f, 0.5f);
float2 pixelCenter = currenPixelLocation / DispatchRaysDimensions();
float2 ndc = float2(2, -2) * pixelCenter + float2(-1, 1);
float3 rayDir = normalize( ndc.x * gCamera.cameraU +
ndc.y * gCamera.cameraV +
gCamera.cameraW );

// Initialize a ray structure for our ray tracer
RayDesc ray = { gCamera.posW, 0.0f, rayDir, 1e+38f };

// Initialize our ray payload (a per-ray, user-definable structure).
SimplePayload payload = { false };

// Trace our ray
TraceRay(gRtScene, RAY_FLAG_NONE, 0xFF, 0, 1, 0, ray, payload );
}

The first few lines lookup the current pixel location. The number of rays launched (i.e., the parmaeter passed to mpRays->execute) is accessible via HLSL’s DispatchRayDimensions() intrinsic, and the current pixel being processed is given by DispatchRaysIndex(). After converting the pixel location into a value in [−1…1], we use the current camera parameters to generate a world-space vector that describes the ray from the camera through this pixel. The camera variable gCamera is automatically passed down by our framework.

前几行查找当前像素位置。发射的光线数量(即传递给 mpRays->execute)可以通过 HLSL 的DispatchRayDimensions() 来访问,并且当前正在处理的像素由 DispatchRaysIndex() 给出。将像素位置转换为 [−1…1],我们使用当前的相机参数来生成一个世界空间矢量,该矢量描述了来自相机的光线通过此像素。相机变量 gCamera 由我们的框架自动传递。

// Convert our ray index into a ray direction in world space. 
float2 currenPixelLocation = DispatchRaysIndex() + float2(0.5f, 0.5f);
float2 pixelCenter = currenPixelLocation / DispatchRaysDimensions();
float2 ndc = float2(2, -2) * pixelCenter + float2(-1, 1);
float3 rayDir = normalize( ndc.x * gCamera.cameraU +
ndc.y * gCamera.cameraV +
gCamera.cameraW );

We then create a ray using the DirectX data type RayDesc. The first entry in the structure (named Origin) specifies where the ray starts. The second entry (named TMin) specifies the minimum distance we need to travel along our ray before reporting a valid hit. The third entry (named Direction) specifies the ray directions. The fourth entry (named TMax) specifies the maximum distance along our ray to return a valid hit.

然后,我们使用 DirectX 数据类型 RayDesc 创建一个射线。参数中的第一个参数(名为 Origin)指定光线的起始位置。第二个参数(名为 TMin 指定了在报告有效命中之前,我们需要沿着光线行进的最小距离。第三个参数(名为 Direction“)指定光线方向。第四个参数(名为 TMax 指定了返回有效命中时沿光线的最大距离。

// Initialize a ray structure for our ray tracer
RayDesc ray = { gCamera.posW, 0.0f, rayDir, 1e+38f };

// Initialize a ray structure for our ray tracer
// RayDesc ray;
// ray.Origin = gCamera.posW; // Start our ray at the world-space camera position
// ray.Direction = normalize(rayDir); // Our ray direction; normalizing this is often wise
// ray.TMin = 0.0f; // Start at 0.0; for camera, no danger of self-intersection
// ray.TMax = 1e+38f; // Maximum distance to look for a ray hit

When tracing rays in DirectX, there is a user-definable payload structure that accompanies each ray and allows you to stash temporary data during ray tracing. In this example shader this payload is unused, so is not particularly important.

在使用 DirectX 进行跟踪光线时,每条光线都附带一个用户可定义的有效负载结构,并允许您在光线追踪期间存储临时数据。在此示例着色器中此有效负载未使用,因此不是特别重要。

// Initialize our ray payload (a per-ray, user-definable structure).
SimplePayload payload = { false };

Finally, we call TraceRay() to launch our ray with the following parameters:

  • The first parameter, gRtScene, is the ray acceleration structure. Our framework populates this HLSL variable for you automatically.
  • Second, we specify flags to control ray behavior. Here, we use no special flags.
  • Third, we specify an instance mask. This allows you to skip some instances during traversal. A value 0xFF tests all geometry in the scene (skipping none).
  • Fourth, we specify which hit group to use. In the DXR spec, this is a somewhat complex computation. For most simple usage, this corresponds directly to the ID of your hit group, as determined in RayTracedGBufferPass::initialize. (In our case, there is only one hit group so this takes a value of 0).
  • Fifth, we specify how many hit groups there are. In our case this is 1. (Our framework computes this and stores it in the varaible hitProgramCount, if you wish to avoid hard coding this value.)
  • Sixth, we specify which miss shader to use. This directly corresponds to the miss shader ID determined in RayTracedGBufferPass::initialize.
  • Seventh, we specify the ray to trace.
  • Finally, we specify the payload data structure to use while tracing this ray.

最后,我们调用TraceRay()来启动具有以下参数的射线:

  • 第一个参数 gRtScene 是射线加速度结构。我们的框架会自动为您填充此 HLSL 变量
  • 第二个参数,我们指定标志来控制光线行为。在这里我们没有使用特殊标志
  • 第三个参数,我们指定一个实例掩码。这允许您在遍历期间跳过某些实例。值 0xFF 表示测试场景中的所有几何图形(不跳过任何几何图形)
  • 第四个参数,我们指定要使用的命中组。在 DXR 规范中,这是一个有点复杂的计算。对于大多数简单的用法,这直接对应于命中组的 ID,如 RayTracedGBufferPass::initialize 中所确定的那样(在我们的例子中,只有一个命中组,所以这需要一个值为 0)
  • 第五个参数,我们指定有多少个命中组。在我们的例子中是1(我们的框架会计算这一点,如果你想避免硬编码这个值会将其存储在可修改的 hitProgramCount 中)
  • 第六个参数,我们指定要使用的未命中着色器。这直接对应于在 RayTracedGBufferPass::initialize 中确定的 miss 着色器 ID
  • 第七个参数,我们指定要追踪的光线
  • 最后,我们指定在追踪此光线时要使用的有效负载数据结构
// Trace our ray
TraceRay(gRtScene, RAY_FLAG_NONE, 0xFF, 0, 1, 0, ray, payload );


// Trace our ray
TraceRay(gRtScene, // A Falcor built-in containing the raytracing acceleration structure
RAY_FLAG_CULL_BACK_FACING_TRIANGLES, // Ray flags. (Here, we will skip hits with back-facing triangles)
0xFF, // Instance inclusion mask. 0xFF => no instances discarded from this mask
0, // Hit group to index (i.e., when intersecting, call hit shader #0)
hitProgramCount, // Number of hit groups ('hitProgramCount' is built-in from Falcor with the right number)
0, // Miss program index (i.e., when missing, call miss shader #0)
ray, // Data structure describing the ray to trace
rayData); // Our user-defined ray payload structure to store intermediate results

After calling TraceRay(), some processing happens internally. The next shader executed depends on the geometry, ray direction, and internal data structures. Any-hit shaders get launched for some potential hits, closest-hit shaders get launched when you have identified the single closest hit along the ray, and miss shaders get launched if no valid hit occurs.

调用TraceRay()后,一些处理在内部进行。执行的下一个着色器取决于几何体、光线方向和内部数据结构。对于任何命中着色器,都会针对某些潜在的命中启动,当您确定光线沿线的单个最接近命中时,将启动最接近命中的着色器,如果没有发生有效的命中,则会启动未命中着色器

// A dummy payload for this simple ray; never used
struct SimplePayload {
bool dummyValue;
};

// Our miss shader's variables
cbuffer MissShaderCB {
float3 gBgColor;
};

// The output textures. See bindings in C++ code.
// -> gMatDif is visible in the miss shader
// -> All textures are visible in the hit shaders
RWTexture2D<float4> gWsPos, gWsNorm, gMatDif, gMatSpec, gMatExtra;

[shader("miss")]
void PrimaryMiss(inout SimplePayload)
{
gMatDif[DispatchRaysIndex()] = float4( gBgColor, 1.0f );
}

This miss shader is fairly straightforward. When a ray misses all geometry, write out the background color into the specified texture. Where in the texture do we write? That depends on the current pixel location we are working on (using the same DispatchRaysIndex() intrinsic we used in our ray generation shader).

这个未命中着色器相当简单。当光线错过所有几何图形时,将背景色写出到指定的纹理中。我们在纹理的哪个位置写?这取决于我们正在处理的当前像素位置(使用我们在光线生成着色器中使用的相同 DispatchRaysIndex() 固有功能)。

// What code is executed when our ray hits a potentially transparent surface?
[shader("anyhit")]
void PrimaryAnyHit(inout SimpleRayPayload, BuiltInTriangleIntersectionAttributes attribs)
{
// Is this a transparent part of the surface? If so, ignore this hit
if (alphaTestFails(attribs))
IgnoreHit();
}

Our any hit shader is also straightforward. In most cases, when ray traversal detects a hit point, it is a valid intersection. However, if the geometry uses alpha-testing and has a textured alpha mask, we need to query it before confirming the intersection. Here we call a utility function alphaTestFails() (see tutorial code for specifics) that loads the alpha texture from Falcor’s internal scene structure and determines if we hit a transparent texel. If so, this hit is ignored via the IgnoreHit() intrinsic, otherwise the hit is accepted.

我们的任何命中着色器也很简单。在大多数情况下,当光线遍历检测到命中点时,它是一个有效的交集。但是,如果几何体使用 alpha 测试并具有纹理 alpha 蒙版,则需要在确认交集之前对其进行查询。在这里,我们调用一个实用程序函数 alphaTestFails() 有关详细信息,请参阅教程代码),该函数从 Falcor 的内部场景结构加载 alpha 纹理并确定我们是否命中透明纹理。如果是这样,则通过 IgnoreHit() 内部函数忽略此命中,否则将接受此命中。

(译者注:这里附上该函数的完整定义,位于同一个文件夹下的 alphaTest.hlsli。)

/**********************************************************************************************************************
# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
# following conditions are met:
# * Redistributions of code must retain the copyright notice, this list of conditions and the following disclaimer.
# * Neither the name of NVIDIA CORPORATION nor the names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**********************************************************************************************************************/

// This function tests if the alpha test fails, given the attributes of the current hit.
// -> Can legally be called in a DXR any-hit shader or a DXR closest-hit shader, and
// accesses Falcor helpers and data structures to extract and perform the alpha test.
bool alphaTestFails(BuiltInTriangleIntersectionAttributes attribs)
{
// Run a Falcor helper to extract the current hit point's geometric data
VertexOut vsOut = getVertexAttributes(PrimitiveIndex(), attribs);

// Extracts the diffuse color from the material (the alpha component is opacity)
ExplicitLodTextureSampler lodSampler = { 0 }; // Specify the tex lod/mip to use here
float4 baseColor = sampleTexture(gMaterial.resources.baseColor, gMaterial.resources.samplerState,
vsOut.texC, gMaterial.baseColor, EXTRACT_DIFFUSE_TYPE(gMaterial.flags), lodSampler);

// Test if this hit point fails a standard alpha test.
return (baseColor.a < gMaterial.alphaThreshold);
}

// This function combines two Falcor-defined utility routines into one. (That does not
// require the user to define an additional opaque data type 'VertexOut', which
// is largely irrelevant since ShadingData contains all the important data from
// VertexOut)
ShadingData getShadingData(uint primId, BuiltInTriangleIntersectionAttributes barys)
{
VertexOut vsOut = getVertexAttributes(primId, barys);
return prepareShadingData(vsOut, gMaterial, gCamera.posW, 0);
}
[shader("closesthit")]
void PrimaryClosestHit(inout SimpleRayPayload, BuiltinIntersectionAttribs attribs)
{
// Which pixel spawned our ray?
uint2 idx = DispatchRaysIndex();

// Run helper function to compute important data at the current hit point
ShadingData shadeData = getShadingData( PrimitiveIndex(), attribs );

// Save out our G-buffer values to the specified output textures
gWsPos[idx] = float4(shadeData.posW, 1.f);
gWsNorm[idx] = float4(shadeData.N, length(shadeData.posW - gCamera.posW));
gMatDif[idx] = float4(shadeData.diffuse, shadeData.opacity);
gMatSpec[idx] = float4(shadeData.specular, shadeData.linearRoughness);
gMatExtra[idx] = float4(shadeData.IoR,
shadeData.doubleSidedMaterial ? 1.f : 0.f,
0.f, 0.f);
}

Our closest hit shader looks remarkably like ou pixel shader in Tutorial 3. We first find which pixel onscreen this ray belongs to (using the DispatchRaysIndex() intrinsic), then we get our ShadingData using Falcor utility functions, finally we output our G-buffer data to the corresponding output buffers.

我们最接近的着色器看起来非常像教程3中的ou像素着色器。我们首先找到此光线在屏幕上属于哪个像素(使用 DispatchRaysIndex() 内部函数),然后使用 Falcor 实用程序函数获取ShadingData,最后将 G 缓冲区数据输出到相应的输出缓冲区。

As in Tutorial 3, this G-buffer is extremely verbose, and you could likely compress the same data into many fewer bits. This format is used for clarity.

教程3 一样,这个G缓冲区非常冗长,您可能会将相同的数据压缩更少。为清楚起见,我们还是采用此格式。

What Does it Look Like?

That covers the important points of this tutorial. When running, you get basically the same result as in Tutorial 3:

这涵盖了本教程的要点。运行时,您将获得与教程 3 中基本相同的结果:

Hopefully, this tutorial demonstrated:

  • How to launch DirectX Raytracing work.
  • Write basic ray tracing HLSL shaders.
  • Use intrinsic structures and functions like RayDesc, DispatchRaysIndex and TraceRay.

希望本教程能够演示:

  • 如何启动 DirectX 光线追踪工作
  • 编写基本的光线追踪 HLSL 着色器
  • 使用内部结构和函数,如 RayDescTraceRayDispatchRaysIndex

When you are ready, continue on to Tutorial 5, where we build on our existing G-buffers to render ray traced ambient occlusion.

准备就绪后,请继续学习 教程 5,我们在现有 G 缓冲区的基础上进行构建,以渲染光线追踪环境光遮蔽。

(译注:下面是一些补充展示:)