Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,971 changes: 1,971 additions & 0 deletions attachments/38_ray_tracing.cpp

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions attachments/38_ray_tracing.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#version 450

layout(binding = 1) uniform sampler2D texSampler;

layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;

layout(location = 0) out vec4 outColor;

void main() {
outColor = texture(texSampler, fragTexCoord);
}
95 changes: 95 additions & 0 deletions attachments/38_ray_tracing.slang
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
struct VSInput {
float3 inPosition;
float3 inColor;
float2 inTexCoord;
float3 inNormal;
};

struct UniformBuffer {
float4x4 model;
float4x4 view;
float4x4 proj;
float3 cameraPos;
};
[[vk::binding(0,0)]]
ConstantBuffer<UniformBuffer> ubo;

// TASK04: Acceleration structure binding
[[vk::binding(1,0)]]
RaytracingAccelerationStructure accelerationStructure;

[[vk::binding(2,0)]]
StructuredBuffer<uint> indexBuffer;

[[vk::binding(3,0)]]
StructuredBuffer<float2> uvBuffer;

// TASK09: Instance look-up table
struct InstanceLUT {
uint materialID;
uint indexBufferOffset;
};
[[vk::binding(4,0)]]
StructuredBuffer<InstanceLUT> instanceLUTBuffer;

struct VSOutput
{
float4 pos : SV_Position;
float3 fragColor;
float2 fragTexCoord;
float3 fragNormal;
float3 worldPos;
};

[shader("vertex")]
VSOutput vertMain(VSInput input) {
VSOutput output;
output.pos = mul(ubo.proj, mul(ubo.view, mul(ubo.model, float4(input.inPosition, 1.0))));
output.fragColor = input.inColor;
output.fragTexCoord = input.inTexCoord;
output.fragNormal = input.inNormal;
output.worldPos = mul(ubo.model, float4(input.inPosition, 1.0)).xyz;
return output;
}

// TASK09: Bindless resources
[[vk::binding(0,1)]]
SamplerState textureSampler;

[[vk::binding(1,1)]]
Texture2D<float4> textures[];

struct PushConstant {
uint materialIndex;
};
[push_constant]
PushConstant pc;

static const float3 lightDir = float3(-6.0, 0.0, 6.0);

// Small epsilon to avoid self-intersection
static const float EPSILON = 0.01;

float2 intersection_uv(uint instanceID, uint primIndex, float2 barycentrics) {
// TASK10
return float2(0.0, 0.0);
}

void apply_reflection(float3 P, float3 N, inout float4 baseColor) {
// TASK11
}

// TASK05: Implement ray query shadows
bool in_shadow(float3 P)
{
bool hit = false;

return hit;
}

[shader("fragment")]
float4 fragMain(VSOutput vertIn) : SV_TARGET {
float4 baseColor = textures[pc.materialIndex].Sample(textureSampler, vertIn.fragTexCoord);

return baseColor;
}
20 changes: 20 additions & 0 deletions attachments/38_ray_tracing.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#version 450

layout(binding = 0) uniform UniformBufferObject {
mat4 model;
mat4 view;
mat4 proj;
} ubo;

layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;

layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord;

void main() {
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
fragColor = inColor;
fragTexCoord = inTexCoord;
}
210 changes: 210 additions & 0 deletions attachments/38_ray_tracing_complete.slang
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
struct VSInput {
float3 inPosition;
float3 inColor;
float2 inTexCoord;
float3 inNormal;
};

struct UniformBuffer {
float4x4 model;
float4x4 view;
float4x4 proj;
float3 cameraPos;
};
[[vk::binding(0,0)]]
ConstantBuffer<UniformBuffer> ubo;

// TASK05: Acceleration structure binding
[[vk::binding(1,0)]]
RaytracingAccelerationStructure accelerationStructure;

[[vk::binding(2,0)]]
StructuredBuffer<uint> indexBuffer;

[[vk::binding(3,0)]]
StructuredBuffer<float2> uvBuffer;

// TASK09: Instance look-up table
struct InstanceLUT {
uint materialID;
uint indexBufferOffset;
};
[[vk::binding(4,0)]]
StructuredBuffer<InstanceLUT> instanceLUTBuffer;

struct VSOutput
{
float4 pos : SV_Position;
float3 fragColor;
float2 fragTexCoord;
float3 fragNormal;
float3 worldPos;
};

[shader("vertex")]
VSOutput vertMain(VSInput input) {
VSOutput output;
output.pos = mul(ubo.proj, mul(ubo.view, mul(ubo.model, float4(input.inPosition, 1.0))));
output.fragColor = input.inColor;
output.fragTexCoord = input.inTexCoord;
output.fragNormal = input.inNormal;
output.worldPos = mul(ubo.model, float4(input.inPosition, 1.0)).xyz;
return output;
}

// TASK09: Bindless resources
[[vk::binding(0,1)]]
SamplerState textureSampler;

[[vk::binding(1,1)]]
Texture2D<float4> textures[];

// TASK11
struct PushConstant {
uint materialIndex;
uint reflective;
};
[push_constant]
PushConstant pc;

static const float3 lightDir = float3(-6.0, 0.0, 6.0);

// Small epsilon to avoid self-intersection
static const float EPSILON = 0.01;

float2 intersection_uv(uint instanceID, uint primIndex, float2 barycentrics) {
uint indexOffset = instanceLUTBuffer[NonUniformResourceIndex(instanceID)].indexBufferOffset;

uint i0 = indexBuffer[indexOffset + (primIndex * 3 + 0)];
uint i1 = indexBuffer[indexOffset + (primIndex * 3 + 1)];
uint i2 = indexBuffer[indexOffset + (primIndex * 3 + 2)];

float2 uv0 = uvBuffer[i0];
float2 uv1 = uvBuffer[i1];
float2 uv2 = uvBuffer[i2];

float w0 = 1.0 - barycentrics.x - barycentrics.y;
float w1 = barycentrics.x;
float w2 = barycentrics.y;

return w0 * uv0 + w1 * uv1 + w2 * uv2;
}

void apply_reflection(float3 P, float3 N, inout float4 baseColor) {
// Build the reflections ray
float3 V = normalize(ubo.cameraPos - P);
float3 R = reflect(-V, N);

RayDesc reflectionRayDesc;
reflectionRayDesc.Origin = P;
reflectionRayDesc.Direction = R;
reflectionRayDesc.TMin = EPSILON;
reflectionRayDesc.TMax = 1e4;

// Initialize a ray query for reflections
RayQuery<RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES> rq;
let rayFlags = RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES;

rq.TraceRayInline(accelerationStructure, rayFlags, 0xFF, reflectionRayDesc);

while (rq.Proceed())
{
uint instanceID = rq.CandidateRayInstanceCustomIndex();
uint primIndex = rq.CandidatePrimitiveIndex();

float2 uv = intersection_uv(instanceID, primIndex, rq.CandidateTriangleBarycentrics());

uint materialID = instanceLUTBuffer[NonUniformResourceIndex(instanceID)].materialID;
float4 intersectionColor = textures[NonUniformResourceIndex(materialID)].SampleLevel(textureSampler, uv, 0);

if (intersectionColor.a < 0.5) {
// If the triangle is transparent, we continue to trace
// to find the next opaque triangle.
} else {
// If we hit an opaque triangle, we stop tracing.
rq.CommitNonOpaqueTriangleHit();
}
}

bool hit = (rq.CommittedStatus() == COMMITTED_TRIANGLE_HIT);

if (hit)
{
uint instanceID = rq.CommittedRayInstanceCustomIndex();
uint primIndex = rq.CommittedPrimitiveIndex();

float2 uv = intersection_uv(instanceID, primIndex, rq.CommittedTriangleBarycentrics());

uint materialID = instanceLUTBuffer[NonUniformResourceIndex(instanceID)].materialID;
float4 intersectionColor = textures[NonUniformResourceIndex(materialID)].SampleLevel(textureSampler, uv, 0);

baseColor.rgb = lerp(baseColor.rgb, intersectionColor.rgb, 0.7);
}
}

// TASK05: Implement ray query shadows
bool in_shadow(float3 P)
{
// Build the shadow ray from the world position toward the light
RayDesc shadowRayDesc;
shadowRayDesc.Origin = P;
shadowRayDesc.Direction = normalize(lightDir);
shadowRayDesc.TMin = EPSILON;
shadowRayDesc.TMax = 1e4;

// Initialize a ray query for shadows
RayQuery<RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES |
RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH> sq;
let rayFlags = RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES |
RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH;

sq.TraceRayInline(accelerationStructure, rayFlags, 0xFF, shadowRayDesc);

while (sq.Proceed())
{
uint instanceID = sq.CandidateRayInstanceCustomIndex();
uint primIndex = sq.CandidatePrimitiveIndex();

float2 uv = intersection_uv(instanceID, primIndex, sq.CandidateTriangleBarycentrics());

uint materialID = instanceLUTBuffer[NonUniformResourceIndex(instanceID)].materialID;
float4 intersectionColor = textures[NonUniformResourceIndex(materialID)].SampleLevel(textureSampler, uv, 0);

if (intersectionColor.a < 0.5) {
// If the triangle is transparent, we continue to trace
// to find the next opaque triangle.
} else {
// If we hit an opaque triangle, we stop tracing.
sq.CommitNonOpaqueTriangleHit();
}
}

// If the shadow ray intersects an opaque triangle, we consider the pixel in shadow
bool hit = (sq.CommittedStatus() == COMMITTED_TRIANGLE_HIT);

return hit;
}

[shader("fragment")]
float4 fragMain(VSOutput vertIn) : SV_TARGET {
float4 baseColor = textures[pc.materialIndex].Sample(textureSampler, vertIn.fragTexCoord);

// Alpha test
if (baseColor.a < 0.5) discard;

float3 P = vertIn.worldPos;
float3 N = vertIn.fragNormal;

if (pc.reflective > 0) {
apply_reflection(P, N, baseColor);
}

bool inShadow = in_shadow(P);

// Darken if in shadow
if (inShadow) {
baseColor.rgb *= 0.2;
}

return baseColor;
}
18 changes: 15 additions & 3 deletions attachments/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ function (add_slang_shader_target TARGET)
)
add_custom_command (
OUTPUT ${SHADERS_DIR}/slang.spv
COMMAND ${SLANGC_EXECUTABLE} ${SHADER_SOURCES} -target spirv -profile spirv_1_4 -emit-spirv-directly -fvk-use-entrypoint-name ${ENTRY_POINTS} -o slang.spv
COMMAND ${SLANGC_EXECUTABLE} ${SHADER_SOURCES} -target spirv -profile spirv_1_4+spvRayQueryKHR -emit-spirv-directly -fvk-use-entrypoint-name ${ENTRY_POINTS} -o slang.spv
WORKING_DIRECTORY ${SHADERS_DIR}
DEPENDS ${SHADERS_DIR} ${SHADER_SOURCES}
COMMENT "Compiling Slang Shaders"
Expand Down Expand Up @@ -130,10 +130,12 @@ function (add_chapter CHAPTER_NAME)
target_link_libraries (${CHAPTER_NAME} ${CHAPTER_LIBS})
endif ()
if (DEFINED CHAPTER_MODELS)
file (COPY assets/${CHAPTER_MODELS} DESTINATION ${CMAKE_BINARY_DIR}/${CHAPTER_NAME}/models)
list(TRANSFORM CHAPTER_MODELS PREPEND "${CMAKE_SOURCE_DIR}/assets/")
file (COPY ${CHAPTER_MODELS} DESTINATION ${CMAKE_BINARY_DIR}/${CHAPTER_NAME}/models)
endif ()
if (DEFINED CHAPTER_TEXTURES)
file (COPY assets/${CHAPTER_TEXTURES} DESTINATION ${CMAKE_BINARY_DIR}/${CHAPTER_NAME}/textures)
list(TRANSFORM CHAPTER_TEXTURES PREPEND "${CMAKE_SOURCE_DIR}/assets/")
file (COPY ${CHAPTER_TEXTURES} DESTINATION ${CMAKE_BINARY_DIR}/${CHAPTER_NAME}/textures)
endif ()
endfunction ()

Expand Down Expand Up @@ -275,3 +277,13 @@ add_chapter (36_multiple_objects
add_chapter (37_multithreading
SHADER 37_shader_compute
LIBS glm::glm)

add_chapter (38_ray_tracing
SHADER 38_ray_tracing
MODELS plant_on_table.obj
MODELS plant_on_table.mtl
TEXTURES plant_on_table_textures/nettle_plant_diff_4k.png
TEXTURES plant_on_table_textures/potted_plant_02_pot_diff_1k.png
TEXTURES plant_on_table_textures/wooden_picnic_table_bottom_diff_1k.png
TEXTURES plant_on_table_textures/wooden_picnic_table_top_diff_1k.png
LIBS glm::glm tinyobjloader::tinyobjloader)
Loading
Loading