**NVIDIA Vulkan Ray Tracing Tutorial**
**Antialiasing**

# Introduction
This is an extension of the Vulkan ray tracing [tutorial](vkrt_tutorial.md.htm).
In this extension, we will implement antialiasing by jittering the offset of each ray for each pixel over time, instead of always shooting each ray from the middle of its pixel.
(insert setup.md.html here)
## Random Functions
We will use some simple functions for random number generation, which suffice for this example.
Create a new shader file `random.glsl` with the following code. Add it to the `shaders` directory and rerun CMake, and include this new file in `raytrace.rgen`:
~~~~ C++
// Generate a random unsigned int from two unsigned int values, using 16 pairs
// of rounds of the Tiny Encryption Algorithm. See Zafar, Olano, and Curtis,
// "GPU Random Numbers via the Tiny Encryption Algorithm"
uint tea(uint val0, uint val1)
{
uint v0 = val0;
uint v1 = val1;
uint s0 = 0;
for(uint n = 0; n < 16; n++)
{
s0 += 0x9e3779b9;
v0 += ((v1 << 4) + 0xa341316c) ^ (v1 + s0) ^ ((v1 >> 5) + 0xc8013ea4);
v1 += ((v0 << 4) + 0xad90777d) ^ (v0 + s0) ^ ((v0 >> 5) + 0x7e95761e);
}
return v0;
}
// Generate a random unsigned int in [0, 2^24) given the previous RNG state
// using the Numerical Recipes linear congruential generator
uint lcg(inout uint prev)
{
uint LCG_A = 1664525u;
uint LCG_C = 1013904223u;
prev = (LCG_A * prev + LCG_C);
return prev & 0x00FFFFFF;
}
// Generate a random float in [0, 1) given the previous RNG state
float rnd(inout uint prev)
{
return (float(lcg(prev)) / float(0x01000000));
}
~~~~
## Frame Number
Since our jittered samples will be accumulated across frames, we need to know which frame we are currently rendering. A frame number of 0 will indicate a new frame, and we will accumulate the data for larger frame numbers.
Note that the uniform image is read/write, which makes it possible to accumulate previous frames.
In `raytrace.rgen`, add the push constant block from `raytrace.rchit`, adding a new `frame` member:
~~~~ C++
layout(push_constant) uniform Constants
{
vec4 clearColor;
vec3 lightPosition;
float lightIntensity;
int lightType;
int frame;
}
pushC;
~~~~
Also add this frame member to the `RtPushConstant` struct in `hello_vulkan.h`:
~~~~ C++
struct RtPushConstant
{
nvmath::vec4f clearColor;
nvmath::vec3f lightPosition;
float lightIntensity;
int lightType;
int frame{0};
} m_rtPushConstants;
~~~~
## Random and Jitter
In `raytrace.rgen`, at the beginning of `main()`, initialize the random seed:
~~~~ C++
// Initialize the random number
uint seed = tea(gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x, pushC.frame);
~~~~
Then we need two random numbers to vary the X and Y inside the pixel, except for frame 0, where we always shoot
in the center.
~~~~ C++
float r1 = rnd(seed);
float r2 = rnd(seed);
// Subpixel jitter: send the ray through a different position inside the pixel
// each time, to provide antialiasing.
vec2 subpixel_jitter = pushC.frame == 0 ? vec2(0.5f, 0.5f) : vec2(r1, r2);
~~~~
Now we only need to change how we compute the pixel center:
~~~~ C++
const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + subpixel_jitter;
~~~~
## Storing or Updating
At the end of `main()`, if the frame number is equal to 0, we write directly to the image.
Otherwise, we combine the new image with the previous `frame` frames.
~~~~ C++
// Do accumulation over time
if(pushC.frame > 0)
{
float a = 1.0f / float(pushC.frame + 1);
vec3 old_color = imageLoad(image, ivec2(gl_LaunchIDEXT.xy)).xyz;
imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(mix(old_color, prd.hitValue, a), 1.f));
}
else
{
// First frame, replace the value in the buffer
imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(prd.hitValue, 1.f));
}
~~~~
# Application Frame Update
We need to increment the current rendering frame, but we also need to reset it when something in the
scene is changing.
Add two new functions to the `HelloVulkan` class:
~~~~ C++
void resetFrame();
void updateFrame();
~~~~
The implementation of `updateFrame` resets the frame counter if the camera has changed; otherwise, it increments the frame counter.
~~~~ C++
//--------------------------------------------------------------------------------------------------
// If the camera matrix has changed, resets the frame.
// otherwise, increments frame.
//
void HelloVulkan::updateFrame()
{
static nvmath::mat4f refCamMatrix;
auto& m = CameraManip.getMatrix();
if(memcmp(&refCamMatrix.a00, &m.a00, sizeof(nvmath::mat4f)) != 0)
{
resetFrame();
refCamMatrix = m;
}
m_rtPushConstants.frame++;
}
~~~~
Since `resetFrame` will be called before `updateFrame` increments the frame counter, `resetFrame` will set the frame counter to -1:
~~~~ C++
void HelloVulkan::resetFrame()
{
m_rtPushConstants.frame = -1;
}
~~~~
At the begining of `HelloVulkan::raytrace`, call
~~~~ C++
updateFrame();
~~~~
The application will now antialias the image when ray tracing is enabled.
## Resetting Frame on UI Change
The frame number should also be reset when any parts of the scene change, such as the light direction or the background color. In `renderUI()` in `main.cpp`, check for UI changes and reset the frame number when they happen:
~~~~ C++
void renderUI(HelloVulkan& helloVk)
{
static int item = 1;
bool changed = false;
if(ImGui::Combo("Up Vector", &item, "X\0Y\0Z\0\0"))
{
nvmath::vec3f pos, eye, up;
CameraManip.getLookat(pos, eye, up);
up = nvmath::vec3f(item == 0, item == 1, item == 2);
CameraManip.setLookat(pos, eye, up);
changed = true;
}
changed |=
ImGui::SliderFloat3("Light Position", &helloVk.m_pushConstant.lightPosition.x, -20.f, 20.f);
changed |=
ImGui::SliderFloat("Light Intensity", &helloVk.m_pushConstant.lightIntensity, 0.f, 100.f);
changed |= ImGui::RadioButton("Point", &helloVk.m_pushConstant.lightType, 0);
ImGui::SameLine();
changed |= ImGui::RadioButton("Infinite", &helloVk.m_pushConstant.lightType, 1);
if(changed)
helloVk.resetFrame();
}
~~~~
We also need to check for UI changes inside the main loop inside `main()`:
~~~~ C++
bool changed = false;
// Edit 3 floats representing a color
changed |= ImGui::ColorEdit3("Clear color", reinterpret_cast