Add ray traced glTF sample (#1083)
* Started working on a ray tracing glTF sample * Started working on a ray tracing glTF sample Added textures using descriptor indexing * Frame accumulation Pass glTF node transforms to BLAS build * Shader cleanup * Code cleanup, flip Y using TLAS transform matrix * Create AS for all primitives in the gltf scene * Remove unused variables * Added missing shaders * Minor cleanup
This commit is contained in:
parent
e006185ca0
commit
5962189427
18 changed files with 1109 additions and 2 deletions
49
shaders/glsl/raytracinggltf/anyhit.rahit
Normal file
49
shaders/glsl/raytracinggltf/anyhit.rahit
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* Copyright (c) 2023, Sascha Willems
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
#version 460
|
||||
|
||||
#extension GL_EXT_ray_tracing : require
|
||||
#extension GL_GOOGLE_include_directive : require
|
||||
#extension GL_EXT_nonuniform_qualifier : require
|
||||
#extension GL_EXT_buffer_reference2 : require
|
||||
#extension GL_EXT_scalar_block_layout : require
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
|
||||
|
||||
layout(location = 0) rayPayloadInEXT vec3 hitValue;
|
||||
layout(location = 3) rayPayloadInEXT uint payloadSeed;
|
||||
|
||||
hitAttributeEXT vec2 attribs;
|
||||
|
||||
layout(binding = 3, set = 0) uniform sampler2D image;
|
||||
|
||||
struct GeometryNode {
|
||||
uint64_t vertexBufferDeviceAddress;
|
||||
uint64_t indexBufferDeviceAddress;
|
||||
int textureIndexBaseColor;
|
||||
int textureIndexOcclusion;
|
||||
};
|
||||
layout(binding = 4, set = 0) buffer GeometryNodes { GeometryNode nodes[]; } geometryNodes;
|
||||
|
||||
layout(binding = 5, set = 0) uniform sampler2D textures[];
|
||||
|
||||
#include "bufferreferences.glsl"
|
||||
#include "geometrytypes.glsl"
|
||||
#include "random.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
Triangle tri = unpackTriangle(gl_PrimitiveID, 112);
|
||||
GeometryNode geometryNode = geometryNodes.nodes[gl_GeometryIndexEXT];
|
||||
vec4 color = texture(textures[nonuniformEXT(geometryNode.textureIndexBaseColor)], tri.uv);
|
||||
// If the alpha value of the texture at the current UV coordinates is below a given threshold, we'll ignore this intersection
|
||||
// That way ray traversal will be stopped and the miss shader will be invoked
|
||||
// if (color.a < 0.9) {
|
||||
//if (((gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x) % 4) == 0) {
|
||||
if(rnd(payloadSeed) > color.a) {
|
||||
ignoreIntersectionEXT;
|
||||
}
|
||||
// }
|
||||
}
|
||||
BIN
shaders/glsl/raytracinggltf/anyhit.rahit.spv
Normal file
BIN
shaders/glsl/raytracinggltf/anyhit.rahit.spv
Normal file
Binary file not shown.
15
shaders/glsl/raytracinggltf/bufferreferences.glsl
Normal file
15
shaders/glsl/raytracinggltf/bufferreferences.glsl
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/* Copyright (c) 2023, Sascha Willems
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
|
||||
layout(push_constant) uniform BufferReferences {
|
||||
uint64_t vertices;
|
||||
uint64_t indices;
|
||||
uint64_t bufferAddress;
|
||||
} bufferReferences;
|
||||
|
||||
layout(buffer_reference, scalar) buffer Vertices {vec4 v[]; };
|
||||
layout(buffer_reference, scalar) buffer Indices {uint i[]; };
|
||||
layout(buffer_reference, scalar) buffer Data {vec4 f[]; };
|
||||
63
shaders/glsl/raytracinggltf/closesthit.rchit
Normal file
63
shaders/glsl/raytracinggltf/closesthit.rchit
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* Copyright (c) 2023, Sascha Willems
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#version 460
|
||||
|
||||
#extension GL_EXT_ray_tracing : require
|
||||
#extension GL_GOOGLE_include_directive : require
|
||||
#extension GL_EXT_nonuniform_qualifier : require
|
||||
#extension GL_EXT_buffer_reference2 : require
|
||||
#extension GL_EXT_scalar_block_layout : require
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
|
||||
|
||||
layout(location = 0) rayPayloadInEXT vec3 hitValue;
|
||||
layout(location = 2) rayPayloadEXT bool shadowed;
|
||||
hitAttributeEXT vec2 attribs;
|
||||
|
||||
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;
|
||||
layout(binding = 3, set = 0) uniform sampler2D image;
|
||||
|
||||
struct GeometryNode {
|
||||
uint64_t vertexBufferDeviceAddress;
|
||||
uint64_t indexBufferDeviceAddress;
|
||||
int textureIndexBaseColor;
|
||||
int textureIndexOcclusion;
|
||||
};
|
||||
layout(binding = 4, set = 0) buffer GeometryNodes { GeometryNode nodes[]; } geometryNodes;
|
||||
|
||||
layout(binding = 5, set = 0) uniform sampler2D textures[];
|
||||
|
||||
#include "bufferreferences.glsl"
|
||||
#include "geometrytypes.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
Triangle tri = unpackTriangle(gl_PrimitiveID, 112);
|
||||
hitValue = vec3(tri.normal);
|
||||
|
||||
GeometryNode geometryNode = geometryNodes.nodes[gl_GeometryIndexEXT];
|
||||
|
||||
vec3 color = texture(textures[nonuniformEXT(geometryNode.textureIndexBaseColor)], tri.uv).rgb;
|
||||
if (geometryNode.textureIndexOcclusion > -1) {
|
||||
float occlusion = texture(textures[nonuniformEXT(geometryNode.textureIndexOcclusion)], tri.uv).r;
|
||||
color *= occlusion;
|
||||
}
|
||||
|
||||
hitValue = color;
|
||||
|
||||
// Shadow casting
|
||||
float tmin = 0.001;
|
||||
float tmax = 10000.0;
|
||||
float epsilon = 0.001;
|
||||
vec3 origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT + tri.normal * epsilon;
|
||||
shadowed = true;
|
||||
vec3 lightVector = vec3(-5.0, -2.5, -5.0);
|
||||
// Trace shadow ray and offset indices to match shadow hit/miss shader group indices
|
||||
// traceRayEXT(topLevelAS, gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT | gl_RayFlagsSkipClosestHitShaderEXT, 0xFF, 0, 0, 1, origin, tmin, lightVector, tmax, 2);
|
||||
// if (shadowed) {
|
||||
// hitValue *= 0.7;
|
||||
// }
|
||||
}
|
||||
BIN
shaders/glsl/raytracinggltf/closesthit.rchit.spv
Normal file
BIN
shaders/glsl/raytracinggltf/closesthit.rchit.spv
Normal file
Binary file not shown.
50
shaders/glsl/raytracinggltf/geometrytypes.glsl
Normal file
50
shaders/glsl/raytracinggltf/geometrytypes.glsl
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/* Copyright (c) 2023, Sascha Willems
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
vec3 pos;
|
||||
vec3 normal;
|
||||
vec2 uv;
|
||||
};
|
||||
|
||||
struct Triangle {
|
||||
Vertex vertices[3];
|
||||
vec3 normal;
|
||||
vec2 uv;
|
||||
};
|
||||
|
||||
// This function will unpack our vertex buffer data into a single triangle and calculates uv coordinates
|
||||
Triangle unpackTriangle(uint index, int vertexSize) {
|
||||
Triangle tri;
|
||||
const uint triIndex = index * 3;
|
||||
|
||||
GeometryNode geometryNode = geometryNodes.nodes[gl_GeometryIndexEXT];
|
||||
|
||||
Indices indices = Indices(geometryNode.indexBufferDeviceAddress);
|
||||
Vertices vertices = Vertices(geometryNode.vertexBufferDeviceAddress);
|
||||
|
||||
// Unpack vertices
|
||||
// Data is packed as vec4 so we can map to the glTF vertex structure from the host side
|
||||
// We match vkglTF::Vertex: pos.xyz+normal.x, normalyz+uv.xy
|
||||
// glm::vec3 pos;
|
||||
// glm::vec3 normal;
|
||||
// glm::vec2 uv;
|
||||
// ...
|
||||
for (uint i = 0; i < 3; i++) {
|
||||
const uint offset = indices.i[triIndex + i] * 6;
|
||||
vec4 d0 = vertices.v[offset + 0]; // pos.xyz, n.x
|
||||
vec4 d1 = vertices.v[offset + 1]; // n.yz, uv.xy
|
||||
tri.vertices[i].pos = d0.xyz;
|
||||
tri.vertices[i].normal = vec3(d0.w, d1.xy);
|
||||
tri.vertices[i].uv = d1.zw;
|
||||
}
|
||||
// Calculate values at barycentric coordinates
|
||||
vec3 barycentricCoords = vec3(1.0f - attribs.x - attribs.y, attribs.x, attribs.y);
|
||||
tri.uv = tri.vertices[0].uv * barycentricCoords.x + tri.vertices[1].uv * barycentricCoords.y + tri.vertices[2].uv * barycentricCoords.z;
|
||||
tri.normal = tri.vertices[0].normal * barycentricCoords.x + tri.vertices[1].normal * barycentricCoords.y + tri.vertices[2].normal * barycentricCoords.z;
|
||||
return tri;
|
||||
}
|
||||
15
shaders/glsl/raytracinggltf/miss.rmiss
Normal file
15
shaders/glsl/raytracinggltf/miss.rmiss
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/* Copyright (c) 2023, Sascha Willems
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#version 460
|
||||
#extension GL_EXT_ray_tracing : enable
|
||||
|
||||
layout(location = 0) rayPayloadInEXT vec3 hitValue;
|
||||
|
||||
void main()
|
||||
{
|
||||
hitValue = vec3(1.0);
|
||||
}
|
||||
BIN
shaders/glsl/raytracinggltf/miss.rmiss.spv
Normal file
BIN
shaders/glsl/raytracinggltf/miss.rmiss.spv
Normal file
Binary file not shown.
37
shaders/glsl/raytracinggltf/random.glsl
Normal file
37
shaders/glsl/raytracinggltf/random.glsl
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Copyright (c) 2023, Sascha Willems
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
|
||||
// Tiny Encryption Algorithm
|
||||
// By Fahad Zafar, Marc Olano and Aaron Curtis, see https://www.highperformancegraphics.org/previous/www_2010/media/GPUAlgorithms/HPG2010_GPUAlgorithms_Zafar.pdf
|
||||
uint tea(uint val0, uint val1)
|
||||
{
|
||||
uint sum = 0;
|
||||
uint v0 = val0;
|
||||
uint v1 = val1;
|
||||
for (uint n = 0; n < 16; n++)
|
||||
{
|
||||
sum += 0x9E3779B9;
|
||||
v0 += ((v1 << 4) + 0xA341316C) ^ (v1 + sum) ^ ((v1 >> 5) + 0xC8013EA4);
|
||||
v1 += ((v0 << 4) + 0xAD90777D) ^ (v0 + sum) ^ ((v0 >> 5) + 0x7E95761E);
|
||||
}
|
||||
return v0;
|
||||
}
|
||||
|
||||
// Linear congruential generator based on the previous RNG state
|
||||
// See https://en.wikipedia.org/wiki/Linear_congruential_generator
|
||||
uint lcg(inout uint previous)
|
||||
{
|
||||
const uint multiplier = 1664525u;
|
||||
const uint increment = 1013904223u;
|
||||
previous = (multiplier * previous + increment);
|
||||
return previous & 0x00FFFFFF;
|
||||
}
|
||||
|
||||
// Generate a random float in [0, 1) given the previous RNG state
|
||||
float rnd(inout uint previous)
|
||||
{
|
||||
return (float(lcg(previous)) / float(0x01000000));
|
||||
}
|
||||
77
shaders/glsl/raytracinggltf/raygen.rgen
Normal file
77
shaders/glsl/raytracinggltf/raygen.rgen
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/* Copyright (c) 2023, Sascha Willems
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#version 460
|
||||
#extension GL_EXT_ray_tracing : enable
|
||||
#extension GL_GOOGLE_include_directive : require
|
||||
|
||||
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;
|
||||
layout(binding = 1, set = 0, rgba8) uniform image2D image;
|
||||
layout(binding = 2, set = 0) uniform CameraProperties
|
||||
{
|
||||
mat4 viewInverse;
|
||||
mat4 projInverse;
|
||||
uint frame;
|
||||
} cam;
|
||||
|
||||
layout(location = 0) rayPayloadEXT vec3 hitValue;
|
||||
layout(location = 3) rayPayloadEXT uint payloadSeed;
|
||||
|
||||
#include "random.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
uint seed = tea(gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x, cam.frame);
|
||||
|
||||
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 = cam.frame == 0 ? vec2(0.5f, 0.5f) : vec2(r1, r2);
|
||||
const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + subpixel_jitter;
|
||||
const vec2 inUV = pixelCenter / vec2(gl_LaunchSizeEXT.xy);
|
||||
vec2 d = inUV * 2.0 - 1.0;
|
||||
|
||||
// const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5);
|
||||
// const vec2 inUV = pixelCenter/vec2(gl_LaunchSizeEXT.xy);
|
||||
// vec2 d = inUV * 2.0 - 1.0;
|
||||
|
||||
vec4 origin = cam.viewInverse * vec4(0,0,0,1);
|
||||
vec4 target = cam.projInverse * vec4(d.x, d.y, 1, 1) ;
|
||||
vec4 direction = cam.viewInverse*vec4(normalize(target.xyz), 0.0) ;
|
||||
|
||||
float tmin = 0.001;
|
||||
float tmax = 10000.0;
|
||||
|
||||
hitValue = vec3(0.0);
|
||||
vec3 hitValues = vec3(0);
|
||||
|
||||
const int samples = 4;
|
||||
|
||||
// Trace multiple rays for e.g. transparency
|
||||
for(int smpl = 0; smpl < samples; smpl++) {
|
||||
payloadSeed = tea(gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x, cam.frame);
|
||||
traceRayEXT(topLevelAS, gl_RayFlagsNoneEXT, 0xff, 0, 0, 0, origin.xyz, tmin, direction.xyz, tmax, 0);
|
||||
hitValues += hitValue;
|
||||
}
|
||||
|
||||
// imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hitValues / float(samples), 0.0));
|
||||
|
||||
vec3 hitVal = hitValues / float(samples);
|
||||
|
||||
if(cam.frame > 0)
|
||||
{
|
||||
float a = 1.0f / float(cam.frame + 1);
|
||||
vec3 old_color = imageLoad(image, ivec2(gl_LaunchIDEXT.xy)).xyz;
|
||||
imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(mix(old_color, hitVal, a), 1.f));
|
||||
}
|
||||
else
|
||||
{
|
||||
// First frame, replace the value in the buffer
|
||||
imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hitVal, 1.f));
|
||||
}
|
||||
}
|
||||
BIN
shaders/glsl/raytracinggltf/raygen.rgen.spv
Normal file
BIN
shaders/glsl/raytracinggltf/raygen.rgen.spv
Normal file
Binary file not shown.
9
shaders/glsl/raytracinggltf/shadow.rmiss
Normal file
9
shaders/glsl/raytracinggltf/shadow.rmiss
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#version 460
|
||||
#extension GL_EXT_ray_tracing : require
|
||||
|
||||
layout(location = 2) rayPayloadInEXT bool shadowed;
|
||||
|
||||
void main()
|
||||
{
|
||||
shadowed = false;
|
||||
}
|
||||
BIN
shaders/glsl/raytracinggltf/shadow.rmiss.spv
Normal file
BIN
shaders/glsl/raytracinggltf/shadow.rmiss.spv
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue