Bildsynthese
Path Tracing
Thorsten Thormählen
28. Mai 2024
Teil 3, Kapitel 2
Thorsten Thormählen
28. Mai 2024
Teil 3, Kapitel 2
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}, \{a, b\} \in \mathcal{B}$ |
| Zahlenbereiche, Koordinatenräume | doppelt gestrichen | $\mathbb{N}, \mathbb{Z}, \mathbb{R}^2, \mathbb{R}^3$ |
$L_o(\mathbf{v}) = L_e(\mathbf{v}) + \int\limits_\Omega \mathrm{f}_r(\mathbf{v}, \mathbf{l})\, \, \underbrace{L_i(\mathbf{l}) \cos(\theta) \, d\omega}_{dE(\mathbf{l})}$
struct RayPayloadType {
vec3 radiance;
vec3 nextRayOrigin;
vec3 nextRayDirection;
vec3 nextFactor;
int level;
}; // type of the "payload" variable
...
void main() { /**** RAY GENERATION SHADER ****/
// compute random pixel offset
vec2 pixelOffset = hammersley(uint(frameID), uint(frameSize));
// compute the texture coordinate for the output image in range [0.0, 1.0]
vec2 texCoord = (vec2(gl_LaunchIDEXT.xy) + pixelOffset) / vec2(gl_LaunchSizeEXT.xy);
// camera parameter
float aspect = float(gl_LaunchSizeEXT.x) / float(gl_LaunchSizeEXT.y);
vec3 rayOrigin = camPos;
vec3 rayDirection = getCameraRayLookAt(30.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.nextFactor = vec3(1.0);
vec3 contribution = vec3(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 && length(contribution) > 0.001) {
payload.level = level;
// 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.radiance;
contribution *= payload.nextFactor;
level++;
}
if(frameID == 0) {
gsnSetPixel(vec4(color, 1.0));
} else {
vec3 previousAverage = gsnGetPreviousPixel().rgb;
previousAverage = pow(previousAverage, vec3(2.2)); // inverse gamma correction
vec3 newAverage = (previousAverage.rgb * float(frameID) + color) / float(frameID + 1);
newAverage = pow(newAverage, vec3(1.0 / 2.2)); // gamma correction
gsnSetPixel(vec4(newAverage, 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);
// assign materials
// Cornell Box
vec3 baseColor = cornellWhite;
vec3 emission = vec3(0.0);
if(gl_InstanceID == 2 && texCoords.x < 0.25) {
baseColor = cornellRed;
}
if(gl_InstanceID == 2 && texCoords.x >= 0.5 && texCoords.x < 0.75) {
baseColor = cornellGreen;
}
// light sphere
if(gl_InstanceID == 1) {
baseColor = vec3(0.0);
emission = vec3(20.0);
}
// different random value for each pixel, each level, and each frame
vec3 random = random_pcg3d(uvec3(gl_LaunchIDEXT.xy, frameID + payload.level));
// uniform sampling of hemisphere
float theta = 0.5 * PI * random.y;
float phi = 2.0 * PI * random.x;
payload.radiance = emission;
// sampled indirect diffuse direction in normal space
vec3 localDiffuseDir = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
vec3 diffuseDir = getNormalSpace(normal) * localDiffuseDir;
payload.nextRayOrigin = position;
payload.nextRayDirection = diffuseDir;
payload.nextFactor = baseColor * PI * cos(theta) * sin(theta);
}
void main() { /**** MISS SHADER ****/
// set color to black
payload.radiance = vec3(0.0, 0.0, 0.0);
// no more reflections
payload.nextRayOrigin = vec3(0.0, 0.0, 0.0);
payload.nextRayDirection = vec3(0.0, 0.0, 0.0);
}
| WDF / Abbildung | Draufsicht | Seitenansicht |
|---|---|---|
| Gleichverteilt in Polarwinkeln
$\mathrm{p}(\theta, \phi) = \frac{1}{2\pi} \frac{1}{\pi/2}$
$\phi = 2 \pi \,u$
$\theta = \frac{\pi}{2}\, v$
(entspricht Riemann Summe)
|
|
|
| Gleichverteilt auf Halbkugel
$\mathrm{p}(\theta, \phi) = \frac{1}{2\pi} \,\sin(\theta)$
$\phi = 2 \pi \,u$
$\theta = \arccos(1 - v)$
|
|
|
| WDF / Abbildung | Draufsicht | Seitenansicht |
|---|---|---|
| Phong BRDF (Diffuser Anteil)
$\mathrm{p}(\theta, \phi) = \frac{1}{\pi} \, \cos(\theta) \,\sin(\theta)$
$\phi = 2 \pi \,u$
$\theta = \arcsin(\sqrt{v})$
|
|
|
| Phong BRDF (Spekularer Anteil)
$\mathrm{p}(\theta, \phi) = \frac{n_s + 1}{2 \pi} \, \cos(\theta)^{n_s} \,\sin(\theta)$
$\phi = 2 \pi \,u$
$\theta = \arccos\left((1-v)^{\frac{1}{n_s+1}}\right)$
|
|
|
| WDF / Abbildung | Draufsicht | Seitenansicht |
|---|---|---|
| Mikrofacetten GGX Verteilung mit $r_p = 0.5$ und $\alpha = r_p^2$
$\mathrm{D}_{\tiny \mbox{GGX}}(\theta) = \frac{\alpha^2}{\pi \left(\cos^2(\theta) (\alpha^2-1)+1\right)^2}$
$\mathrm{p}(\theta, \phi) = \mathrm{D}_{\tiny \mbox{GGX}}(\theta)\cos(\theta)\sin(\theta)$
$\phi = 2 \pi \,u$
$\theta = \arccos\left(\sqrt{\frac{1 - v}{v (\alpha^2-1) + 1} }\right)$
|
|
|
| Mikrofacetten GGX Verteilung mit $r_p = 0.25$ |
|
|
float theta = 0.5 * PI * random.y; float phi = 2.0 * PI * random.x; vec3 localDiffuseDir = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); vec3 diffuseDir = getNormalSpace(normal) * localDiffuseDir; payload.nextRayOrigin = position; payload.nextRayDirection = diffuseDir; payload.nextFactor = baseColor * PI * cos(theta) * sin(theta);
float theta = asin(sqrt(random.y)); float phi = 2.0 * PI * random.x; vec3 localDiffuseDir = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); vec3 diffuseDir = getNormalSpace(normal) * localDiffuseDir; payload.nextRayOrigin = position; payload.nextRayDirection = diffuseDir; payload.nextFactor = baseColor;
Anregungen oder Verbesserungsvorschläge können auch gerne per E-mail an mich gesendet werden: Kontakt