Toy with raytracing

This commit is contained in:
Magnus Norddahl 2025-06-24 21:20:48 +02:00
commit 523aaedecf
6 changed files with 139 additions and 46 deletions

View file

@ -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());

View file

@ -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);

View file

@ -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;
};

View file

@ -6,54 +6,131 @@
#include <shaders/lightmap/trace_light.glsl>
#include <shaders/lightmap/trace_ambient_occlusion.glsl>
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);
}

View file

@ -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;

View file

@ -1,8 +1,6 @@
#include <shaders/lightmap/binding_viewer.glsl>
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);
}