Bildsynthese
Vulkan Ray Tracing Pipeline
Thorsten Thormählen
12. April 2022
Teil 2, Kapitel 1
Thorsten Thormählen
12. April 2022
Teil 2, Kapitel 1
Dies ist die Druck-Ansicht.
Weiterschalten der Folien durch die → Taste oder
durch das Klicken auf den rechten Folienrand.
Das Weiterschalten der Folien kann ebenfalls durch das Klicken auf den rechten bzw. linken Folienrand erfolgen.
Typ | Schriftart | Beispiele |
---|---|---|
Variablen (Skalare) | kursiv | $a, b, x, y$ |
Funktionen | aufrecht | $\mathrm{f}, \mathrm{g}(x), \mathrm{max}(x)$ |
Vektoren | fett, Elemente zeilenweise | $\mathbf{a}, \mathbf{b}= \begin{pmatrix}x\\y\end{pmatrix} = (x, y)^\top,$ $\mathbf{B}=(x, y, z)^\top$ |
Matrizen | Schreibmaschine | $\mathtt{A}, \mathtt{B}= \begin{bmatrix}a & b\\c & d\end{bmatrix}$ |
Mengen | kalligrafisch | $\mathcal{A}, B=\{a, b\}, b \in \mathcal{B}$ |
Zahlenbereiche, Koordinatenräume | doppelt gestrichen | $\mathbb{N}, \mathbb{Z}, \mathbb{R}^2, \mathbb{R}^3$ |
Die Raytracing-Pipeline besteht aus 5 Shadern:
traceRayEXT(...)
zum "Acceleration Structure Traversal"-Block
traceRayEXT
Funktion ist die payload
Variable, die
die gesammelten Informationen des Strahls enthältpayload
Variable hat einen benutzerdefinierten Typ und kann während der Strahlverfolgung durch die
unterschiedlichen Shader verändert werdentraceRayEXT
zum aufrufenden Shader zurück und die payload
Variable kann vom Ray Generation-Shader
ausgewertet werden, um ein Ausgabebild zu erstellenreportIntersectionEXT(...)
hitAttributeEXT
Variable schreiben (die vom benutzerdefinierten Typ sein kann)hitAttributeEXT vec2 baryCoord
" VariableignoreIntersectionEXT
Befehl ignoriertterminateRayEXT
abzubrechenignoreIntersectionEXT
nicht
aufgerufen wird, dann wird der Treffer an die Strahlverfolgung übermitteltpayload
Variable manipulierentraceRayEXT
Funktion aufrufen, was einen weiteren Strahl in den
Strahlverfolgungsblock sendet und eventuell eine Rekursion erzeugen kannRay generation | Closest-hit | Miss | Intersection | Any-hit | |
uvec3 gl_LaunchIDEXT |
✓ | ✓ | ✓ | ✓ | ✓ |
uvec3 gl_LaunchSizeEXT |
✓ | ✓ | ✓ | ✓ | ✓ |
int gl_PrimitiveID |
✓ | ✓ | ✓ | ||
int gl_InstanceID |
✓ | ✓ | ✓ | ||
int gl_InstanceCustomIndexEXT |
✓ | ✓ | ✓ | ||
int gl_GeometryIndexEXT |
✓ | ✓ | ✓ | ||
vec3 gl_WorldRayOriginEXT |
✓ | ✓ | ✓ | ✓ | |
vec3 gl_WorldRayDirectionEXT |
✓ | ✓ | ✓ | ✓ | |
vec3 gl_ObjectRayOriginEXT |
✓ | ✓ | ✓ | ||
vec3 gl_ObjectRayDirectionEXT |
✓ | ✓ | ✓ | ||
float gl_RayTminEXT |
✓ | ✓ | ✓ | ✓ | |
float gl_RayTmaxEXT |
✓ | ✓ | ✓ | ✓ | |
uint gl_IncomingRayFlagsEXT |
✓ | ✓ | ✓ | ✓ | |
float gl_HitTEXT |
✓ | ✓ | |||
uint gl_HitKindEXT |
✓ | ✓ | |||
mat4x3 gl_ObjectToWorldEXT |
✓ | ✓ | ✓ | ||
mat4x3 gl_WorldToObjectEXT |
✓ | ✓ | ✓ |
const uint gl_RayFlagsNoneEXT = 0u; |
const uint gl_RayFlagsNoOpaqueEXT = 2u; |
const uint gl_RayFlagsTerminateOnFirstHitEXT = 4u; |
const uint gl_RayFlagsSkipClosestHitShaderEXT = 8u; |
const uint gl_RayFlagsCullBackFacingTrianglesEXT = 16u; |
const uint gl_RayFlagsCullFrontFacingTrianglesEXT = 32u; |
const uint gl_RayFlagsCullOpaqueEXT = 64u; |
const uint gl_RayFlagsCullNoOpaqueEXT = 128u; |
const uint gl_HitKindFrontFacingTriangleEXT = 0xFEu; |
const uint gl_HitKindBackFacingTriangleEXT = 0xFFu; |
gl_ObjectToWorldEXT
gl_WorldToObjectEXT
gl_InstanceID = 0
gl_InstanceID = 1
gl_InstanceID = 2
gl_InstanceID
gl_PrimitiveID
gl_ObjectToWorldEXT
und
gl_WorldToObjectEXT
Variable zugegriffen werden$\frac{f}{1} = \frac{\cos( 0.5 \, \Theta)}{\sin( 0.5 \, \Theta)} \Leftrightarrow f = \mathrm{cotan}( 0.5 \, \Theta)$
// Returns a camera ray for a camera at the origin that is // looking in negative z-direction. "fieldOfViewY" must be given in degrees. // "point" must be in range [0.0, 1.0] to cover the complete image plane. // vec3 getCameraRay(float fieldOfViewY, float aspectRatio, vec2 point) { // compute focal length from given field-of-view float focalLength = 1.0 / tan(0.5 * fieldOfViewY * 3.14159265359 / 180.0); // compute position in the camera's image plane in range [-1.0, 1.0] vec2 pos = 2.0 * (point - 0.5); return normalize(vec3(pos.x * aspectRatio, pos.y, -focalLength)); } void main() { /**** RAY GENERATION SHADER ****/ // compute the texture coordinate for the output image in range [0.0, 1.0] vec2 texCoord = (vec2(gl_LaunchIDEXT.xy) + 0.5) / vec2(gl_LaunchSizeEXT.xy); // camera's aspect ratio float aspect = float(gl_LaunchSizeEXT.x) / float(gl_LaunchSizeEXT.y); vec3 rayOrigin = vec3(0.0, 0.0, 0.0); vec3 rayDirection = getCameraRay(45.0, aspect, texCoord); ...
traceRayEXT(...)
im Ray Generation Shader in den Strahlverfolgungsblock geschicktpayload.color
Variable wird auf rot gesetzt.
Anderenfalls, wenn kein Treffer stattfindet, setzt der Miss Shader payload.color
auf schwarz.traceRayEXT
zum aufrufenden Ray Generation Shader zurück und die geänderte payload.color
Variable
wird dort in das Ausgabebild geschriebengl_InstanceID
,
gl_ObjectToWorldEXT
und gl_PrimitiveID
automatisch entsprechend gesetzt
und erlauben so die BLAS-Instanz, seine Transformation und das Primitiv (d.h. in diesem Fall das Dreieck) des Treffers zu identifizieren
gl_InstanceID
und
gl_PrimitiveID
als Eingabeparameter haben und die lokalen Vertex Positionen, lokalen Normalen und Texturkoordinaten der drei
Stützpunkte des getroffenen Dreiecks liefernvec2 baryCoord
Variable bereitgestellttraceRayEXT
Funktion aufrufen, um einen Strahl in den Strahlverfolgungsblock zu schickentraceRayEXT
auf
und schickt einen Schattenstrahl in Richtung der Lichtquelle.
gl_RayFlagsSkipClosestHitShaderEXT
gesetztgl_RayFlagsTerminateOnFirstHitEXT
gesetzt wirdpayload.shadowRayMiss
Variable von false
auf true
traceRayEXT
Funktion zum emittierenden Closest-Hit Shader
zurückkehrt, kann diese Variable überprüft werden, um festzustellen, ob der Oberflächenpunkt im Schatten liegt oder nichtstruct RayPayloadType { vec3 color; bool shadowRayMiss; }; // type of the "payload" variable ... void main() { /**** CLOSEST-HIT SHADER ****/ // get mesh vertex data in object space vec3 p0, p1, p2; gsnGetPositions(gl_InstanceID, gl_PrimitiveID, p0, p1, p2); vec3 n0, n1, n2; gsnGetNormals(gl_InstanceID, gl_PrimitiveID, n0, n1, n2); vec2 t0, t1, t2; gsnGetTexCoords(gl_InstanceID, gl_PrimitiveID, t0, t1, t2); // interpolate with barycentric coordinate vec3 barys = vec3(1.0f - baryCoord.x - baryCoord.y, baryCoord.x, baryCoord.y); vec3 localNormal = normalize(n0 * barys.x + n1 * barys.y + n2 * barys.z); vec3 localPosition = p0 * barys.x + p1 * barys.y + p2 * barys.z; vec2 texCoords = t0 * barys.x + t1 * barys.y + t2 * barys.z; // transform to world space mat3 normalMat; gsnGetNormal3x3Matrix(gl_InstanceID, normalMat); vec3 normal = normalize(normalMat * localNormal); vec3 position = gl_ObjectToWorldEXT * vec4(localPosition, 1.0); // dynamic light location float t = float(frameID % 45)/float(45); vec3 lightPos = vec3(5.0 * sin(2.0*PI*t), 5.0 * cos(2.0*PI*t), 5.0); vec3 lightDir = normalize(lightPos - position); // prepare shadow ray uint rayFlags = gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsSkipClosestHitShaderEXT; float rayMin = 0.001; float rayMax = length(lightPos - position); float shadowBias = 0.001; uint cullMask = 0xFFu; float frontFacing = dot(-gl_WorldRayDirectionEXT, normal); vec3 shadowRayOrigin = position + sign(frontFacing) * shadowBias * normal; vec3 shadowRayDirection = lightDir; payload.shadowRayMiss = false; // shot shadow ray traceRayEXT(topLevelAS, rayFlags, cullMask, 0u, 0u, 0u, shadowRayOrigin, rayMin, shadowRayDirection, rayMax, 0); // diffuse shading vec3 radiance = ambientColor; // ambient term if(payload.shadowRayMiss) { // if not in shadow float irradiance = max(dot(lightDir, normal), 0.0); if(irradiance > 0.0) { // if receives light radiance += baseColor * irradiance; // diffuse shading } } payload.color = vec3(radiance); } void main() { /**** MISS SHADER ****/ // set color to black payload.color = vec3(0.0, 0.0, 0.0); // shadow ray has not hit an object payload.shadowRayMiss = true; }
reflect
zur Verfügung
vec3 r_out = reflect(r_in, normal);
trace(level, ray, &color) { // THIS IS PSEUDOCODE!!! if (intersect(ray, &hit)) { shadow = testShadow(hit); directColor = getDirectLight(hit, shadow); if (reflectionFactor > 0.0 && level < maxLevel) { reflectedRay = reflect(ray, hit.normal); trace(level + 1, reflectedRay, &reflectionColor); // recursion } color = color + directColor + reflectionFactor * reflectionColor; } else { color = backgroundColor; } }
trace(ray, &color) { // THIS IS PSEUDOCODE!!!
nextRay = ray;
contribution = 1.0; level = 0;
while (nextRay && level < maxLevel) {
if (intersect(nextRay, &hit)) {
shadow = testShadow(hit);
directColor = getDirectLight(hit, shadow);
if (reflectionFactor > 0.0) {
reflectedRay = reflect(nextRay, hit.normal);
nextRay = reflectedRay;
} else {
nextRay = false;
}
} else {
directColor = backgroundColor;
nextRay = false;
}
color = color + contribution * directColor;
contribution = contribution * reflectionFactor;
level = level + 1;
}
}
struct RayPayloadType { vec3 directLight; vec3 nextRayOrigin; vec3 nextRayDirection; float nextReflectionFactor; bool shadowRayMiss; }; // type of the "payload" variable ... void main() { /**** RAY GENERATION SHADER ****/ // compute the texture coordinate for the output image in range [0.0, 1.0] vec2 texCoord = (vec2(gl_LaunchIDEXT.xy) + 0.5) / vec2(gl_LaunchSizeEXT.xy); // camera parameter float aspect = float(gl_LaunchSizeEXT.x) / float(gl_LaunchSizeEXT.y); vec3 rayOrigin = camPos; vec3 rayDirection = getCameraRayLookAt(20.0, aspect, camPos, camLookAt, camUp, texCoord); uint rayFlags = gl_RayFlagsNoneEXT; // no ray flags float rayMin = 0.001; // minimum ray distance for a hit float rayMax = 10000.0; // maximum ray distance for a hit uint cullMask = 0xFFu; // no culling // init ray and payload payload.nextRayOrigin = rayOrigin; payload.nextRayDirection = rayDirection; payload.nextReflectionFactor = 1.0; float contribution = 1.0; vec3 color = vec3(0.0, 0.0, 0.0); int level = 0; const int maxLevel = 5; // shot rays while(length(payload.nextRayDirection) > 0.1 && level < maxLevel && contribution > 0.001) { // Submitting the camera ray to the acceleration structure traversal. // The last parameter is the index of the "payload" variable (always 0) traceRayEXT(topLevelAS, rayFlags, cullMask, 0u, 0u, 0u, payload.nextRayOrigin, rayMin, payload.nextRayDirection, rayMax, 0); color += contribution * payload.directLight; contribution *= payload.nextReflectionFactor; level++; } gsnSetPixel(vec4(color, 1.0)); } void main() { /**** CLOSEST-HIT SHADER ****/ // get mesh vertex data in object space vec3 p0, p1, p2; gsnGetPositions(gl_InstanceID, gl_PrimitiveID, p0, p1, p2); vec3 n0, n1, n2; gsnGetNormals(gl_InstanceID, gl_PrimitiveID, n0, n1, n2); vec2 t0, t1, t2; gsnGetTexCoords(gl_InstanceID, gl_PrimitiveID, t0, t1, t2); // interpolate with barycentric coordinate vec3 barys = vec3(1.0f - baryCoord.x - baryCoord.y, baryCoord.x, baryCoord.y); vec3 localNormal = normalize(n0 * barys.x + n1 * barys.y + n2 * barys.z); vec3 localPosition = p0 * barys.x + p1 * barys.y + p2 * barys.z; vec2 texCoords = t0 * barys.x + t1 * barys.y + t2 * barys.z; // transform to world space mat3 normalMat; gsnGetNormal3x3Matrix(gl_InstanceID, normalMat); vec3 normal = normalize(normalMat * localNormal); vec3 position = gl_ObjectToWorldEXT * vec4(localPosition, 1.0); vec3 lightDir = normalize(lightPos - position); // prepare shadow ray uint rayFlags = gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsSkipClosestHitShaderEXT; float rayMin = 0.001; float rayMax = length(lightPos - position); float shadowBias = 0.001; uint cullMask = 0xFFu; float frontFacing = dot(-gl_WorldRayDirectionEXT, normal); vec3 shadowRayOrigin = position + sign(frontFacing) * shadowBias * normal; vec3 shadowRayDirection = lightDir; payload.shadowRayMiss = false; // shot shadow ray traceRayEXT(topLevelAS, rayFlags, cullMask, 0u, 0u, 0u, shadowRayOrigin, rayMin, shadowRayDirection, rayMax, 0); // diffuse shading (direct light) vec3 radiance = ambientColor; // ambient term if(payload.shadowRayMiss) { // if not in shadow float irradiance = max(dot(lightDir, normal), 0.0); if(irradiance > 0.0) { // if receives light radiance += baseColor * irradiance; // diffuse shading } } payload.directLight = radiance; // compute reflected ray (prepare next traceRay) float reflectionFactor = 0.25; if(reflectionFactor > 0.0) { payload.nextRayOrigin = position; payload.nextRayDirection = reflect(gl_WorldRayDirectionEXT, normal); payload.nextReflectionFactor = reflectionFactor; } else { // no more reflections payload.nextRayOrigin = vec3(0.0, 0.0, 0.0); payload.nextRayDirection = vec3(0.0, 0.0, 0.0); } } void main() { /**** MISS SHADER ****/ // set color to black payload.directLight = vec3(0.0, 0.0, 0.0); // shadow ray has not hit an object payload.shadowRayMiss = true; // no more reflections payload.nextRayOrigin = vec3(0.0, 0.0, 0.0); payload.nextRayDirection = vec3(0.0, 0.0, 0.0); }
vec4 previousAverage = gsnGetPreviousPixel(); vec3 newAverage = (previousAverage.rgb * float(frameID) + payload.color) / float(frameID + 1); gsnSetPixel(vec4(newAverage, 1.0));
Index $n$ | Zahlwert (Basis 2) | Gespiegelt | $h_2(n)$ |
---|---|---|---|
1 | 1 | 0.1 = 1/2 | 0.5 |
2 | 10 | 0.01 = 1/4 | 0.25 |
3 | 11 | 0.11 = 3/4 | 0.75 |
4 | 100 | 0.001 = 1/8 | 0.125 |
5 | 101 | 0.101 = 1/2 + 1/8 | 0.625 |
6 | 110 | 0.011 = 1/4 + 1/8 | 0.375 |
7 | 111 | 0.111 = 1/2 + 1/4 + 1/8 | 0.875 |
Index $n$ | Zahlwert (Basis 3) | Gespiegelt | $h_3(n)$ |
---|---|---|---|
1 | 1 | 0.1 = 1/3 | 0.333 |
2 | 2 | 0.2 = 2/3 | 0.666 |
3 | 10 | 0.01 = 1/9 | 0.111 |
4 | 11 | 0.11 = 1/3 + 1/9 | 0.444 |
5 | 12 | 0.21 = 2/3 + 1/9 | 0.777 |
6 | 20 | 0.02 = 2/9 | 0.222 |
7 | 21 | 0.12 = 1/3 + 2/9 | 0.555 |
8 | 22 | 0.22 = 2/3 + 2/9 | 0.888 |
Anregungen oder Verbesserungsvorschläge können auch gerne per E-Mail an mich gesendet werden: Kontakt