A Gentle Introduction to DirectX Raytracing 7

引言

本节的主题是抗锯齿,在时间累积时将相机抖动添加到抗锯齿中。

In Tutorial 6, we built a SimpleAccumulationPass that allows averaging multiple frames in sequence to get high-quality, noise-free results. However, this leaves numerous problems, including obvious aliasing along geometric edges.

教程6中,我们构建了一个 SimpleAccumulationPass。它允许按顺序平均多个帧以获得高质量,无噪声的结果。但是,这留下了许多问题,包括沿几何边缘的明显锯齿。

In this tutorial, we add a simple antialiasing scheme by jittering the camera position and using the SimpleAccumulationPass to average the jittered camera locations. While this antialiasing scheme is quite simple, camera jitter is used in many more advanced schemes including today’s state-of-the-art temporal antialiasing (TAA) techniques.

在本教程中,我们通过抖动相机位置并使用 SimpleAccumulationPass 来平均抖动的相机位置,从而添加一个简单的抗锯齿方案。虽然这种抗锯齿方案非常简单,但相机抖动用于许多更高级的方案,包括当今最先进的时间抗锯齿(TAA)技术。

Our Antialised Rendering Pipeline

If you open up Tutor07-SimpleAntialiasing.cpp, you will find our new pipeline combines a new JitteredGBufferPass, the AmbientOcclusionPass from Tutorial 5, and the new SimpleAccumulationPass from Tutorial 6.

如果您打开 Tutor07-SimpleAntialiasing.cpp,您会发现我们的新管道结合了新的 JitteredGBufferPass 教程5中的 AmbientOcclusionPass教程6中的新SimpleAccumulationPass

// Create our rendering pipeline
RenderingPipeline *pipeline = new RenderingPipeline();
pipeline->setPass(0, JitteredGBufferPass::create());
pipeline->setPass(1, AmbientOcclusionPass::create());
pipeline->setPass(2,
SimpleAccumulationPass::create(ResourceManager::kOutputChannel));

This JitteredGBufferPass builds on the SimpleGBufferPass from Tutorial 3 but computes a per-frame camera jitter. You can think of this jitter as moving the center of each pixel slightly every frame, by +/- 0.5 pixel in any direction.

JitteredGBufferPass 基于教程 3中的 SimpleGBufferPass 构建,但计算每帧相机抖动。您可以将这种抖动视为每帧将每个像素的中心稍微移动一点,向任何方向移动+/- 0.5像素。

The compilable tutorial code provides two different ways of selecting this jitter: using a set of discrete samples (the positions used for 8x MSAA) or using a randomly selected offset each frame. Both techniques work identically, the only change is how the offset in [−0.5..+0.5] is computed. The discussion below only walks through the random jitter.

可编译的教程代码提供了两种不同的方法来选择这种抖动:使用一组离散样本(用于8x MSAA的位置)或使用每帧随机选择的偏移量。这两种技术的工作方式相同,唯一的变化是 [−0.5.+0.5] 计算。下面的讨论仅介绍随机抖动。

Setting up Our Jittered Camera Pass

Continue by looking in JitteredGBufferPass.h, which should look similar to SimpleGBufferPass.h from Tutorial 3.

继续查看 JitteredGBufferPass.h,它看起来应该类似于教程 3中的SimpleGBufferPass.h

Key changes are a number of new variables related to random number selection:

主要变化是与随机数选择相关的许多新变量:

bool                                  mUseJitter = true; 
std::uniform_real_distribution<float> mRngDist;
std::mt19937 mRng;

mUseJitter is a user-controllable variable in the GUI that allows toggling camera jitter. mRng and mRngDist are random number generators from the C++ standard library. It turns out the default initialization for mRngDist is exactly what we want. We initialize mRng in JitteredGBufferPass::initialize by seeding it with the current time (which we also get from the C++ standard library).

mUseJitter 是 GUI 中用户可控制的变量,允许切换相机抖动。 mRngmRngDist 是来自 C++ 标准库的随机数生成器。事实证明 mRngDist 的默认初始化正是我们想要的。我们在 JitteredGBufferPass::initialize 中初始化 mRng 方法是用当前时间设定种子(我们也从 C++ 标准库中获得)。

bool JitteredGBufferPass::initialize(RenderContext::SharedPtr pRenderContext, 
ResourceManager::SharedPtr pResManager)
{
...
auto now = std::chrono::high_resolution_clock::now();
auto msTime = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
mRng = std::mt19937( uint32_t(msTime.time_since_epoch().count()) );
...
}

Rendering With a Jittered Camera Pass

Now that we have set up our random number generator, we can add camera jitter in a very straightforward way during our JitteredGBufferPass::execute(), which is a direct copy of the SimpleGBufferPass except the following additional lines:

现在我们已经设置了随机数生成器,我们可以在 JitteredGBufferPass::execute() 期间以非常简单的方式添加相机抖动,这是 SimpleGBufferPass 的直接副本,除了以下附加行:

void JitteredGBufferPass::execute(RenderContext::SharedPtr pRenderContext)
{
...
if (mUseJitter && mpScene && mpScene->getActiveCamera()) {
uint2 screenSz = mpResManger->getScreenSize();
float xJitter = (mRngDist(mRng) - 0.5f) / float(screenSz.x);
float yJitter = (mRngDist(mRng) - 0.5f) / float(screenSz.y);
mpScene->getActiveCamera()->setJitter( xJitter, yJitter );
}
...
}

The if is protection against undefined results, since we can’t jitter the camera if we don’t have a valid scene or camera.

if 是针对未定义结果的保护,因为如果我们没有有效的场景或相机,我们就无法抖动相机。

if (mUseJitter && mpScene && mpScene->getActiveCamera()) {

}

We then get a random number. mRngDist(mRng) returns a random value between 0 and 1. We offset that to get a random value between −0.5 and +0.5.

然后我们得到一个随机数。 mRngDist(mRng) 返回一个介于 0 和 1 之间的随机值。我们偏移它以获得一个介于 −0.5 和 +0.5 之间的随机值。

float xJitter  = (mRngDist(mRng) - 0.5f) / float(screenSz.x);
float yJitter = (mRngDist(mRng) - 0.5f) / float(screenSz.y);

Falcor’s camera system already has a built-in method for camera jittering, but it requires the jitter to be relative to the entire screen, rather than an individual pixel, so we need to divide by the screen resolution prior to calling setJitter().

Falcor的相机系统已经内置了相机抖动的方法,但它要求抖动相对于整个屏幕,而不是单个像素,因此我们需要在调用 setJitter() 之前除以屏幕分辨率。.

Interestingly, adding camera jitter requires no changes to our shader code, so at this point we are done.

有趣的是,添加相机抖动不需要更改着色器代码,因此此时我们已完成。

What Does it Look Like?

That covers the important points of this tutorial. When running, you get the following result:

这涵盖了本教程的要点。运行时,您将获得以下结果:

Hopefully, this tutorial demonstrated how to add randomized jitter to the camera to achieve simple antialiasing when combined with temporal accumulation.

希望本教程演示了如何向相机添加随机抖动,以便在与时间累积相结合时实现简单的抗锯齿。

When you are ready, continue on to Tutorial 8, which uses a thin lens camera model rather than the pinhole camera model usually used in interactive computer graphics. Using a thin lens camera allows us to generate dynamic depth of field.

准备就绪后,请继续学习教程 8,该教程使用薄镜头相机模型,而不是计算机图形学中通常使用的交互式针孔相机模型。使用薄镜头相机可以让我们产生动态景深