This repository has been archived by the owner on May 18, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathvkrt_tuto_anyhit.md.html
418 lines (289 loc) · 13.2 KB
/
vkrt_tuto_anyhit.md.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
<meta charset="utf-8" lang="en">
**NVIDIA Vulkan Ray Tracing Tutorial**
**Anyhit Shaders**
<small>Authors: [Martin-Karl Lefrançois](https://devblogs.nvidia.com/author/mlefrancois/), Neil Bickford </small>
![](Images/anyhit.png)
This is an extension of the Vulkan ray tracing [tutorial](vkrt_tutorial.md.html).
Like closest hit shaders, any hit shaders operate on intersections between rays and geometry. However, the any hit
shader will be executed for all hits along the ray. The closest hit shader will then be invoked on the closest accepted
intersection.
The any hit shader can be useful for discarding intersections, such as for alpha cutouts for example, but can also be
used for simple transparency. In this example we will show what is needed to do to add this shader type and to create a
transparency effect.
!!! Note Note
This example is based on many elements from the [Antialiasing Tutorial](vkrt_tuto_jitter_cam.md.html).
(insert setup.md.html here)
# Any Hit
## `raytrace.rahit`
Create a new shader file `raytrace.rahit` and rerun CMake to have it added to the solution.
This shader starts like `raytrace.chit`, but uses less information.
~~~~ C++
#version 460
#extension GL_NV_ray_tracing : require
#extension GL_EXT_nonuniform_qualifier : enable
#extension GL_EXT_scalar_block_layout : enable
#extension GL_GOOGLE_include_directive : enable
#include "random.glsl"
#include "raycommon.glsl"
#include "wavefront.glsl"
// clang-format off
layout(location = 0) rayPayloadInNV hitPayload prd;
layout(binding = 2, set = 1, scalar) buffer ScnDesc { sceneDesc i[]; } scnDesc;
layout(binding = 4, set = 1) buffer MatIndexColorBuffer { int i[]; } matIndex[];
layout(binding = 5, set = 1, scalar) buffer Vertices { Vertex v[]; } vertices[];
layout(binding = 6, set = 1) buffer Indices { uint i[]; } indices[];
layout(binding = 1, set = 1, scalar) buffer MatColorBufferObject { WaveFrontMaterial m[]; } materials[];
// clang-format on
~~~~
!!! Note
You can find the source of `random.glsl` in the Antialiasing Tutorial [here](vkrt_tuto_jitter_cam.md.html#environmentsetup/randomfunctions).
For the any hit shader, we need to know which material we hit, and whether that material supports transparency. If it is
opaque, we simply return, which means that the hit will be accepted.
~~~~ C++
void main()
{
// Object of this instance
uint objId = scnDesc.i[gl_InstanceID].objId;
// Indices of the triangle
uint ind = indices[nonuniformEXT(objId)].i[3 * gl_PrimitiveID + 0];
// Vertex of the triangle
Vertex v0 = vertices[nonuniformEXT(objId)].v[ind.x];
// Material of the object
int matIdx = matIndex[nonuniformEXT(objId)].i[gl_PrimitiveID];
WaveFrontMaterial mat = materials[nonuniformEXT(objId)].m[matIdx];
if (mat.illum != 4)
return;
~~~~
Now we will apply transparency:
~~~~ C++
if (mat.dissolve == 0.0)
ignoreIntersectionNV();
else if(rnd(prd.seed) > mat.dissolve)
ignoreIntersectionNV();
}
~~~~
As you can see, we are using a random number generator to determine if the ray hits or ignores the object. If we
accumulate enough rays, the final result will converge to what we want.
## `raycommon.glsl`
The random `seed` also needs to be passed in the ray payload.
In `raycommon.glsl`, add the seed:
~~~~ C++
struct hitPayload
{
vec3 hitValue;
uint seed;
};
~~~~
## Adding Any Hit to `createRtPipeline`
The any hit shader will be part of the hit shader group. Currently, the hit shader group only contains the closest hit shader.
In `createRtPipeline()`, after loading `raytrace.rchit.spv`, load `raytrace.rahit.spv`
~~~~ C++
vk::ShaderModule ahitSM =
nvvkpp::util::createShaderModule(m_device, //
nvh::loadFile("shaders/raytrace.rahit.spv", true, paths));
~~~~
add the any hit shader to the hit group
~~~~ C++
stages.push_back({{}, vk::ShaderStageFlagBits::eClosestHitNV, chitSM, "main"});
hg.setClosestHitShader(static_cast<uint32_t>(stages.size() - 1));
stages.push_back({{}, vk::ShaderStageFlagBits::eAnyHitNV, ahitSM, "main"});
hg.setAnyHitShader(static_cast<uint32_t>(stages.size() - 1));
m_rtShaderGroups.push_back(hg);
~~~~
and at the end, delete it:
~~~~ C++
m_device.destroy(ahitSM);
~~~~
## Give access of the buffers to the Any Hit shader
In `createDescriptorSetLayout()`, we need to allow the Any Hit shader to access some buffers.
This is the case for the material and scene description buffers
~~~~ C++
// Materials (binding = 1)
m_descSetLayoutBind.emplace_back(
vkDS(1, vkDT::eStorageBuffer, nbObj,
vkSS::eVertex | vkSS::eFragment | vkSS::eClosestHitNV | vkSS::eAnyHitNV));
// Scene description (binding = 2)
m_descSetLayoutBind.emplace_back( //
vkDS(2, vkDT::eStorageBuffer, 1,
vkSS::eVertex | vkSS::eFragment | vkSS::eClosestHitNV | vkSS::eAnyHitNV));
~~~~
and also for the vertex, index and material index buffers:
~~~~ C++
// Materials (binding = 4)
m_descSetLayoutBind.emplace_back( //
vkDS(4, vkDT::eStorageBuffer, nbObj,
vkSS::eFragment | vkSS::eClosestHitNV | vkSS::eAnyHitNV));
// Storing vertices (binding = 5)
m_descSetLayoutBind.emplace_back( //
vkDS(5, vkDT::eStorageBuffer, nbObj, vkSS::eClosestHitNV | vkSS::eAnyHitNV));
// Storing indices (binding = 6)
m_descSetLayoutBind.emplace_back( //
vkDS(6, vkDT::eStorageBuffer, nbObj, vkSS::eClosestHitNV | vkSS::eAnyHitNV));
~~~~
## Opaque Flag
In the example, when creating `VkGeometryNV` objects, we set their flags to `vk::GeometryFlagBitsNV::eOpaque`. However, this avoided invoking the any hit shader.
We could remove all of the flags, but another issue could happen: the any hit shader could be called multiple times for the same triangle. To have the any hit shader process only one hit per triangle, set the `eNoDuplicateAnyHitInvocation` flag:
~~~~ C++
geometry.setFlags(vk::GeometryFlagBitsNV::eNoDuplicateAnyHitInvocation);
~~~~
## `raytrace.rgen`
If you have done the previous [Jitter Camera/Antialiasing](vkrt_tuto_jitter_cam.md.html) tutorial,
you will need just a few changes.
First, `seed` will need to be available in the any hit shader, which is the reason we have added it to the hitPayload structure.
Change the local `seed` to `prd.seed` everywhere.
~~~~ C++
prd.seed = tea(gl_LaunchIDNV.y * gl_LaunchSizeNV.x + gl_LaunchIDNV.x, pushC.frame);
~~~~
For optimization, the `TraceNV` call was using the `gl_RayFlagsOpaqueNV` flag. But
this will skip the any hit shader, so change it to
~~~~ C++
uint rayFlags = gl_RayFlagsNoneNV;
~~~~
## `raytrace.rchit`
Similarly, in the closest hit shader, change the flag to `gl_RayFlagsSkipClosestHitShaderNV`, as we want to enable the any hit and miss shaders, but we still don't care
about the closest hit shader for shadow rays. This will enable transparent shadows.
~~~~ C++
uint flags = gl_RayFlagsSkipClosestHitShaderNV;
~~~~
# Scene and Model
For a more interesting scene, you can replace the `helloVk.loadModel` calls in `main()` with the following scene:
~~~~ C++
helloVk.loadModel(nvh::findFile("media/scenes/wuson.obj", defaultSearchPaths));
helloVk.loadModel(nvh::findFile("media/scenes/sphere.obj", defaultSearchPaths),
nvmath::scale_mat4(nvmath::vec3f(1.5f))
* nvmath::translation_mat4(nvmath::vec3f(0.0f, 1.0f, 0.0f)));
helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths));
~~~~
## OBJ Materials
By default, all objects are opaque, you will need to change the material description.
Edit the first few lines of `media/scenes/wuson.mtl` and `media/scenes/sphere.mtl` to use a new illumination model (4) with a dissolve value of 0.5:
~~~~ C++
newmtl default
illum 4
d 0.5
...
~~~~
# Accumulation
As mentioned earlier, for the effect to work, we need to accumulate frames over time. Please implement the following from [Jitter Camera/Antialiasing](vkrt_tuto_jitter_cam.md.html):
* [Frame Number](vkrt_tuto_jitter_cam.md.html#environmentsetup/framenumber)
* [Storing or Updating](vkrt_tuto_jitter_cam.md.html#environmentsetup/storingorupdating)
* [Application Frame Update](vkrt_tuto_jitter_cam.md.html#applicationframeupdate)
# Fixing Pipeline
The above code works, but might not work in the future. The reason is, the shadow ray `traceNV` call in the Closest Hit shader, uses payload 1
and when intersecting the object, the any hit shader will be executed using payload 0. In the time of writing those lines, the driver add
padding and there are no side effect, but this is not how thing should be done.
Each `traceNV` invocation should have as many Hit Groups as there are trace calls with different payload. For the other examples, it is still fine,
because we are using the `gl_RayFlagsSkipClosestHitShaderNV` flag and the closest hit shader (payload 0) will not be called and there were not
any hit or intersection shaders in the Hit Group. But in this example, the closest hit will be skiped, but not the any hit.
**To fix this**, we need to add another hit group.
This is how the current SBT looks like.
![](Images/anyhit_0.png)
And we need to add the following to the ray tracing pipeline, a copy of the previous Hit Group, with a new AnyHit using the proper payload.
![](Images/anyhit_01.png)
## New shaders
Create two new files `raytrace_0.ahit` and `raytrace_1.ahit`, and rename `raytrace.ahit` to `raytrace_ahit.glsl`
!!! WARNING CMake
Cmake need to be re-run to add the new files to the project.
In `raytrace_0.ahit` add the following code
~~~~ C
#version 460
#extension GL_GOOGLE_include_directive : enable
#define PAYLOAD_0
#include "raytrace_rahit.glsl"
~~~~
and in `raytrace_1.ahit`, replace `PAYLOAD_0` by `PAYLOAD_1`
Then in `raytrace_ahit.glsl` remove the `#version 460` and add the following code, so that we have the right layout.
~~~~ C
#ifdef PAYLOAD_0
layout(location = 0) rayPayloadInNV hitPayload prd;
#elif defined(PAYLOAD_1)
layout(location = 1) rayPayloadInNV shadowPayload prd;
#endif
~~~~
## New Payload
We cannot simply have a bool for our shadow ray payload. We also need the `seed` for the random function.
In the `raycommon.glsl` file, add the following structure
~~~~ C
struct shadowPayload
{
bool isHit;
uint seed;
};
~~~~
The usage of the shadow payload is done in the closest hit and shadow miss shader. First, let's modify `raytraceShadow.rmiss` to look like this
~~~~ C
#version 460
#extension GL_NV_ray_tracing : require
#extension GL_GOOGLE_include_directive : enable
#include "raycommon.glsl"
layout(location = 1) rayPayloadInNV shadowPayload prd;
void main()
{
prd.isHit = false;
}
~~~~
The the change in the closest hit shader `raytrace.rchit`, need to change the usage of the payload, but also the call to `traceNV`
Replace the payload to
~~~~ C
layout(location = 1) rayPayloadNV shadowPayload prdShadow;
~~~~
Then just before the call to `traceNV`, initialize the values to
~~~~ C
prdShadow.isHit = true;
prdShadow.seed = prd.seed;
~~~~
and after the trace, set the seed value back to the main payload
~~~~ C
prd.seed = prdShadow.seed;
~~~~
And check if the trace shadow hit an object of not
~~~~ C
if(prdShadow.isHit)
~~~~
### traceNV
When we call `traceNV`, since we are using the payload 1 (last argument), we also
need the trace to hit the alternative hit group, the one using the payload 1.
To do this, we need to set the sbtRecordOffset to 1
~~~~ C
traceNV(topLevelAS, // acceleration structure
flags, // rayFlags
0xFF, // cullMask
1, // sbtRecordOffset
0, // sbtRecordStride
1, // missIndex
origin, // ray origin
tMin, // ray min range
rayDir, // ray direction
tMax, // ray max range
1 // payload (location = 1)
);
~~~~
## Ray tracing Pipeline
The final step is to add the new Hit Group. This is a change in `HelloVulkan::createRtPipeline()`.
We need to load the new any hit shader and create a new Hit Group.
Replace the `"shaders/raytrace.rahit.spv"` for `"shaders/raytrace_0.rahit.spv"`
Then, after the creating of the first Hit Group, create a new one, where only the any hit using payload 1
is added. We are skipping the closest hit shader in the trace call, so we can ignore it in the Hit Group.
~~~~ C
// Payload 1
vk::ShaderModule ahit1SM =
nvvk::createShaderModule(m_device, //
nvh::loadFile("shaders/raytrace_1.rahit.spv", true, paths));
hg.setClosestHitShader(VK_SHADER_UNUSED_NV); // Not used by shadow (skipped)
stages.push_back({{}, vk::ShaderStageFlagBits::eAnyHitNV, ahit1SM, "main"});
hg.setAnyHitShader(static_cast<uint32_t>(stages.size() - 1));
m_rtShaderGroups.push_back(hg);
~~~~
At the end of the function, delete the shader module `ahit1SM`.
!!! NOTE Re-Run
Everything should work as before, but now it does it right.
# Final Code
You can find the final code in the folder [ray_tracing_anyhit](https://github.com/nvpro-samples/vk_raytracing_tutorial/tree/master/ray_tracing_anyhit)
<!-- Markdeep: -->
<link rel="stylesheet" href="vkrt_tutorial.css?">
<script> window.markdeepOptions = { tocStyle: "medium" };</script>
<script src="markdeep.min.js" charset="utf-8"></script>
<script src="https://developer.nvidia.com/sites/default/files/akamai/gameworks/whitepapers/markdeep.min.js" charset="utf-8"></script>
<script>
window.alreadyProcessedMarkdeep || (document.body.style.visibility = "visible")
</script>