From 523aaedecf7f81059deeba2ee5f607760a138279 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Tue, 24 Jun 2025 21:20:48 +0200 Subject: [PATCH] Toy with raytracing --- src/common/rendering/vulkan/vk_levelmesh.cpp | 6 +- src/common/rendering/vulkan/vk_levelmesh.h | 7 +- .../shaders/lightmap/binding_viewer.glsl | 7 +- .../static/shaders/lightmap/frag_viewer.glsl | 151 +++++++++++++----- .../shaders/lightmap/trace_levelmesh.glsl | 9 ++ .../static/shaders/lightmap/vert_viewer.glsl | 7 - 6 files changed, 140 insertions(+), 47 deletions(-) diff --git a/src/common/rendering/vulkan/vk_levelmesh.cpp b/src/common/rendering/vulkan/vk_levelmesh.cpp index 08ea472c3..cb793d27f 100644 --- a/src/common/rendering/vulkan/vk_levelmesh.cpp +++ b/src/common/rendering/vulkan/vk_levelmesh.cpp @@ -645,13 +645,17 @@ void VkLevelMesh::RaytraceScene(const VkRenderPassKey& renderPassKey, VulkanComm float f = 1.0f / std::tan(fovy * (pi::pif() / 360.0f)); ViewerPushConstants pushconstants; - pushconstants.ViewToWorld = viewToWorld; pushconstants.CameraPos = cameraPos; pushconstants.ProjX = f / aspect; pushconstants.ProjY = f; pushconstants.SunDir = FVector3(Mesh->SunDirection.X, Mesh->SunDirection.Z, Mesh->SunDirection.Y); pushconstants.SunColor = Mesh->SunColor; pushconstants.SunIntensity = Mesh->SunIntensity; + pushconstants.ViewX = (viewToWorld * FVector4(1.0f, 0.0f, 0.0f, 1.0f)).XYZ() - cameraPos; + pushconstants.ViewY = (viewToWorld * FVector4(0.0f, 1.0f, 0.0f, 1.0f)).XYZ() - cameraPos; + pushconstants.ViewZ = (viewToWorld * FVector4(0.0f, 0.0f, 1.0f, 1.0f)).XYZ() - cameraPos; + pushconstants.ResolutionScaleX = 2.0f / fb->GetWidth(); + pushconstants.ResolutionScaleY = 2.0f / fb->GetHeight(); commands->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, Viewer.PipelineLayout.get(), 0, Viewer.DescriptorSet.get()); commands->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, Viewer.PipelineLayout.get(), 1, fb->GetDescriptorSetManager()->GetBindlessSet()); diff --git a/src/common/rendering/vulkan/vk_levelmesh.h b/src/common/rendering/vulkan/vk_levelmesh.h index 0c3794c90..56ef406e2 100644 --- a/src/common/rendering/vulkan/vk_levelmesh.h +++ b/src/common/rendering/vulkan/vk_levelmesh.h @@ -55,13 +55,18 @@ struct LightInfo struct ViewerPushConstants { - VSMatrix ViewToWorld; FVector3 CameraPos; float ProjX; FVector3 SunDir; float ProjY; FVector3 SunColor; float SunIntensity; + FVector3 ViewX; + float ResolutionScaleX; + FVector3 ViewY; + float ResolutionScaleY; + FVector3 ViewZ; + float Unused; }; static_assert(sizeof(LightInfo) == sizeof(float) * 20); diff --git a/wadsrc/static/shaders/lightmap/binding_viewer.glsl b/wadsrc/static/shaders/lightmap/binding_viewer.glsl index 28ce3fe3d..06be8ef47 100644 --- a/wadsrc/static/shaders/lightmap/binding_viewer.glsl +++ b/wadsrc/static/shaders/lightmap/binding_viewer.glsl @@ -30,11 +30,16 @@ layout(set = 1, binding = 0) uniform sampler2D textures[]; layout(push_constant) uniform PushConstants { - mat4 ViewToWorld; vec3 CameraPos; float ProjX; vec3 SunDir; float ProjY; vec3 SunColor; float SunIntensity; + vec3 ViewX; + float ResolutionScaleX; + vec3 ViewY; + float ResolutionScaleY; + vec3 ViewZ; + float Unused; }; diff --git a/wadsrc/static/shaders/lightmap/frag_viewer.glsl b/wadsrc/static/shaders/lightmap/frag_viewer.glsl index 14e8d536d..b680ec43a 100644 --- a/wadsrc/static/shaders/lightmap/frag_viewer.glsl +++ b/wadsrc/static/shaders/lightmap/frag_viewer.glsl @@ -6,54 +6,131 @@ #include #include -layout(location = 0) in vec3 FragRay; layout(location = 0) out vec4 fragcolor; +vec3 skyColor(vec3 direction) +{ + if (direction.y > 0.0f) + return mix(vec3(0.6, 0.25, 0.15), vec3(0.8, 0.5, 0.5), direction.y); + else + return vec3(0.03); +} + +uint stepRNG(uint rngState) +{ + return rngState * 747796405 + 1; +} + +float stepAndOutputRNGFloat(inout uint rngState) +{ + rngState = stepRNG(rngState); + uint word = ((rngState >> ((rngState >> 28) + 4)) ^ rngState) * 277803737; + word = (word >> 22) ^ word; + return float(word) / 4294967295.0f; +} + void main() { - vec3 incoming = vec3(0.1); + const int NUM_SAMPLES = 64;//64; + const int MAX_BOUNCES = 8;//32; - vec3 origin = CameraPos; - vec3 L = normalize(FragRay); - TraceResult result = TraceFirstHit(origin, 0.0, L, 10000.0); - if (result.primitiveIndex != -1) + uint rngState = uint(gl_FragCoord.x) + 8720 * uint(gl_FragCoord.y); + vec3 summedPixelColor = vec3(0.0); + + for(int sampleIdx = 0; sampleIdx < NUM_SAMPLES; sampleIdx++) { - SurfaceInfo surface = GetSurface(result.primitiveIndex); - vec3 surfacepos = origin + L * result.t; + vec3 rayOrigin = CameraPos; - if (surface.Sky == 0.0) + const vec2 randomPixelCenter = gl_FragCoord.xy - 0.5 + vec2(stepAndOutputRNGFloat(rngState), stepAndOutputRNGFloat(rngState)); + vec4 viewpos = vec4(randomPixelCenter * vec2(ResolutionScaleX, ResolutionScaleY) - 1.0, -1.0, 1.0); + viewpos.x /= ProjX; + viewpos.y /= ProjY; + vec3 rayDirection = normalize(ViewX * viewpos.x + ViewY * viewpos.y - ViewZ); + + vec3 accumulatedRayColor = vec3(1.0); + for(int tracedSegments = 0; tracedSegments < MAX_BOUNCES; tracedSegments++) { - incoming += TraceSunLight(surfacepos, surface.Normal); - - uint LightStart = surface.LightStart; - uint LightEnd = surface.LightEnd; - for (uint j = LightStart; j < LightEnd; j++) + TraceResult result = TraceFirstHit(rayOrigin, 0.0, rayDirection, 100000.0); + if (result.primitiveIndex != -1) { - incoming += TraceLight(surfacepos, surface.Normal, lights[lightIndexes[j]], 0.0, false); + SurfaceInfo surface = GetSurface(result.primitiveIndex); + if (surface.Sky == 0.0) + { + vec4 color; + if (surface.TextureIndex != 0) + { + vec2 uv = GetSurfaceUV(result.primitiveIndex, result.primitiveWeights); + color = texture(textures[surface.TextureIndex], uv); + } + else + { + // Hit a surface without a texture + color = vec4(0.0); + } + + vec3 worldPos = GetSurfacePos(result.primitiveIndex, result.primitiveWeights); + vec3 lightRayOrigin = worldPos + 0.001 * surface.Normal; + + if (color.a > 0.5) + { + accumulatedRayColor *= color.rgb; + + /* + uint LightStart = surface.LightStart; + uint LightEnd = surface.LightEnd; + if (LightStart != LightEnd && stepAndOutputRNGFloat(rngState) < 0.5) + { + // This is total BS - pretend we hit the lights 50% of the time + // We need some kind of physical representation for the lights + + vec3 incoming = vec3(0.0); + //incoming += TraceSunLight(lightRayOrigin, surface.Normal); + for (uint j = LightStart; j < LightEnd; j++) + incoming += TraceLight(lightRayOrigin, surface.Normal, lights[lightIndexes[j]], 0.0, false); + summedPixelColor += accumulatedRayColor * incoming; + break; + } + */ + + rayOrigin = worldPos + 0.001 * surface.Normal; + + // Mirror reflect 10% of the time to create a glossy surface + /*if (stepAndOutputRNGFloat(rngState) < 0.1) + { + // Mirror reflection: + rayDirection = reflect(rayDirection, surface.Normal); + } + else*/ + { + // Lambertian diffuse reflection: + // Generate a random point on a sphere of radius 1 centered at the normal + const float theta = 6.2831853 * stepAndOutputRNGFloat(rngState); // Random in [0, 2pi] + const float u = 2.0 * stepAndOutputRNGFloat(rngState) - 1.0; // Random in [-1, 1] + const float r = sqrt(1.0 - u * u); + rayDirection = surface.Normal + vec3(r * cos(theta), r * sin(theta), u); + rayDirection = normalize(rayDirection); + } + } + else + { + // Transparent + rayOrigin = worldPos - 0.001 * surface.Normal; + } + } + else + { + // Hit the sky + summedPixelColor += accumulatedRayColor * skyColor(rayDirection); + break; + } + } + else + { + // Ray hit nothing + break; } - - // incoming *= TraceAmbientOcclusion(surfacepos, surface.Normal); - } - else - { - incoming = SunColor; - } - - if (surface.TextureIndex != 0) - { - vec2 uv = GetSurfaceUV(result.primitiveIndex, result.primitiveWeights); - vec4 color = texture(textures[surface.TextureIndex], uv); - incoming *= color.rgb; - } - else - { - incoming = vec3(0.0, 0.0, 1.0); } } - else - { - incoming = vec3(1.0, 0.0, 0.0); - } - fragcolor = vec4(incoming, 1.0); + fragcolor = vec4(summedPixelColor / float(NUM_SAMPLES), 1.0); } diff --git a/wadsrc/static/shaders/lightmap/trace_levelmesh.glsl b/wadsrc/static/shaders/lightmap/trace_levelmesh.glsl index a87200a0e..5f9320d25 100644 --- a/wadsrc/static/shaders/lightmap/trace_levelmesh.glsl +++ b/wadsrc/static/shaders/lightmap/trace_levelmesh.glsl @@ -6,6 +6,15 @@ SurfaceInfo GetSurface(int primitiveIndex) return surfaces[surfaceIndices[primitiveIndex]]; } +vec3 GetSurfacePos(int primitiveIndex, vec3 primitiveWeights) +{ + int index = primitiveIndex * 3; + return + vertices[elements[index + 1]].pos * primitiveWeights.x + + vertices[elements[index + 2]].pos * primitiveWeights.y + + vertices[elements[index + 0]].pos * primitiveWeights.z; +} + vec2 GetSurfaceUV(int primitiveIndex, vec3 primitiveWeights) { int index = primitiveIndex * 3; diff --git a/wadsrc/static/shaders/lightmap/vert_viewer.glsl b/wadsrc/static/shaders/lightmap/vert_viewer.glsl index 4d82f47d3..a7c7dfdc1 100644 --- a/wadsrc/static/shaders/lightmap/vert_viewer.glsl +++ b/wadsrc/static/shaders/lightmap/vert_viewer.glsl @@ -1,8 +1,6 @@ #include -layout(location = 0) out vec3 FragRay; - vec2 positions[4] = vec2[]( vec2(0.0, 0.0), vec2(1.0, 0.0), @@ -12,10 +10,5 @@ vec2 positions[4] = vec2[]( void main() { - vec4 viewpos = vec4(positions[gl_VertexIndex] * 2.0 - 1.0, -1.0, 1.0); - viewpos.x /= ProjX; - viewpos.y = -viewpos.y / ProjY; - FragRay = ((ViewToWorld * viewpos).xyz - CameraPos); - gl_Position = vec4((positions[gl_VertexIndex] * 2.0 - 1.0) * vec2(1.0, -1.0), 1.0, 1.0); }