mirror of
https://github.com/ZDoom/ZDRay.git
synced 2025-02-21 10:41:24 +00:00
Split shaders into three types (light, sun, bounce)
This commit is contained in:
parent
9ce4c027bd
commit
8df36944eb
16 changed files with 915 additions and 595 deletions
|
@ -170,9 +170,15 @@ set( SOURCES
|
|||
src/lightmap/stacktrace.h
|
||||
src/lightmap/gpuraytracer.cpp
|
||||
src/lightmap/gpuraytracer.h
|
||||
src/lightmap/glsl_closesthit.h
|
||||
src/lightmap/glsl_miss.h
|
||||
src/lightmap/glsl_raygen.h
|
||||
src/lightmap/glsl_rchit_bounce.h
|
||||
src/lightmap/glsl_rchit_light.h
|
||||
src/lightmap/glsl_rchit_sun.h
|
||||
src/lightmap/glsl_rgen_bounce.h
|
||||
src/lightmap/glsl_rgen_light.h
|
||||
src/lightmap/glsl_rgen_sun.h
|
||||
src/lightmap/glsl_rmiss_bounce.h
|
||||
src/lightmap/glsl_rmiss_light.h
|
||||
src/lightmap/glsl_rmiss_sun.h
|
||||
src/math/angle.cpp
|
||||
src/math/bounds.cpp
|
||||
src/math/mathlib.cpp
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
static const char* glsl_raygen = R"glsl(
|
||||
|
||||
#version 460
|
||||
#extension GL_EXT_ray_tracing : require
|
||||
|
||||
struct hitPayload
|
||||
{
|
||||
float hitAttenuation;
|
||||
bool isSkyRay;
|
||||
};
|
||||
|
||||
layout(location = 0) rayPayloadEXT hitPayload payload;
|
||||
|
||||
layout(set = 0, binding = 0) uniform accelerationStructureEXT acc;
|
||||
layout(set = 0, binding = 1, rgba32f) uniform image2D positions;
|
||||
layout(set = 0, binding = 2, rgba32f) uniform image2D normals;
|
||||
layout(set = 0, binding = 3, rgba32f) uniform image2D outputs;
|
||||
|
||||
layout(set = 0, binding = 4) uniform Uniforms
|
||||
{
|
||||
vec3 LightOrigin;
|
||||
float PassType;
|
||||
float LightRadius;
|
||||
float LightIntensity;
|
||||
float LightInnerAngleCos;
|
||||
float LightOuterAngleCos;
|
||||
vec3 LightSpotDir;
|
||||
float SampleDistance;
|
||||
vec3 LightColor;
|
||||
float Padding;
|
||||
};
|
||||
|
||||
float RadicalInverse_VdC(uint bits)
|
||||
{
|
||||
bits = (bits << 16u) | (bits >> 16u);
|
||||
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
||||
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
||||
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
||||
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
||||
return float(bits) * 2.3283064365386963e-10f; // / 0x100000000
|
||||
}
|
||||
|
||||
vec2 Hammersley(uint i, uint N)
|
||||
{
|
||||
return vec2(float(i) / float(N), RadicalInverse_VdC(i));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texelPos = ivec2(gl_LaunchIDEXT.xy);
|
||||
vec4 data0 = imageLoad(positions, texelPos);
|
||||
vec4 data1 = imageLoad(normals, texelPos);
|
||||
if (data1 == vec4(0))
|
||||
return;
|
||||
|
||||
vec3 origin = data0.xyz;
|
||||
vec3 normal = data1.xyz;
|
||||
|
||||
vec4 emittance = vec4(0.0);
|
||||
if (PassType == 1.0)
|
||||
emittance = imageLoad(outputs, texelPos);
|
||||
|
||||
const float minDistance = 0.01;
|
||||
const uint sample_count = 1024;
|
||||
|
||||
payload.isSkyRay = (PassType == 0.0);
|
||||
if (!payload.isSkyRay)
|
||||
{
|
||||
float dist = distance(LightOrigin, origin);
|
||||
if (dist > minDistance && dist < LightRadius)
|
||||
{
|
||||
vec3 dir = normalize(LightOrigin - origin);
|
||||
|
||||
float distAttenuation = max(1.0 - (dist / LightRadius), 0.0);
|
||||
float angleAttenuation = max(dot(normal, dir), 0.0);
|
||||
float spotAttenuation = 1.0;
|
||||
if (LightOuterAngleCos > -1.0)
|
||||
{
|
||||
float cosDir = dot(dir, LightSpotDir);
|
||||
spotAttenuation = smoothstep(LightOuterAngleCos, LightInnerAngleCos, cosDir);
|
||||
spotAttenuation = max(spotAttenuation, 0.0);
|
||||
}
|
||||
|
||||
float attenuation = distAttenuation * angleAttenuation * spotAttenuation;
|
||||
if (attenuation > 0.0)
|
||||
{
|
||||
float shadowAttenuation = 0.0;
|
||||
vec3 e0 = cross(normal, abs(normal.x) < abs(normal.y) ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0));
|
||||
vec3 e1 = cross(normal, e0);
|
||||
e0 = cross(normal, e1);
|
||||
for (uint i = 0; i < sample_count; i++)
|
||||
{
|
||||
vec2 offset = (Hammersley(i, sample_count) - 0.5) * SampleDistance;
|
||||
vec3 origin2 = origin + offset.x * e0 + offset.y * e1;
|
||||
|
||||
float dist2 = distance(LightOrigin, origin2);
|
||||
vec3 dir2 = normalize(LightOrigin - origin2);
|
||||
|
||||
traceRayEXT(acc, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, origin2, minDistance, dir2, dist2, 0);
|
||||
shadowAttenuation += payload.hitAttenuation;
|
||||
}
|
||||
shadowAttenuation *= 1.0 / float(sample_count);
|
||||
|
||||
attenuation *= shadowAttenuation;
|
||||
|
||||
emittance.rgb += LightColor * (attenuation * LightIntensity);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vec3 e0 = cross(normal, abs(normal.x) < abs(normal.y) ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0));
|
||||
vec3 e1 = cross(normal, e0);
|
||||
e0 = cross(normal, e1);
|
||||
|
||||
origin += normal * 0.1;
|
||||
|
||||
float attenuation = 0.0;
|
||||
for (uint i = 0; i < sample_count; i++)
|
||||
{
|
||||
vec2 offset = (Hammersley(i, sample_count) - 0.5) * SampleDistance;
|
||||
vec3 origin2 = origin + offset.x * e0 + offset.y * e1;
|
||||
|
||||
float dist2 = 32768.0;
|
||||
vec3 dir2 = LightSpotDir;
|
||||
|
||||
traceRayEXT(acc, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, origin2, minDistance, dir2, dist2, 0);
|
||||
attenuation += payload.hitAttenuation;
|
||||
}
|
||||
attenuation *= 1.0 / float(sample_count);
|
||||
|
||||
emittance.rgb += LightColor * (attenuation * LightIntensity);
|
||||
}
|
||||
|
||||
emittance.w += 1.0;
|
||||
imageStore(outputs, texelPos, emittance);
|
||||
}
|
||||
|
||||
)glsl";
|
33
src/lightmap/glsl_rchit_bounce.h
Normal file
33
src/lightmap/glsl_rchit_bounce.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
static const char* glsl_rchit_bounce = R"glsl(
|
||||
|
||||
#version 460
|
||||
#extension GL_EXT_ray_tracing : require
|
||||
|
||||
struct hitPayload
|
||||
{
|
||||
float hitAttenuation;
|
||||
};
|
||||
|
||||
struct SurfaceInfo
|
||||
{
|
||||
vec3 Normal;
|
||||
float EmissiveDistance;
|
||||
vec3 EmissiveColor;
|
||||
float EmissiveIntensity;
|
||||
float Sky;
|
||||
float Padding0, Padding1, Padding2;
|
||||
};
|
||||
|
||||
layout(location = 0) rayPayloadInEXT hitPayload payload;
|
||||
|
||||
layout(set = 0, binding = 5) buffer SurfaceIndexBuffer { int surfaceIndices[]; };
|
||||
layout(set = 0, binding = 6) buffer SurfaceBuffer { SurfaceInfo surfaces[]; };
|
||||
|
||||
void main()
|
||||
{
|
||||
// SurfaceInfo surface = surfaces[surfaceIndices[gl_PrimitiveID]];
|
||||
|
||||
payload.hitAttenuation = 0.0;
|
||||
}
|
||||
|
||||
)glsl";
|
33
src/lightmap/glsl_rchit_light.h
Normal file
33
src/lightmap/glsl_rchit_light.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
static const char* glsl_rchit_light = R"glsl(
|
||||
|
||||
#version 460
|
||||
#extension GL_EXT_ray_tracing : require
|
||||
|
||||
struct hitPayload
|
||||
{
|
||||
float hitAttenuation;
|
||||
};
|
||||
|
||||
struct SurfaceInfo
|
||||
{
|
||||
vec3 Normal;
|
||||
float EmissiveDistance;
|
||||
vec3 EmissiveColor;
|
||||
float EmissiveIntensity;
|
||||
float Sky;
|
||||
float Padding0, Padding1, Padding2;
|
||||
};
|
||||
|
||||
layout(location = 0) rayPayloadInEXT hitPayload payload;
|
||||
|
||||
layout(set = 0, binding = 5) buffer SurfaceIndexBuffer { int surfaceIndices[]; };
|
||||
layout(set = 0, binding = 6) buffer SurfaceBuffer { SurfaceInfo surfaces[]; };
|
||||
|
||||
void main()
|
||||
{
|
||||
//SurfaceInfo surface = surfaces[surfaceIndices[gl_PrimitiveID]];
|
||||
|
||||
payload.hitAttenuation = 0.0;
|
||||
}
|
||||
|
||||
)glsl";
|
|
@ -1,4 +1,4 @@
|
|||
static const char* glsl_closesthit = R"glsl(
|
||||
static const char* glsl_rchit_sun = R"glsl(
|
||||
|
||||
#version 460
|
||||
#extension GL_EXT_ray_tracing : require
|
||||
|
@ -6,7 +6,6 @@ static const char* glsl_closesthit = R"glsl(
|
|||
struct hitPayload
|
||||
{
|
||||
float hitAttenuation;
|
||||
bool isSkyRay;
|
||||
};
|
||||
|
||||
struct SurfaceInfo
|
||||
|
@ -27,15 +26,7 @@ layout(set = 0, binding = 6) buffer SurfaceBuffer { SurfaceInfo surfaces[]; };
|
|||
void main()
|
||||
{
|
||||
SurfaceInfo surface = surfaces[surfaceIndices[gl_PrimitiveID]];
|
||||
|
||||
if (!payload.isSkyRay)
|
||||
{
|
||||
payload.hitAttenuation = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
payload.hitAttenuation = surface.Sky;
|
||||
}
|
||||
}
|
||||
|
||||
)glsl";
|
110
src/lightmap/glsl_rgen_bounce.h
Normal file
110
src/lightmap/glsl_rgen_bounce.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
static const char* glsl_rgen_bounce = R"glsl(
|
||||
|
||||
#version 460
|
||||
#extension GL_EXT_ray_tracing : require
|
||||
|
||||
struct hitPayload
|
||||
{
|
||||
float hitAttenuation;
|
||||
};
|
||||
|
||||
layout(location = 0) rayPayloadEXT hitPayload payload;
|
||||
|
||||
layout(set = 0, binding = 0) uniform accelerationStructureEXT acc;
|
||||
layout(set = 0, binding = 1, rgba32f) uniform image2D positions;
|
||||
layout(set = 0, binding = 2, rgba32f) uniform image2D normals;
|
||||
layout(set = 0, binding = 3, rgba32f) uniform image2D outputs;
|
||||
|
||||
layout(set = 0, binding = 4) uniform Uniforms
|
||||
{
|
||||
vec3 LightOrigin;
|
||||
float PassType;
|
||||
float LightRadius;
|
||||
float LightIntensity;
|
||||
float LightInnerAngleCos;
|
||||
float LightOuterAngleCos;
|
||||
vec3 LightSpotDir;
|
||||
float SampleDistance;
|
||||
vec3 LightColor;
|
||||
float Padding;
|
||||
};
|
||||
|
||||
float RadicalInverse_VdC(uint bits)
|
||||
{
|
||||
bits = (bits << 16u) | (bits >> 16u);
|
||||
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
||||
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
||||
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
||||
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
||||
return float(bits) * 2.3283064365386963e-10f; // / 0x100000000
|
||||
}
|
||||
|
||||
vec2 Hammersley(uint i, uint N)
|
||||
{
|
||||
return vec2(float(i) / float(N), RadicalInverse_VdC(i));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texelPos = ivec2(gl_LaunchIDEXT.xy);
|
||||
vec4 data0 = imageLoad(positions, texelPos);
|
||||
vec4 data1 = imageLoad(normals, texelPos);
|
||||
if (data1 == vec4(0))
|
||||
return;
|
||||
|
||||
vec3 origin = data0.xyz;
|
||||
vec3 normal = data1.xyz;
|
||||
|
||||
vec4 emittance = vec4(0.0);
|
||||
if (PassType == 1.0)
|
||||
emittance = imageLoad(outputs, texelPos);
|
||||
|
||||
const float minDistance = 0.01;
|
||||
const uint sample_count = 1024;
|
||||
|
||||
float dist = distance(LightOrigin, origin);
|
||||
if (dist > minDistance && dist < LightRadius)
|
||||
{
|
||||
vec3 dir = normalize(LightOrigin - origin);
|
||||
|
||||
float distAttenuation = max(1.0 - (dist / LightRadius), 0.0);
|
||||
float angleAttenuation = max(dot(normal, dir), 0.0);
|
||||
float spotAttenuation = 1.0;
|
||||
if (LightOuterAngleCos > -1.0)
|
||||
{
|
||||
float cosDir = dot(dir, LightSpotDir);
|
||||
spotAttenuation = smoothstep(LightOuterAngleCos, LightInnerAngleCos, cosDir);
|
||||
spotAttenuation = max(spotAttenuation, 0.0);
|
||||
}
|
||||
|
||||
float attenuation = distAttenuation * angleAttenuation * spotAttenuation;
|
||||
if (attenuation > 0.0)
|
||||
{
|
||||
float shadowAttenuation = 0.0;
|
||||
vec3 e0 = cross(normal, abs(normal.x) < abs(normal.y) ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0));
|
||||
vec3 e1 = cross(normal, e0);
|
||||
e0 = cross(normal, e1);
|
||||
for (uint i = 0; i < sample_count; i++)
|
||||
{
|
||||
vec2 offset = (Hammersley(i, sample_count) - 0.5) * SampleDistance;
|
||||
vec3 origin2 = origin + offset.x * e0 + offset.y * e1;
|
||||
|
||||
float dist2 = distance(LightOrigin, origin2);
|
||||
vec3 dir2 = normalize(LightOrigin - origin2);
|
||||
|
||||
traceRayEXT(acc, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, origin2, minDistance, dir2, dist2, 0);
|
||||
shadowAttenuation += payload.hitAttenuation;
|
||||
}
|
||||
shadowAttenuation *= 1.0 / float(sample_count);
|
||||
|
||||
attenuation *= shadowAttenuation;
|
||||
|
||||
emittance.rgb += LightColor * (attenuation * LightIntensity);
|
||||
}
|
||||
}
|
||||
|
||||
emittance.w += 1.0;
|
||||
imageStore(outputs, texelPos, emittance);
|
||||
}
|
||||
|
||||
)glsl";
|
110
src/lightmap/glsl_rgen_light.h
Normal file
110
src/lightmap/glsl_rgen_light.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
static const char* glsl_rgen_light = R"glsl(
|
||||
|
||||
#version 460
|
||||
#extension GL_EXT_ray_tracing : require
|
||||
|
||||
struct hitPayload
|
||||
{
|
||||
float hitAttenuation;
|
||||
};
|
||||
|
||||
layout(location = 0) rayPayloadEXT hitPayload payload;
|
||||
|
||||
layout(set = 0, binding = 0) uniform accelerationStructureEXT acc;
|
||||
layout(set = 0, binding = 1, rgba32f) uniform image2D positions;
|
||||
layout(set = 0, binding = 2, rgba32f) uniform image2D normals;
|
||||
layout(set = 0, binding = 3, rgba32f) uniform image2D outputs;
|
||||
|
||||
layout(set = 0, binding = 4) uniform Uniforms
|
||||
{
|
||||
vec3 LightOrigin;
|
||||
float PassType;
|
||||
float LightRadius;
|
||||
float LightIntensity;
|
||||
float LightInnerAngleCos;
|
||||
float LightOuterAngleCos;
|
||||
vec3 LightSpotDir;
|
||||
float SampleDistance;
|
||||
vec3 LightColor;
|
||||
float Padding;
|
||||
};
|
||||
|
||||
float RadicalInverse_VdC(uint bits)
|
||||
{
|
||||
bits = (bits << 16u) | (bits >> 16u);
|
||||
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
||||
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
||||
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
||||
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
||||
return float(bits) * 2.3283064365386963e-10f; // / 0x100000000
|
||||
}
|
||||
|
||||
vec2 Hammersley(uint i, uint N)
|
||||
{
|
||||
return vec2(float(i) / float(N), RadicalInverse_VdC(i));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texelPos = ivec2(gl_LaunchIDEXT.xy);
|
||||
vec4 data0 = imageLoad(positions, texelPos);
|
||||
vec4 data1 = imageLoad(normals, texelPos);
|
||||
if (data1 == vec4(0))
|
||||
return;
|
||||
|
||||
vec3 origin = data0.xyz;
|
||||
vec3 normal = data1.xyz;
|
||||
|
||||
vec4 emittance = vec4(0.0);
|
||||
if (PassType == 1.0)
|
||||
emittance = imageLoad(outputs, texelPos);
|
||||
|
||||
const float minDistance = 0.01;
|
||||
const uint sample_count = 1024;
|
||||
|
||||
float dist = distance(LightOrigin, origin);
|
||||
if (dist > minDistance && dist < LightRadius)
|
||||
{
|
||||
vec3 dir = normalize(LightOrigin - origin);
|
||||
|
||||
float distAttenuation = max(1.0 - (dist / LightRadius), 0.0);
|
||||
float angleAttenuation = max(dot(normal, dir), 0.0);
|
||||
float spotAttenuation = 1.0;
|
||||
if (LightOuterAngleCos > -1.0)
|
||||
{
|
||||
float cosDir = dot(dir, LightSpotDir);
|
||||
spotAttenuation = smoothstep(LightOuterAngleCos, LightInnerAngleCos, cosDir);
|
||||
spotAttenuation = max(spotAttenuation, 0.0);
|
||||
}
|
||||
|
||||
float attenuation = distAttenuation * angleAttenuation * spotAttenuation;
|
||||
if (attenuation > 0.0)
|
||||
{
|
||||
float shadowAttenuation = 0.0;
|
||||
vec3 e0 = cross(normal, abs(normal.x) < abs(normal.y) ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0));
|
||||
vec3 e1 = cross(normal, e0);
|
||||
e0 = cross(normal, e1);
|
||||
for (uint i = 0; i < sample_count; i++)
|
||||
{
|
||||
vec2 offset = (Hammersley(i, sample_count) - 0.5) * SampleDistance;
|
||||
vec3 origin2 = origin + offset.x * e0 + offset.y * e1;
|
||||
|
||||
float dist2 = distance(LightOrigin, origin2);
|
||||
vec3 dir2 = normalize(LightOrigin - origin2);
|
||||
|
||||
traceRayEXT(acc, gl_RayFlagsOpaqueEXT, 0xff, 1, 0, 1, origin2, minDistance, dir2, dist2, 0);
|
||||
shadowAttenuation += payload.hitAttenuation;
|
||||
}
|
||||
shadowAttenuation *= 1.0 / float(sample_count);
|
||||
|
||||
attenuation *= shadowAttenuation;
|
||||
|
||||
emittance.rgb += LightColor * (attenuation * LightIntensity);
|
||||
}
|
||||
}
|
||||
|
||||
emittance.w += 1.0;
|
||||
imageStore(outputs, texelPos, emittance);
|
||||
}
|
||||
|
||||
)glsl";
|
90
src/lightmap/glsl_rgen_sun.h
Normal file
90
src/lightmap/glsl_rgen_sun.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
static const char* glsl_rgen_sun = R"glsl(
|
||||
|
||||
#version 460
|
||||
#extension GL_EXT_ray_tracing : require
|
||||
|
||||
struct hitPayload
|
||||
{
|
||||
float hitAttenuation;
|
||||
};
|
||||
|
||||
layout(location = 0) rayPayloadEXT hitPayload payload;
|
||||
|
||||
layout(set = 0, binding = 0) uniform accelerationStructureEXT acc;
|
||||
layout(set = 0, binding = 1, rgba32f) uniform image2D positions;
|
||||
layout(set = 0, binding = 2, rgba32f) uniform image2D normals;
|
||||
layout(set = 0, binding = 3, rgba32f) uniform image2D outputs;
|
||||
|
||||
layout(set = 0, binding = 4) uniform Uniforms
|
||||
{
|
||||
vec3 LightOrigin;
|
||||
float PassType;
|
||||
float LightRadius;
|
||||
float LightIntensity;
|
||||
float LightInnerAngleCos;
|
||||
float LightOuterAngleCos;
|
||||
vec3 LightSpotDir;
|
||||
float SampleDistance;
|
||||
vec3 LightColor;
|
||||
float Padding;
|
||||
};
|
||||
|
||||
float RadicalInverse_VdC(uint bits)
|
||||
{
|
||||
bits = (bits << 16u) | (bits >> 16u);
|
||||
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
||||
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
||||
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
||||
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
||||
return float(bits) * 2.3283064365386963e-10f; // / 0x100000000
|
||||
}
|
||||
|
||||
vec2 Hammersley(uint i, uint N)
|
||||
{
|
||||
return vec2(float(i) / float(N), RadicalInverse_VdC(i));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texelPos = ivec2(gl_LaunchIDEXT.xy);
|
||||
vec4 data0 = imageLoad(positions, texelPos);
|
||||
vec4 data1 = imageLoad(normals, texelPos);
|
||||
if (data1 == vec4(0))
|
||||
return;
|
||||
|
||||
vec3 origin = data0.xyz;
|
||||
vec3 normal = data1.xyz;
|
||||
|
||||
vec4 emittance = vec4(0.0);
|
||||
if (PassType == 1.0)
|
||||
emittance = imageLoad(outputs, texelPos);
|
||||
|
||||
const float minDistance = 0.01;
|
||||
const uint sample_count = 1024;
|
||||
|
||||
vec3 e0 = cross(normal, abs(normal.x) < abs(normal.y) ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0));
|
||||
vec3 e1 = cross(normal, e0);
|
||||
e0 = cross(normal, e1);
|
||||
|
||||
origin += normal * 0.1;
|
||||
|
||||
float attenuation = 0.0;
|
||||
for (uint i = 0; i < sample_count; i++)
|
||||
{
|
||||
vec2 offset = (Hammersley(i, sample_count) - 0.5) * SampleDistance;
|
||||
vec3 origin2 = origin + offset.x * e0 + offset.y * e1;
|
||||
|
||||
float dist2 = 32768.0;
|
||||
vec3 dir2 = LightSpotDir;
|
||||
|
||||
traceRayEXT(acc, gl_RayFlagsOpaqueEXT, 0xff, 2, 0, 2, origin2, minDistance, dir2, dist2, 0);
|
||||
attenuation += payload.hitAttenuation;
|
||||
}
|
||||
attenuation *= 1.0 / float(sample_count);
|
||||
emittance.rgb += LightColor * (attenuation * LightIntensity);
|
||||
|
||||
emittance.w += 1.0;
|
||||
imageStore(outputs, texelPos, emittance);
|
||||
}
|
||||
|
||||
)glsl";
|
|
@ -1,4 +1,4 @@
|
|||
static const char* glsl_miss = R"glsl(
|
||||
static const char* glsl_rmiss_bounce = R"glsl(
|
||||
|
||||
#version 460
|
||||
#extension GL_EXT_ray_tracing : require
|
||||
|
@ -6,21 +6,13 @@ static const char* glsl_miss = R"glsl(
|
|||
struct hitPayload
|
||||
{
|
||||
float hitAttenuation;
|
||||
bool isSkyRay;
|
||||
};
|
||||
|
||||
layout(location = 0) rayPayloadInEXT hitPayload payload;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (!payload.isSkyRay)
|
||||
{
|
||||
payload.hitAttenuation = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
payload.hitAttenuation = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
)glsl";
|
18
src/lightmap/glsl_rmiss_light.h
Normal file
18
src/lightmap/glsl_rmiss_light.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
static const char* glsl_rmiss_light = R"glsl(
|
||||
|
||||
#version 460
|
||||
#extension GL_EXT_ray_tracing : require
|
||||
|
||||
struct hitPayload
|
||||
{
|
||||
float hitAttenuation;
|
||||
};
|
||||
|
||||
layout(location = 0) rayPayloadInEXT hitPayload payload;
|
||||
|
||||
void main()
|
||||
{
|
||||
payload.hitAttenuation = 1.0;
|
||||
}
|
||||
|
||||
)glsl";
|
18
src/lightmap/glsl_rmiss_sun.h
Normal file
18
src/lightmap/glsl_rmiss_sun.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
static const char* glsl_rmiss_sun = R"glsl(
|
||||
|
||||
#version 460
|
||||
#extension GL_EXT_ray_tracing : require
|
||||
|
||||
struct hitPayload
|
||||
{
|
||||
float hitAttenuation;
|
||||
};
|
||||
|
||||
layout(location = 0) rayPayloadInEXT hitPayload payload;
|
||||
|
||||
void main()
|
||||
{
|
||||
payload.hitAttenuation = 0.0;
|
||||
}
|
||||
|
||||
)glsl";
|
|
@ -8,64 +8,27 @@
|
|||
#include "framework/templates.h"
|
||||
#include "framework/halffloat.h"
|
||||
#include "vulkanbuilders.h"
|
||||
#include "stacktrace.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <zlib.h>
|
||||
#include "renderdoc_app.h"
|
||||
#include "glsl_raygen.h"
|
||||
#include "glsl_miss.h"
|
||||
#include "glsl_closesthit.h"
|
||||
#include "glsl_rgen_bounce.h"
|
||||
#include "glsl_rgen_light.h"
|
||||
#include "glsl_rgen_sun.h"
|
||||
#include "glsl_rchit_bounce.h"
|
||||
#include "glsl_rchit_light.h"
|
||||
#include "glsl_rchit_sun.h"
|
||||
#include "glsl_rmiss_bounce.h"
|
||||
#include "glsl_rmiss_light.h"
|
||||
#include "glsl_rmiss_sun.h"
|
||||
|
||||
extern int LightBounce;
|
||||
|
||||
static RENDERDOC_API_1_4_2* renderdoc = nullptr;
|
||||
extern bool VKDebug;
|
||||
|
||||
GPURaytracer::GPURaytracer()
|
||||
{
|
||||
if (!renderdoc)
|
||||
{
|
||||
if (HMODULE mod = GetModuleHandle(TEXT("renderdoc.dll")))
|
||||
{
|
||||
pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI");
|
||||
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_4_2, (void**)&renderdoc);
|
||||
if (ret == 1)
|
||||
printf("Renderdoc detected!\n");
|
||||
else
|
||||
renderdoc = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto printLog = [](const char* typestr, const std::string& msg)
|
||||
{
|
||||
printf("\n[%s] %s\n", typestr, msg.c_str());
|
||||
|
||||
std::string callstack = CaptureStackTraceText(0);
|
||||
if (!callstack.empty())
|
||||
printf("%s\n", callstack.c_str());
|
||||
};
|
||||
device = std::make_unique<VulkanDevice>(0, true, printLog);
|
||||
|
||||
const auto& props = device->physicalDevice.properties;
|
||||
|
||||
std::string deviceType;
|
||||
switch (props.deviceType)
|
||||
{
|
||||
case VK_PHYSICAL_DEVICE_TYPE_OTHER: deviceType = "other"; break;
|
||||
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: deviceType = "integrated gpu"; break;
|
||||
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: deviceType = "discrete gpu"; break;
|
||||
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: deviceType = "virtual gpu"; break;
|
||||
case VK_PHYSICAL_DEVICE_TYPE_CPU: deviceType = "cpu"; break;
|
||||
default: deviceType = std::to_string(props.deviceType); break;
|
||||
}
|
||||
|
||||
std::string apiVersion = std::to_string(VK_VERSION_MAJOR(props.apiVersion)) + "." + std::to_string(VK_VERSION_MINOR(props.apiVersion)) + "." + std::to_string(VK_VERSION_PATCH(props.apiVersion));
|
||||
std::string driverVersion = std::to_string(VK_VERSION_MAJOR(props.driverVersion)) + "." + std::to_string(VK_VERSION_MINOR(props.driverVersion)) + "." + std::to_string(VK_VERSION_PATCH(props.driverVersion));
|
||||
|
||||
printf("Vulkan device: %s\n", props.deviceName);
|
||||
printf("Vulkan device type: %s\n", deviceType.c_str());
|
||||
printf("Vulkan version: %s (api) %s (driver)\n", apiVersion.c_str(), driverVersion.c_str());
|
||||
device = std::make_unique<VulkanDevice>(0, VKDebug);
|
||||
PrintVulkanInfo();
|
||||
}
|
||||
|
||||
GPURaytracer::~GPURaytracer()
|
||||
|
@ -76,6 +39,13 @@ void GPURaytracer::Raytrace(LevelMesh* level)
|
|||
{
|
||||
mesh = level;
|
||||
|
||||
printf("Building vulkan acceleration structures\n");
|
||||
|
||||
if (device->renderdoc)
|
||||
device->renderdoc->StartFrameCapture(0, 0);
|
||||
|
||||
CreateVulkanObjects();
|
||||
|
||||
printf("Tracing light probes\n");
|
||||
|
||||
Worker::RunJob((int)mesh->lightProbes.size(), [=](int id) {
|
||||
|
@ -84,10 +54,6 @@ void GPURaytracer::Raytrace(LevelMesh* level)
|
|||
|
||||
printf("Tracing surfaces (%d bounces)\n", LightBounce);
|
||||
|
||||
struct SurfaceTask
|
||||
{
|
||||
int surf, x, y;
|
||||
};
|
||||
std::vector<SurfaceTask> tasks;
|
||||
|
||||
for (size_t i = 0; i < mesh->surfaces.size(); i++)
|
||||
|
@ -108,9 +74,48 @@ void GPURaytracer::Raytrace(LevelMesh* level)
|
|||
}
|
||||
}
|
||||
|
||||
if (renderdoc)
|
||||
renderdoc->StartFrameCapture(0, 0);
|
||||
UploadTasks(tasks);
|
||||
|
||||
// Sunlight
|
||||
{
|
||||
Uniforms uniforms = {};
|
||||
uniforms.LightOrigin = Vec3(0.0f, 0.0f, 0.0f);
|
||||
uniforms.LightRadius = -1.0f;
|
||||
uniforms.LightIntensity = 1.0f;
|
||||
uniforms.LightInnerAngleCos = -1.0f;
|
||||
uniforms.LightOuterAngleCos = -1.0f;
|
||||
uniforms.LightSpotDir = mesh->map->GetSunDirection();
|
||||
uniforms.LightColor = mesh->map->GetSunColor();
|
||||
uniforms.PassType = 0.0f;
|
||||
uniforms.SampleDistance = (float)mesh->samples;
|
||||
RunTrace(uniforms, rgenSunRegion);
|
||||
}
|
||||
|
||||
for (ThingLight& light : mesh->map->ThingLights)
|
||||
{
|
||||
Uniforms uniforms = {};
|
||||
uniforms.LightOrigin = light.LightOrigin();
|
||||
uniforms.LightRadius = light.LightRadius();
|
||||
uniforms.LightIntensity = light.intensity;
|
||||
uniforms.LightInnerAngleCos = light.innerAngleCos;
|
||||
uniforms.LightOuterAngleCos = light.outerAngleCos;
|
||||
uniforms.LightSpotDir = light.SpotDir();
|
||||
uniforms.LightColor = light.rgb;
|
||||
uniforms.PassType = 1.0f;
|
||||
uniforms.SampleDistance = (float)mesh->samples;
|
||||
RunTrace(uniforms, rgenLightRegion);
|
||||
}
|
||||
|
||||
DownloadTasks(tasks);
|
||||
|
||||
if (device->renderdoc)
|
||||
device->renderdoc->EndFrameCapture(0, 0);
|
||||
|
||||
printf("Raytrace complete\n");
|
||||
}
|
||||
|
||||
void GPURaytracer::CreateVulkanObjects()
|
||||
{
|
||||
cmdpool = std::make_unique<VulkanCommandPool>(device.get(), device->graphicsFamily);
|
||||
cmdbuffer = cmdpool->createBuffer();
|
||||
cmdbuffer->begin();
|
||||
|
@ -125,7 +130,10 @@ void GPURaytracer::Raytrace(LevelMesh* level)
|
|||
PipelineBarrier finishbuildbarrier;
|
||||
finishbuildbarrier.addMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR);
|
||||
finishbuildbarrier.execute(cmdbuffer.get(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR);
|
||||
}
|
||||
|
||||
void GPURaytracer::UploadTasks(const std::vector<SurfaceTask>& tasks)
|
||||
{
|
||||
size_t maxTasks = (size_t)rayTraceImageSize * rayTraceImageSize;
|
||||
if (tasks.size() > maxTasks)
|
||||
throw std::runtime_error("Ray trace task count is too large");
|
||||
|
@ -136,7 +144,7 @@ void GPURaytracer::Raytrace(LevelMesh* level)
|
|||
Vec4* normals = (Vec4*)(imageData + imageSize);
|
||||
for (size_t i = 0; i < tasks.size(); i++)
|
||||
{
|
||||
SurfaceTask& task = tasks[i];
|
||||
const SurfaceTask& task = tasks[i];
|
||||
Surface* surface = mesh->surfaces[task.surf].get();
|
||||
|
||||
Vec3 normal = surface->plane.Normal();
|
||||
|
@ -181,20 +189,10 @@ void GPURaytracer::Raytrace(LevelMesh* level)
|
|||
barrier2.addImage(normalsImage.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
barrier2.addImage(outputImage.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, 0, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
|
||||
barrier2.execute(cmdbuffer.get(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR);
|
||||
}
|
||||
|
||||
// Sunlight
|
||||
void GPURaytracer::RunTrace(const Uniforms& uniforms, const VkStridedDeviceAddressRegionKHR& rgenShader)
|
||||
{
|
||||
Uniforms uniforms = {};
|
||||
uniforms.LightOrigin = Vec3(0.0f, 0.0f, 0.0f);
|
||||
uniforms.LightRadius = -1.0f;
|
||||
uniforms.LightIntensity = 1.0f;
|
||||
uniforms.LightInnerAngleCos = -1.0f;
|
||||
uniforms.LightOuterAngleCos = -1.0f;
|
||||
uniforms.LightSpotDir = mesh->map->GetSunDirection();
|
||||
uniforms.LightColor = mesh->map->GetSunColor();
|
||||
uniforms.PassType = 0.0f;
|
||||
uniforms.SampleDistance = (float)mesh->samples;
|
||||
|
||||
auto data = uniformTransferBuffer->Map(0, sizeof(Uniforms));
|
||||
memcpy(data, &uniforms, sizeof(Uniforms));
|
||||
uniformTransferBuffer->Unmap();
|
||||
|
@ -207,8 +205,7 @@ void GPURaytracer::Raytrace(LevelMesh* level)
|
|||
|
||||
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline.get());
|
||||
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipelineLayout.get(), 0, descriptorSet.get());
|
||||
cmdbuffer->traceRays(&rgenRegion, &missRegion, &hitRegion, &callRegion, rayTraceImageSize, rayTraceImageSize, 1);
|
||||
|
||||
cmdbuffer->traceRays(&rgenShader, &missRegion, &hitRegion, &callRegion, rayTraceImageSize, rayTraceImageSize, 1);
|
||||
cmdbuffer->end();
|
||||
|
||||
auto submitFence = std::make_unique<VulkanFence>(device.get());
|
||||
|
@ -227,58 +224,13 @@ void GPURaytracer::Raytrace(LevelMesh* level)
|
|||
cmdbuffer->begin();
|
||||
}
|
||||
|
||||
for (ThingLight& light : mesh->map->ThingLights)
|
||||
void GPURaytracer::DownloadTasks(const std::vector<SurfaceTask>& tasks)
|
||||
{
|
||||
Uniforms uniforms = {};
|
||||
uniforms.LightOrigin = light.LightOrigin();
|
||||
uniforms.LightRadius = light.LightRadius();
|
||||
uniforms.LightIntensity = light.intensity;
|
||||
uniforms.LightInnerAngleCos = light.innerAngleCos;
|
||||
uniforms.LightOuterAngleCos = light.outerAngleCos;
|
||||
uniforms.LightSpotDir = light.SpotDir();
|
||||
uniforms.LightColor = light.rgb;
|
||||
uniforms.PassType = 1.0f;
|
||||
uniforms.SampleDistance = (float)mesh->samples;
|
||||
|
||||
auto data = uniformTransferBuffer->Map(0, sizeof(Uniforms));
|
||||
memcpy(data, &uniforms, sizeof(Uniforms));
|
||||
uniformTransferBuffer->Unmap();
|
||||
|
||||
cmdbuffer->copyBuffer(uniformTransferBuffer.get(), uniformBuffer.get());
|
||||
|
||||
PipelineBarrier barrier3;
|
||||
barrier3.addBuffer(uniformBuffer.get(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
barrier3.execute(cmdbuffer.get(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR);
|
||||
|
||||
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline.get());
|
||||
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipelineLayout.get(), 0, descriptorSet.get());
|
||||
cmdbuffer->traceRays(&rgenRegion, &missRegion, &hitRegion, &callRegion, rayTraceImageSize, rayTraceImageSize, 1);
|
||||
|
||||
cmdbuffer->end();
|
||||
|
||||
auto submitFence = std::make_unique<VulkanFence>(device.get());
|
||||
|
||||
QueueSubmit submit;
|
||||
submit.addCommandBuffer(cmdbuffer.get());
|
||||
submit.execute(device.get(), device->graphicsQueue, submitFence.get());
|
||||
|
||||
vkWaitForFences(device->device, 1, &submitFence->fence, VK_TRUE, std::numeric_limits<uint64_t>::max());
|
||||
vkResetFences(device->device, 1, &submitFence->fence);
|
||||
|
||||
printf(".");
|
||||
|
||||
cmdbuffer.reset();
|
||||
cmdbuffer = cmdpool->createBuffer();
|
||||
cmdbuffer->begin();
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
PipelineBarrier barrier4;
|
||||
barrier4.addImage(outputImage.get(), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
|
||||
barrier4.execute(cmdbuffer.get(), VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
|
||||
region = {};
|
||||
VkBufferImageCopy region = {};
|
||||
region.bufferOffset = 0;
|
||||
region.imageExtent.width = rayTraceImageSize;
|
||||
region.imageExtent.height = rayTraceImageSize;
|
||||
|
@ -297,11 +249,12 @@ void GPURaytracer::Raytrace(LevelMesh* level)
|
|||
vkWaitForFences(device->device, 1, &submitFence->fence, VK_TRUE, std::numeric_limits<uint64_t>::max());
|
||||
vkResetFences(device->device, 1, &submitFence->fence);
|
||||
|
||||
imageData = (uint8_t*)imageTransferBuffer->Map(0, imageSize);
|
||||
size_t imageSize = sizeof(Vec4) * rayTraceImageSize * rayTraceImageSize;
|
||||
uint8_t* imageData = (uint8_t*)imageTransferBuffer->Map(0, imageSize);
|
||||
Vec4* output = (Vec4*)imageData;
|
||||
for (size_t i = 0; i < tasks.size(); i++)
|
||||
{
|
||||
SurfaceTask& task = tasks[i];
|
||||
const SurfaceTask& task = tasks[i];
|
||||
Surface* surface = mesh->surfaces[task.surf].get();
|
||||
|
||||
size_t sampleWidth = surface->lightmapDims[0];
|
||||
|
@ -309,245 +262,7 @@ void GPURaytracer::Raytrace(LevelMesh* level)
|
|||
}
|
||||
imageTransferBuffer->Unmap();
|
||||
|
||||
if (renderdoc)
|
||||
renderdoc->EndFrameCapture(0, 0);
|
||||
|
||||
printf("Raytrace complete\n");
|
||||
}
|
||||
|
||||
void GPURaytracer::RaytraceProbeSample(LightProbeSample* probe)
|
||||
{
|
||||
Vec3 incoming(0.0f, 0.0f, 0.0f);
|
||||
|
||||
if (LightBounce > 0)
|
||||
{
|
||||
Vec3 directions[6] =
|
||||
{
|
||||
{ 1.0f, 0.0f, 0.0f },
|
||||
{ -1.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 1.0f, 0.0f },
|
||||
{ 0.0f, -1.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, 1.0f, },
|
||||
{ 0.0f, 0.0f, -1.0f, }
|
||||
};
|
||||
for (int i = 0; i < SAMPLE_COUNT; i++)
|
||||
{
|
||||
const Vec3& normal = directions[i % 6];
|
||||
Vec2 Xi = Hammersley(i, SAMPLE_COUNT);
|
||||
Vec3 H = ImportanceSampleGGX(Xi, normal, 1.0f);
|
||||
Vec3 L = Vec3::Normalize(H * (2.0f * Vec3::Dot(normal, H)) - normal);
|
||||
incoming += TracePath(probe->Position, L, i);
|
||||
}
|
||||
incoming = incoming / (float)SAMPLE_COUNT / (float)LightBounce;
|
||||
}
|
||||
|
||||
for (ThingLight& light : mesh->map->ThingLights)
|
||||
{
|
||||
Vec3 lightOrigin = light.LightOrigin();
|
||||
float lightRadius = light.LightRadius();
|
||||
|
||||
if (probe->Position.DistanceSq(lightOrigin) > (lightRadius * lightRadius))
|
||||
continue;
|
||||
|
||||
if (mesh->TraceAnyHit(lightOrigin, probe->Position))
|
||||
continue; // this light is occluded by something
|
||||
|
||||
Vec3 dir = (lightOrigin - probe->Position);
|
||||
float dist = dir.Unit();
|
||||
dir.Normalize();
|
||||
|
||||
incoming += light.rgb * (light.SpotAttenuation(dir) * light.DistAttenuation(dist) * light.intensity);
|
||||
}
|
||||
|
||||
const Vec3& sundir = mesh->map->GetSunDirection();
|
||||
LevelTraceHit trace = mesh->Trace(probe->Position, probe->Position + sundir * 32768.0f);
|
||||
if (trace.fraction != 1.0f && trace.hitSurface->bSky)
|
||||
incoming += mesh->map->GetSunColor();
|
||||
|
||||
probe->Color = incoming;
|
||||
}
|
||||
|
||||
void GPURaytracer::RaytraceSurfaceSample(Surface* surface, int x, int y)
|
||||
{
|
||||
Vec3 normal = surface->plane.Normal();
|
||||
Vec3 pos = surface->lightmapOrigin + normal + surface->lightmapSteps[0] * (float)x + surface->lightmapSteps[1] * (float)y;
|
||||
|
||||
Vec3 incoming(0.0f, 0.0f, 0.0f);
|
||||
if (LightBounce > 0)
|
||||
{
|
||||
float totalWeight = 0.0f;
|
||||
for (int i = 0; i < SAMPLE_COUNT; i++)
|
||||
{
|
||||
Vec2 Xi = Hammersley(i, SAMPLE_COUNT);
|
||||
Vec3 H = ImportanceSampleGGX(Xi, normal, 1.0f);
|
||||
Vec3 L = Vec3::Normalize(H * (2.0f * Vec3::Dot(normal, H)) - normal);
|
||||
float NdotL = std::max(Vec3::Dot(normal, L), 0.0f);
|
||||
if (NdotL > 0.0f)
|
||||
{
|
||||
incoming += TracePath(pos, L, i) * NdotL;
|
||||
totalWeight += NdotL;
|
||||
}
|
||||
}
|
||||
incoming = incoming / totalWeight / (float)LightBounce;
|
||||
}
|
||||
|
||||
incoming = incoming + GetSurfaceEmittance(surface, 0.0f) + GetLightEmittance(surface, pos);
|
||||
|
||||
const Vec3& sundir = mesh->map->GetSunDirection();
|
||||
float attenuation = normal.Dot(sundir);
|
||||
if (attenuation > 0.0f)
|
||||
{
|
||||
LevelTraceHit trace = mesh->Trace(pos, pos + sundir * 32768.0f);
|
||||
if (trace.fraction != 1.0f && trace.hitSurface->bSky)
|
||||
incoming += mesh->map->GetSunColor() * attenuation;
|
||||
}
|
||||
|
||||
size_t sampleWidth = surface->lightmapDims[0];
|
||||
surface->samples[x + y * sampleWidth] = incoming;
|
||||
}
|
||||
|
||||
Vec3 GPURaytracer::GetLightEmittance(Surface* surface, const Vec3& pos)
|
||||
{
|
||||
Vec3 emittance = Vec3(0.0f);
|
||||
for (ThingLight& light : mesh->map->ThingLights)
|
||||
{
|
||||
Vec3 lightOrigin = light.LightOrigin();
|
||||
float lightRadius = light.LightRadius();
|
||||
|
||||
if (surface->plane.Distance(lightOrigin) - surface->plane.d < 0)
|
||||
continue; // completely behind the plane
|
||||
|
||||
if (pos.DistanceSq(lightOrigin) > (lightRadius * lightRadius))
|
||||
continue; // light too far away
|
||||
|
||||
Vec3 dir = (lightOrigin - pos);
|
||||
float dist = dir.Unit();
|
||||
dir.Normalize();
|
||||
|
||||
float attenuation = light.SpotAttenuation(dir) * light.DistAttenuation(dist) * surface->plane.Normal().Dot(dir);
|
||||
if (attenuation <= 0.0f)
|
||||
continue;
|
||||
|
||||
if (mesh->TraceAnyHit(lightOrigin, pos))
|
||||
continue; // this light is occluded by something
|
||||
|
||||
emittance += light.rgb * (attenuation * light.intensity);
|
||||
}
|
||||
return emittance;
|
||||
}
|
||||
|
||||
Vec3 GPURaytracer::TracePath(const Vec3& pos, const Vec3& dir, int sampleIndex, int depth)
|
||||
{
|
||||
if (depth >= LightBounce)
|
||||
return Vec3(0.0f);
|
||||
|
||||
LevelTraceHit hit = mesh->Trace(pos + dir * 0.1f, pos + dir * 2000.0f);
|
||||
if (hit.fraction == 1.0f)
|
||||
return Vec3(0.0f);
|
||||
|
||||
Vec3 normal = hit.hitSurface->plane.Normal();
|
||||
Vec3 hitpos = hit.start * (1.0f - hit.fraction) + hit.end * hit.fraction;
|
||||
|
||||
Vec3 emittance = GetSurfaceEmittance(hit.hitSurface, pos.Distance(hitpos)) + GetLightEmittance(hit.hitSurface, hitpos) * 0.5f;
|
||||
|
||||
const Vec3& sundir = mesh->map->GetSunDirection();
|
||||
float attenuation = normal.Dot(sundir);
|
||||
if (attenuation > 0.0f)
|
||||
{
|
||||
Vec3 start = hitpos + normal * 0.1f;
|
||||
LevelTraceHit trace = mesh->Trace(start, start + sundir * 32768.0f);
|
||||
if (trace.fraction != 1.0f && trace.hitSurface->bSky)
|
||||
emittance += mesh->map->GetSunColor() * (attenuation * 0.5f);
|
||||
}
|
||||
|
||||
Vec2 Xi = Hammersley(sampleIndex, SAMPLE_COUNT);
|
||||
Vec3 H = ImportanceSampleGGX(Xi, normal, 1.0f);
|
||||
Vec3 L = Vec3::Normalize(H * (2.0f * Vec3::Dot(normal, H)) - normal);
|
||||
|
||||
float NdotL = Vec3::Dot(normal, L);
|
||||
if (NdotL <= 0.0f)
|
||||
return emittance;
|
||||
|
||||
const float p = 1 / (2 * M_PI);
|
||||
Vec3 incoming = TracePath(hitpos, normal, (sampleIndex + depth + 1) % SAMPLE_COUNT, depth + 1);
|
||||
|
||||
return emittance + incoming * NdotL / p;
|
||||
}
|
||||
|
||||
float GPURaytracer::RadicalInverse_VdC(uint32_t bits)
|
||||
{
|
||||
bits = (bits << 16u) | (bits >> 16u);
|
||||
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
||||
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
||||
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
||||
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
||||
return float(bits) * 2.3283064365386963e-10f; // / 0x100000000
|
||||
}
|
||||
|
||||
Vec2 GPURaytracer::Hammersley(uint32_t i, uint32_t N)
|
||||
{
|
||||
return Vec2(float(i) / float(N), RadicalInverse_VdC(i));
|
||||
}
|
||||
|
||||
Vec3 GPURaytracer::ImportanceSampleGGX(Vec2 Xi, Vec3 N, float roughness)
|
||||
{
|
||||
float a = roughness * roughness;
|
||||
|
||||
float phi = 2.0f * M_PI * Xi.x;
|
||||
float cosTheta = sqrt((1.0f - Xi.y) / (1.0f + (a * a - 1.0f) * Xi.y));
|
||||
float sinTheta = sqrt(1.0f - cosTheta * cosTheta);
|
||||
|
||||
// from spherical coordinates to cartesian coordinates
|
||||
Vec3 H(std::cos(phi) * sinTheta, std::sin(phi) * sinTheta, cosTheta);
|
||||
|
||||
// from tangent-space vector to world-space sample vector
|
||||
Vec3 up = std::abs(N.z) < 0.999f ? Vec3(0.0f, 0.0f, 1.0f) : Vec3(1.0f, 0.0f, 0.0f);
|
||||
Vec3 tangent = Vec3::Normalize(Vec3::Cross(up, N));
|
||||
Vec3 bitangent = Vec3::Cross(N, tangent);
|
||||
|
||||
Vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
|
||||
return Vec3::Normalize(sampleVec);
|
||||
}
|
||||
|
||||
Vec3 GPURaytracer::GetSurfaceEmittance(Surface* surface, float distance)
|
||||
{
|
||||
SurfaceLightDef* def = nullptr;
|
||||
if (surface->type >= ST_MIDDLESIDE && surface->type <= ST_LOWERSIDE)
|
||||
{
|
||||
int lightdefidx = mesh->map->Sides[surface->typeIndex].lightdef;
|
||||
if (lightdefidx != -1)
|
||||
{
|
||||
def = &mesh->map->SurfaceLights[lightdefidx];
|
||||
}
|
||||
}
|
||||
else if (surface->type == ST_FLOOR || surface->type == ST_CEILING)
|
||||
{
|
||||
MapSubsectorEx* sub = &mesh->map->GLSubsectors[surface->typeIndex];
|
||||
IntSector* sector = mesh->map->GetSectorFromSubSector(sub);
|
||||
|
||||
if (sector && surface->numVerts > 0)
|
||||
{
|
||||
if (sector->floorlightdef != -1 && surface->type == ST_FLOOR)
|
||||
{
|
||||
def = &mesh->map->SurfaceLights[sector->floorlightdef];
|
||||
}
|
||||
else if (sector->ceilinglightdef != -1 && surface->type == ST_CEILING)
|
||||
{
|
||||
def = &mesh->map->SurfaceLights[sector->ceilinglightdef];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (def && distance < def->distance + def->distance)
|
||||
{
|
||||
float radius = def->distance + def->distance;
|
||||
float attenuation = std::max(1.0f - (distance / radius), 0.0f);
|
||||
return def->rgb * (attenuation * def->intensity);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Vec3(0.0f);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void GPURaytracer::CreateVertexAndIndexBuffers()
|
||||
|
@ -828,40 +543,62 @@ void GPURaytracer::CreateTopLevelAccelerationStructure()
|
|||
|
||||
void GPURaytracer::CreateShaders()
|
||||
{
|
||||
try
|
||||
{
|
||||
ShaderBuilder builder;
|
||||
builder.setRayGenShader(glsl_raygen);
|
||||
shaderRayGen = builder.create(device.get());
|
||||
shaderRayGen->SetDebugName("shaderRayGen");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(std::string("Could not compile raygen shader: ") + e.what());
|
||||
rgenBounce = CompileRayGenShader(glsl_rgen_bounce, "rgen.bounce");
|
||||
rgenLight = CompileRayGenShader(glsl_rgen_light, "rgen.light");
|
||||
rgenSun = CompileRayGenShader(glsl_rgen_sun, "rgen.sun");
|
||||
rchitBounce = CompileClosestHitShader(glsl_rchit_bounce, "rchit.bounce");
|
||||
rchitLight = CompileClosestHitShader(glsl_rchit_light, "rchit.light");
|
||||
rchitSun = CompileClosestHitShader(glsl_rchit_sun, "rchit.sun");
|
||||
rmissBounce = CompileMissShader(glsl_rmiss_bounce, "rmiss.bounce");
|
||||
rmissLight = CompileMissShader(glsl_rmiss_light, "rmiss.light");
|
||||
rmissSun = CompileMissShader(glsl_rmiss_sun, "rmiss.sun");
|
||||
}
|
||||
|
||||
std::unique_ptr<VulkanShader> GPURaytracer::CompileRayGenShader(const char* code, const char* name)
|
||||
{
|
||||
try
|
||||
{
|
||||
ShaderBuilder builder;
|
||||
builder.setMissShader(glsl_miss);
|
||||
shaderMiss = builder.create(device.get());
|
||||
shaderMiss->SetDebugName("shaderMiss");
|
||||
builder.setRayGenShader(code);
|
||||
auto shader = builder.create(device.get());
|
||||
shader->SetDebugName(name);
|
||||
return shader;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(std::string("Could not compile miss shader: ") + e.what());
|
||||
throw std::runtime_error(std::string("Could not compile ") + name + ": " + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<VulkanShader> GPURaytracer::CompileClosestHitShader(const char* code, const char* name)
|
||||
{
|
||||
try
|
||||
{
|
||||
ShaderBuilder builder;
|
||||
builder.setClosestHitShader(glsl_closesthit);
|
||||
shaderClosestHit = builder.create(device.get());
|
||||
shaderClosestHit->SetDebugName("shaderClosestHit");
|
||||
builder.setClosestHitShader(code);
|
||||
auto shader = builder.create(device.get());
|
||||
shader->SetDebugName(name);
|
||||
return shader;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(std::string("Could not compile closest hit shader: ") + e.what());
|
||||
throw std::runtime_error(std::string("Could not compile ") + name + ": " + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<VulkanShader> GPURaytracer::CompileMissShader(const char* code, const char* name)
|
||||
{
|
||||
try
|
||||
{
|
||||
ShaderBuilder builder;
|
||||
builder.setMissShader(code);
|
||||
auto shader = builder.create(device.get());
|
||||
shader->SetDebugName(name);
|
||||
return shader;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(std::string("Could not compile ") + name + ": " + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -886,20 +623,27 @@ void GPURaytracer::CreatePipeline()
|
|||
RayTracingPipelineBuilder builder;
|
||||
builder.setLayout(pipelineLayout.get());
|
||||
builder.setMaxPipelineRayRecursionDepth(1);
|
||||
builder.addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, shaderRayGen.get());
|
||||
builder.addShader(VK_SHADER_STAGE_MISS_BIT_KHR, shaderMiss.get());
|
||||
builder.addShader(VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, shaderClosestHit.get());
|
||||
builder.addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, rgenBounce.get());
|
||||
builder.addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, rgenLight.get());
|
||||
builder.addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, rgenSun.get());
|
||||
builder.addShader(VK_SHADER_STAGE_MISS_BIT_KHR, rmissBounce.get());
|
||||
builder.addShader(VK_SHADER_STAGE_MISS_BIT_KHR, rmissLight.get());
|
||||
builder.addShader(VK_SHADER_STAGE_MISS_BIT_KHR, rmissSun.get());
|
||||
builder.addShader(VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, rchitBounce.get());
|
||||
builder.addShader(VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, rchitLight.get());
|
||||
builder.addShader(VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, rchitSun.get());
|
||||
builder.addRayGenGroup(0);
|
||||
builder.addMissGroup(1);
|
||||
builder.addTrianglesHitGroup(2);
|
||||
builder.addRayGenGroup(1);
|
||||
builder.addRayGenGroup(2);
|
||||
builder.addMissGroup(3);
|
||||
builder.addMissGroup(4);
|
||||
builder.addMissGroup(5);
|
||||
builder.addTrianglesHitGroup(6);
|
||||
builder.addTrianglesHitGroup(7);
|
||||
builder.addTrianglesHitGroup(8);
|
||||
pipeline = builder.create(device.get());
|
||||
pipeline->SetDebugName("pipeline");
|
||||
|
||||
// OK, this is by far the WORST idea I've seen in vulkan yet. And that's saying a lot.
|
||||
//
|
||||
// Each shader type has its own table, which needs to be aligned.
|
||||
// That means we can't just copy the shader table pointers directly. Each group needs to be sized up and copied individually.
|
||||
|
||||
const auto& rtProperties = device->physicalDevice.rayTracingProperties;
|
||||
|
||||
auto align_up = [](VkDeviceSize value, VkDeviceSize alignment)
|
||||
|
@ -910,25 +654,34 @@ void GPURaytracer::CreatePipeline()
|
|||
return value;
|
||||
};
|
||||
|
||||
VkDeviceSize missCount = 1;
|
||||
VkDeviceSize hitCount = 1;
|
||||
VkDeviceSize raygenCount = 3;
|
||||
VkDeviceSize missCount = 3;
|
||||
VkDeviceSize hitCount = 3;
|
||||
|
||||
VkDeviceSize handleSize = rtProperties.shaderGroupHandleSize;
|
||||
VkDeviceSize handleSizeAligned = align_up(handleSize, rtProperties.shaderGroupHandleAlignment);
|
||||
|
||||
rgenRegion.stride = align_up(handleSizeAligned, rtProperties.shaderGroupBaseAlignment);
|
||||
missRegion.stride = handleSizeAligned;
|
||||
hitRegion.stride = handleSizeAligned;
|
||||
VkDeviceSize rgenStride = align_up(handleSizeAligned, rtProperties.shaderGroupBaseAlignment);
|
||||
VkDeviceSize rgenSize = rgenStride * raygenCount;
|
||||
|
||||
rgenRegion.size = align_up(handleSizeAligned, rtProperties.shaderGroupBaseAlignment);
|
||||
rgenBounceRegion.stride = rgenStride;
|
||||
rgenBounceRegion.size = rgenStride;
|
||||
rgenLightRegion.stride = rgenStride;
|
||||
rgenLightRegion.size = rgenStride;
|
||||
rgenSunRegion.stride = rgenStride;
|
||||
rgenSunRegion.size = rgenStride;
|
||||
|
||||
missRegion.stride = handleSizeAligned;
|
||||
missRegion.size = align_up(missCount * handleSizeAligned, rtProperties.shaderGroupBaseAlignment);
|
||||
|
||||
hitRegion.stride = handleSizeAligned;
|
||||
hitRegion.size = align_up(hitCount * handleSizeAligned, rtProperties.shaderGroupBaseAlignment);
|
||||
|
||||
VkDeviceSize rgenOffset = 0;
|
||||
VkDeviceSize missOffset = rgenOffset + rgenRegion.size;
|
||||
VkDeviceSize missOffset = rgenOffset + rgenSize;
|
||||
VkDeviceSize hitOffset = missOffset + missRegion.size;
|
||||
|
||||
VkDeviceSize sbtBufferSize = rgenRegion.size + missRegion.size + hitRegion.size;
|
||||
VkDeviceSize sbtBufferSize = rgenSize + missRegion.size + hitRegion.size;
|
||||
|
||||
BufferBuilder bufbuilder;
|
||||
bufbuilder.setUsage(VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
|
||||
|
@ -943,11 +696,21 @@ void GPURaytracer::CreatePipeline()
|
|||
sbtTransferBuffer->SetDebugName("sbtTransferBuffer");
|
||||
uint8_t* src = (uint8_t*)pipeline->shaderGroupHandles.data();
|
||||
uint8_t* dest = (uint8_t*)sbtTransferBuffer->Map(0, sbtBufferSize);
|
||||
memcpy(dest + rgenOffset, src, handleSize);
|
||||
for (VkDeviceSize i = 0; i < raygenCount; i++)
|
||||
{
|
||||
memcpy(dest + rgenOffset + i * rgenStride, src, handleSize);
|
||||
src += handleSize;
|
||||
}
|
||||
for (VkDeviceSize i = 0; i < missCount; i++)
|
||||
memcpy(dest + missOffset + i * missRegion.stride, src + (1 + i) * handleSize, handleSize);
|
||||
{
|
||||
memcpy(dest + missOffset + i * missRegion.stride, src, handleSize);
|
||||
src += handleSize;
|
||||
}
|
||||
for (VkDeviceSize i = 0; i < hitCount; i++)
|
||||
memcpy(dest + hitOffset, src + (1 + missCount + i) * handleSize, handleSize);
|
||||
{
|
||||
memcpy(dest + hitOffset + i * hitRegion.stride, src, handleSize);
|
||||
src += handleSize;
|
||||
}
|
||||
sbtTransferBuffer->Unmap();
|
||||
|
||||
cmdbuffer->copyBuffer(sbtTransferBuffer.get(), shaderBindingTable.get());
|
||||
|
@ -956,7 +719,9 @@ void GPURaytracer::CreatePipeline()
|
|||
info.buffer = shaderBindingTable->buffer;
|
||||
VkDeviceAddress sbtAddress = vkGetBufferDeviceAddress(device->device, &info);
|
||||
|
||||
rgenRegion.deviceAddress = sbtAddress + rgenOffset;
|
||||
rgenBounceRegion.deviceAddress = sbtAddress + rgenOffset;
|
||||
rgenLightRegion.deviceAddress = sbtAddress + rgenOffset + rgenStride;
|
||||
rgenSunRegion.deviceAddress = sbtAddress + rgenOffset + 2 * rgenStride;
|
||||
missRegion.deviceAddress = sbtAddress + missOffset;
|
||||
hitRegion.deviceAddress = sbtAddress + hitOffset;
|
||||
}
|
||||
|
@ -1039,3 +804,261 @@ void GPURaytracer::CreateDescriptorSet()
|
|||
write.addBuffer(descriptorSet.get(), 6, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, surfaceBuffer.get());
|
||||
write.updateSets(device.get());
|
||||
}
|
||||
|
||||
void GPURaytracer::PrintVulkanInfo()
|
||||
{
|
||||
const auto& props = device->physicalDevice.properties;
|
||||
|
||||
std::string deviceType;
|
||||
switch (props.deviceType)
|
||||
{
|
||||
case VK_PHYSICAL_DEVICE_TYPE_OTHER: deviceType = "other"; break;
|
||||
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: deviceType = "integrated gpu"; break;
|
||||
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: deviceType = "discrete gpu"; break;
|
||||
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: deviceType = "virtual gpu"; break;
|
||||
case VK_PHYSICAL_DEVICE_TYPE_CPU: deviceType = "cpu"; break;
|
||||
default: deviceType = std::to_string(props.deviceType); break;
|
||||
}
|
||||
|
||||
std::string apiVersion = std::to_string(VK_VERSION_MAJOR(props.apiVersion)) + "." + std::to_string(VK_VERSION_MINOR(props.apiVersion)) + "." + std::to_string(VK_VERSION_PATCH(props.apiVersion));
|
||||
std::string driverVersion = std::to_string(VK_VERSION_MAJOR(props.driverVersion)) + "." + std::to_string(VK_VERSION_MINOR(props.driverVersion)) + "." + std::to_string(VK_VERSION_PATCH(props.driverVersion));
|
||||
|
||||
printf("Vulkan device: %s\n", props.deviceName);
|
||||
printf("Vulkan device type: %s\n", deviceType.c_str());
|
||||
printf("Vulkan version: %s (api) %s (driver)\n", apiVersion.c_str(), driverVersion.c_str());
|
||||
}
|
||||
|
||||
void GPURaytracer::RaytraceProbeSample(LightProbeSample* probe)
|
||||
{
|
||||
Vec3 incoming(0.0f, 0.0f, 0.0f);
|
||||
|
||||
if (LightBounce > 0)
|
||||
{
|
||||
Vec3 directions[6] =
|
||||
{
|
||||
{ 1.0f, 0.0f, 0.0f },
|
||||
{ -1.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 1.0f, 0.0f },
|
||||
{ 0.0f, -1.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, 1.0f, },
|
||||
{ 0.0f, 0.0f, -1.0f, }
|
||||
};
|
||||
for (int i = 0; i < SAMPLE_COUNT; i++)
|
||||
{
|
||||
const Vec3& normal = directions[i % 6];
|
||||
Vec2 Xi = Hammersley(i, SAMPLE_COUNT);
|
||||
Vec3 H = ImportanceSampleGGX(Xi, normal, 1.0f);
|
||||
Vec3 L = Vec3::Normalize(H * (2.0f * Vec3::Dot(normal, H)) - normal);
|
||||
incoming += TracePath(probe->Position, L, i);
|
||||
}
|
||||
incoming = incoming / (float)SAMPLE_COUNT / (float)LightBounce;
|
||||
}
|
||||
|
||||
for (ThingLight& light : mesh->map->ThingLights)
|
||||
{
|
||||
Vec3 lightOrigin = light.LightOrigin();
|
||||
float lightRadius = light.LightRadius();
|
||||
|
||||
if (probe->Position.DistanceSq(lightOrigin) > (lightRadius * lightRadius))
|
||||
continue;
|
||||
|
||||
if (mesh->TraceAnyHit(lightOrigin, probe->Position))
|
||||
continue; // this light is occluded by something
|
||||
|
||||
Vec3 dir = (lightOrigin - probe->Position);
|
||||
float dist = dir.Unit();
|
||||
dir.Normalize();
|
||||
|
||||
incoming += light.rgb * (light.SpotAttenuation(dir) * light.DistAttenuation(dist) * light.intensity);
|
||||
}
|
||||
|
||||
const Vec3& sundir = mesh->map->GetSunDirection();
|
||||
LevelTraceHit trace = mesh->Trace(probe->Position, probe->Position + sundir * 32768.0f);
|
||||
if (trace.fraction != 1.0f && trace.hitSurface->bSky)
|
||||
incoming += mesh->map->GetSunColor();
|
||||
|
||||
probe->Color = incoming;
|
||||
}
|
||||
|
||||
void GPURaytracer::RaytraceSurfaceSample(Surface* surface, int x, int y)
|
||||
{
|
||||
Vec3 normal = surface->plane.Normal();
|
||||
Vec3 pos = surface->lightmapOrigin + normal + surface->lightmapSteps[0] * (float)x + surface->lightmapSteps[1] * (float)y;
|
||||
|
||||
Vec3 incoming(0.0f, 0.0f, 0.0f);
|
||||
if (LightBounce > 0)
|
||||
{
|
||||
float totalWeight = 0.0f;
|
||||
for (int i = 0; i < SAMPLE_COUNT; i++)
|
||||
{
|
||||
Vec2 Xi = Hammersley(i, SAMPLE_COUNT);
|
||||
Vec3 H = ImportanceSampleGGX(Xi, normal, 1.0f);
|
||||
Vec3 L = Vec3::Normalize(H * (2.0f * Vec3::Dot(normal, H)) - normal);
|
||||
float NdotL = std::max(Vec3::Dot(normal, L), 0.0f);
|
||||
if (NdotL > 0.0f)
|
||||
{
|
||||
incoming += TracePath(pos, L, i) * NdotL;
|
||||
totalWeight += NdotL;
|
||||
}
|
||||
}
|
||||
incoming = incoming / totalWeight / (float)LightBounce;
|
||||
}
|
||||
|
||||
incoming = incoming + GetSurfaceEmittance(surface, 0.0f) + GetLightEmittance(surface, pos);
|
||||
|
||||
const Vec3& sundir = mesh->map->GetSunDirection();
|
||||
float attenuation = normal.Dot(sundir);
|
||||
if (attenuation > 0.0f)
|
||||
{
|
||||
LevelTraceHit trace = mesh->Trace(pos, pos + sundir * 32768.0f);
|
||||
if (trace.fraction != 1.0f && trace.hitSurface->bSky)
|
||||
incoming += mesh->map->GetSunColor() * attenuation;
|
||||
}
|
||||
|
||||
size_t sampleWidth = surface->lightmapDims[0];
|
||||
surface->samples[x + y * sampleWidth] = incoming;
|
||||
}
|
||||
|
||||
Vec3 GPURaytracer::GetLightEmittance(Surface* surface, const Vec3& pos)
|
||||
{
|
||||
Vec3 emittance = Vec3(0.0f);
|
||||
for (ThingLight& light : mesh->map->ThingLights)
|
||||
{
|
||||
Vec3 lightOrigin = light.LightOrigin();
|
||||
float lightRadius = light.LightRadius();
|
||||
|
||||
if (surface->plane.Distance(lightOrigin) - surface->plane.d < 0)
|
||||
continue; // completely behind the plane
|
||||
|
||||
if (pos.DistanceSq(lightOrigin) > (lightRadius * lightRadius))
|
||||
continue; // light too far away
|
||||
|
||||
Vec3 dir = (lightOrigin - pos);
|
||||
float dist = dir.Unit();
|
||||
dir.Normalize();
|
||||
|
||||
float attenuation = light.SpotAttenuation(dir) * light.DistAttenuation(dist) * surface->plane.Normal().Dot(dir);
|
||||
if (attenuation <= 0.0f)
|
||||
continue;
|
||||
|
||||
if (mesh->TraceAnyHit(lightOrigin, pos))
|
||||
continue; // this light is occluded by something
|
||||
|
||||
emittance += light.rgb * (attenuation * light.intensity);
|
||||
}
|
||||
return emittance;
|
||||
}
|
||||
|
||||
Vec3 GPURaytracer::TracePath(const Vec3& pos, const Vec3& dir, int sampleIndex, int depth)
|
||||
{
|
||||
if (depth >= LightBounce)
|
||||
return Vec3(0.0f);
|
||||
|
||||
LevelTraceHit hit = mesh->Trace(pos + dir * 0.1f, pos + dir * 2000.0f);
|
||||
if (hit.fraction == 1.0f)
|
||||
return Vec3(0.0f);
|
||||
|
||||
Vec3 normal = hit.hitSurface->plane.Normal();
|
||||
Vec3 hitpos = hit.start * (1.0f - hit.fraction) + hit.end * hit.fraction;
|
||||
|
||||
Vec3 emittance = GetSurfaceEmittance(hit.hitSurface, pos.Distance(hitpos)) + GetLightEmittance(hit.hitSurface, hitpos) * 0.5f;
|
||||
|
||||
const Vec3& sundir = mesh->map->GetSunDirection();
|
||||
float attenuation = normal.Dot(sundir);
|
||||
if (attenuation > 0.0f)
|
||||
{
|
||||
Vec3 start = hitpos + normal * 0.1f;
|
||||
LevelTraceHit trace = mesh->Trace(start, start + sundir * 32768.0f);
|
||||
if (trace.fraction != 1.0f && trace.hitSurface->bSky)
|
||||
emittance += mesh->map->GetSunColor() * (attenuation * 0.5f);
|
||||
}
|
||||
|
||||
Vec2 Xi = Hammersley(sampleIndex, SAMPLE_COUNT);
|
||||
Vec3 H = ImportanceSampleGGX(Xi, normal, 1.0f);
|
||||
Vec3 L = Vec3::Normalize(H * (2.0f * Vec3::Dot(normal, H)) - normal);
|
||||
|
||||
float NdotL = Vec3::Dot(normal, L);
|
||||
if (NdotL <= 0.0f)
|
||||
return emittance;
|
||||
|
||||
const float p = 1 / (2 * M_PI);
|
||||
Vec3 incoming = TracePath(hitpos, normal, (sampleIndex + depth + 1) % SAMPLE_COUNT, depth + 1);
|
||||
|
||||
return emittance + incoming * NdotL / p;
|
||||
}
|
||||
|
||||
float GPURaytracer::RadicalInverse_VdC(uint32_t bits)
|
||||
{
|
||||
bits = (bits << 16u) | (bits >> 16u);
|
||||
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
||||
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
||||
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
||||
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
||||
return float(bits) * 2.3283064365386963e-10f; // / 0x100000000
|
||||
}
|
||||
|
||||
Vec2 GPURaytracer::Hammersley(uint32_t i, uint32_t N)
|
||||
{
|
||||
return Vec2(float(i) / float(N), RadicalInverse_VdC(i));
|
||||
}
|
||||
|
||||
Vec3 GPURaytracer::ImportanceSampleGGX(Vec2 Xi, Vec3 N, float roughness)
|
||||
{
|
||||
float a = roughness * roughness;
|
||||
|
||||
float phi = 2.0f * M_PI * Xi.x;
|
||||
float cosTheta = sqrt((1.0f - Xi.y) / (1.0f + (a * a - 1.0f) * Xi.y));
|
||||
float sinTheta = sqrt(1.0f - cosTheta * cosTheta);
|
||||
|
||||
// from spherical coordinates to cartesian coordinates
|
||||
Vec3 H(std::cos(phi) * sinTheta, std::sin(phi) * sinTheta, cosTheta);
|
||||
|
||||
// from tangent-space vector to world-space sample vector
|
||||
Vec3 up = std::abs(N.z) < 0.999f ? Vec3(0.0f, 0.0f, 1.0f) : Vec3(1.0f, 0.0f, 0.0f);
|
||||
Vec3 tangent = Vec3::Normalize(Vec3::Cross(up, N));
|
||||
Vec3 bitangent = Vec3::Cross(N, tangent);
|
||||
|
||||
Vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
|
||||
return Vec3::Normalize(sampleVec);
|
||||
}
|
||||
|
||||
Vec3 GPURaytracer::GetSurfaceEmittance(Surface* surface, float distance)
|
||||
{
|
||||
SurfaceLightDef* def = nullptr;
|
||||
if (surface->type >= ST_MIDDLESIDE && surface->type <= ST_LOWERSIDE)
|
||||
{
|
||||
int lightdefidx = mesh->map->Sides[surface->typeIndex].lightdef;
|
||||
if (lightdefidx != -1)
|
||||
{
|
||||
def = &mesh->map->SurfaceLights[lightdefidx];
|
||||
}
|
||||
}
|
||||
else if (surface->type == ST_FLOOR || surface->type == ST_CEILING)
|
||||
{
|
||||
MapSubsectorEx* sub = &mesh->map->GLSubsectors[surface->typeIndex];
|
||||
IntSector* sector = mesh->map->GetSectorFromSubSector(sub);
|
||||
|
||||
if (sector && surface->numVerts > 0)
|
||||
{
|
||||
if (sector->floorlightdef != -1 && surface->type == ST_FLOOR)
|
||||
{
|
||||
def = &mesh->map->SurfaceLights[sector->floorlightdef];
|
||||
}
|
||||
else if (sector->ceilinglightdef != -1 && surface->type == ST_CEILING)
|
||||
{
|
||||
def = &mesh->map->SurfaceLights[sector->ceilinglightdef];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (def && distance < def->distance + def->distance)
|
||||
{
|
||||
float radius = def->distance + def->distance;
|
||||
float attenuation = std::max(1.0f - (distance / radius), 0.0f);
|
||||
return def->rgb * (attenuation * def->intensity);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Vec3(0.0f);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,11 @@ struct SurfaceInfo
|
|||
float Padding0, Padding1, Padding2;
|
||||
};
|
||||
|
||||
struct SurfaceTask
|
||||
{
|
||||
int surf, x, y;
|
||||
};
|
||||
|
||||
class GPURaytracer
|
||||
{
|
||||
public:
|
||||
|
@ -39,13 +44,23 @@ public:
|
|||
void Raytrace(LevelMesh* level);
|
||||
|
||||
private:
|
||||
void CreateVulkanObjects();
|
||||
void CreateVertexAndIndexBuffers();
|
||||
void CreateBottomLevelAccelerationStructure();
|
||||
void CreateTopLevelAccelerationStructure();
|
||||
void CreateShaders();
|
||||
std::unique_ptr<VulkanShader> CompileRayGenShader(const char* code, const char* name);
|
||||
std::unique_ptr<VulkanShader> CompileClosestHitShader(const char* code, const char* name);
|
||||
std::unique_ptr<VulkanShader> CompileMissShader(const char* code, const char* name);
|
||||
void CreatePipeline();
|
||||
void CreateDescriptorSet();
|
||||
|
||||
void UploadTasks(const std::vector<SurfaceTask>& tasks);
|
||||
void RunTrace(const Uniforms& uniforms, const VkStridedDeviceAddressRegionKHR& rgenShader);
|
||||
void DownloadTasks(const std::vector<SurfaceTask>& tasks);
|
||||
|
||||
void PrintVulkanInfo();
|
||||
|
||||
void RaytraceProbeSample(LightProbeSample* probe);
|
||||
void RaytraceSurfaceSample(Surface* surface, int x, int y);
|
||||
Vec3 TracePath(const Vec3& pos, const Vec3& dir, int sampleIndex, int depth = 0);
|
||||
|
@ -79,9 +94,9 @@ private:
|
|||
std::unique_ptr<VulkanBuffer> tlAccelStructBuffer;
|
||||
std::unique_ptr<VulkanAccelerationStructure> tlAccelStruct;
|
||||
|
||||
std::unique_ptr<VulkanShader> shaderRayGen;
|
||||
std::unique_ptr<VulkanShader> shaderMiss;
|
||||
std::unique_ptr<VulkanShader> shaderClosestHit;
|
||||
std::unique_ptr<VulkanShader> rgenBounce, rgenLight, rgenSun;
|
||||
std::unique_ptr<VulkanShader> rmissBounce, rmissLight, rmissSun;
|
||||
std::unique_ptr<VulkanShader> rchitBounce, rchitLight, rchitSun;
|
||||
|
||||
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout;
|
||||
|
||||
|
@ -90,7 +105,7 @@ private:
|
|||
std::unique_ptr<VulkanBuffer> shaderBindingTable;
|
||||
std::unique_ptr<VulkanBuffer> sbtTransferBuffer;
|
||||
|
||||
VkStridedDeviceAddressRegionKHR rgenRegion = {};
|
||||
VkStridedDeviceAddressRegionKHR rgenBounceRegion = {}, rgenLightRegion = {}, rgenSunRegion = {};
|
||||
VkStridedDeviceAddressRegionKHR missRegion = {};
|
||||
VkStridedDeviceAddressRegionKHR hitRegion = {};
|
||||
VkStridedDeviceAddressRegionKHR callRegion = {};
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
|
||||
#include "vulkandevice.h"
|
||||
#include "vulkanobjects.h"
|
||||
#include "stacktrace.h"
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
VulkanDevice::VulkanDevice(int vk_device, bool vk_debug, std::function<void(const char* typestr, const std::string& msg)> printLogCallback) : vk_device(vk_device), vk_debug(vk_debug), printLogCallback(printLogCallback)
|
||||
VulkanDevice::VulkanDevice(int vk_device, bool vk_debug) : vk_device(vk_device), vk_debug(vk_debug)
|
||||
{
|
||||
if (HMODULE mod = GetModuleHandle(TEXT("renderdoc.dll")))
|
||||
{
|
||||
pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI");
|
||||
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_4_2, (void**)&renderdoc);
|
||||
if (ret != 1)
|
||||
renderdoc = nullptr;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
initVolk();
|
||||
|
@ -391,8 +400,11 @@ VkBool32 VulkanDevice::debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT mess
|
|||
typestr = "vulkan";
|
||||
}
|
||||
|
||||
if (device->printLogCallback)
|
||||
device->printLogCallback(typestr, msg);
|
||||
printf("\n[%s] %s\n", typestr, msg.c_str());
|
||||
|
||||
std::string callstack = CaptureStackTraceText(0);
|
||||
if (!callstack.empty())
|
||||
printf("%s\n", callstack.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "volk/volk.h"
|
||||
#include "vk_mem_alloc/vk_mem_alloc.h"
|
||||
#include "renderdoc_app.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#undef min
|
||||
|
@ -45,7 +46,7 @@ public:
|
|||
class VulkanDevice
|
||||
{
|
||||
public:
|
||||
VulkanDevice(int vk_device = 0, bool vk_debug = false, std::function<void(const char* typestr, const std::string& msg)> printLogCallback = {});
|
||||
VulkanDevice(int vk_device = 0, bool vk_debug = false);
|
||||
~VulkanDevice();
|
||||
|
||||
void setDebugObjectName(const char *name, uint64_t handle, VkObjectType type)
|
||||
|
@ -102,12 +103,13 @@ public:
|
|||
std::vector<VulkanPhysicalDevice> availableDevices;
|
||||
std::vector<VulkanCompatibleDevice> supportedDevices;
|
||||
|
||||
RENDERDOC_API_1_4_2* renderdoc = nullptr;
|
||||
|
||||
static void initVolk();
|
||||
|
||||
private:
|
||||
int vk_device;
|
||||
bool vk_debug;
|
||||
std::function<void(const char* typestr, const std::string& msg)> printLogCallback;
|
||||
|
||||
void createInstance();
|
||||
//void createSurface();
|
||||
|
|
|
@ -117,6 +117,7 @@ int NumThreads = 0;
|
|||
int LMDims = 1024;
|
||||
int Samples = 8;
|
||||
bool CPURaytrace = false;
|
||||
bool VKDebug = false;
|
||||
int LightBounce = 1;
|
||||
float GridSize = 32.0f;
|
||||
|
||||
|
@ -159,10 +160,11 @@ static option long_opts[] =
|
|||
{"cpu-raytrace", no_argument, 0, 'C'},
|
||||
{"bounce", required_argument, 0, 'B'},
|
||||
{"gridsize", required_argument, 0, 'i'},
|
||||
{"vkdebug", no_argument, 0, 'D'},
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
static const char short_opts[] = "wVgGvbNrReEm:o:f:p:s:d:PqtzZXx5cj:Q:S:C";
|
||||
static const char short_opts[] = "wVgGvbNrReEm:o:f:p:s:d:PqtzZXx5cj:Q:S:CD";
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
|
@ -452,6 +454,9 @@ static void ParseArgs(int argc, char **argv)
|
|||
case 'C':
|
||||
CPURaytrace = true;
|
||||
break;
|
||||
case 'D':
|
||||
VKDebug = true;
|
||||
break;
|
||||
case 'B':
|
||||
LightBounce = atoi(optarg);
|
||||
if (LightBounce < 0) LightBounce = 0;
|
||||
|
@ -512,6 +517,7 @@ static void ShowUsage()
|
|||
" -i, --gridsize=NNN Automatic light probe grid size, floating point\n"
|
||||
" Lower values increase granularity at the expense of performance\n"
|
||||
" Recommended: 32.0, 64.0, 128.0, etc (default %.1f)\n"
|
||||
" -D, --vkdebug Print messages from the vulkan validation layer\n"
|
||||
" -w, --warn Show warning messages\n"
|
||||
#if HAVE_TIMING
|
||||
" -t, --no-timing Suppress timing information\n"
|
||||
|
|
Loading…
Reference in a new issue