implement per-vertex shading in preparation for actor tracing

This commit is contained in:
Ricardo Luís Vaz Silva 2025-01-16 21:30:13 -03:00 committed by Magnus Norddahl
commit 5adb61a9b4
7 changed files with 212 additions and 115 deletions

View file

@ -250,7 +250,7 @@ void VkDescriptorSetManager::CreateLevelMeshLayout()
.AddBinding(1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT)
.AddBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT)
.AddBinding(3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT)
.AddBinding(4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT)
.AddBinding(4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT)
.AddBinding(5, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT)
.AddBinding(6, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT)
.DebugName("VkDescriptorSetManager.LevelMesh.Layout")
@ -273,24 +273,24 @@ void VkDescriptorSetManager::CreateRSBufferLayout()
void VkDescriptorSetManager::CreateFixedLayout()
{
DescriptorSetLayoutBuilder builder;
builder.AddBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
if (fb->IsRayQueryEnabled())
{
builder.AddBinding(5, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(5, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
}
else
{
builder.AddBinding(5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
}
builder.AddBinding(6, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(7, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(8, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(9, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(10, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(6, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(7, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(8, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(9, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(10, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
builder.DebugName("VkDescriptorSetManager.Fixed.SetLayout");
Fixed.Layout = builder.Create(fb->GetDevice());
}
@ -383,7 +383,7 @@ void VkDescriptorSetManager::CreateBindlessSet()
.AddBinding(
0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
MaxBindlessTextures,
VK_SHADER_STAGE_FRAGMENT_BIT,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT | VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT)
.DebugName("Bindless.Layout")
.Create(fb->GetDevice());

View file

@ -205,15 +205,16 @@ static std::vector<BuiltinFieldDesc> vertexShaderOutputs
{
{"vTexCoord", "", UniformType::Vec4, FieldCondition::ALWAYS}, //0
{"vColor", "", UniformType::Vec4, FieldCondition::ALWAYS}, //1
{"pixelpos", "", UniformType::Vec4, FieldCondition::NOTSIMPLE}, //2
{"pixelpos", "", UniformType::Vec4, FieldCondition::ALWAYS}, //2
{"glowdist", "", UniformType::Vec3, FieldCondition::NOTSIMPLE}, //3
{"gradientdist", "", UniformType::Vec3, FieldCondition::NOTSIMPLE}, //4
{"vWorldNormal", "", UniformType::Vec4, FieldCondition::NOTSIMPLE}, //5
{"vEyeNormal", "", UniformType::Vec4, FieldCondition::NOTSIMPLE}, //6
{"vWorldNormal", "", UniformType::Vec4, FieldCondition::ALWAYS}, //5
{"vEyeNormal", "", UniformType::Vec4, FieldCondition::ALWAYS}, //6
{"ClipDistanceA", "", UniformType::Vec4, FieldCondition::HAS_CLIPDISTANCE}, //7
{"ClipDistanceB", "", UniformType::Vec4, FieldCondition::HAS_CLIPDISTANCE}, //8
{"vLightmap", "", UniformType::Vec3, FieldCondition::ALWAYS}, //9
{"uDataIndex", "flat", UniformType::Int, FieldCondition::USELEVELMESH}, //10
{"vLightColor", "", UniformType::Vec3, FieldCondition::SHADE_VERTEX}, //11
};
static std::vector<BuiltinFieldDesc> fragShaderOutputs
@ -423,7 +424,7 @@ void VkShaderManager::BuildDefinesBlock(FString &definesBlock, const char *defin
switch (key.ShadeVertex)
{
case 1: definesBlock << "#define SHADE_VERTEX\n"; break;
case 2: definesBlock << "#define SHADE_VERTEX_CENTER\n"; break;
case 2: definesBlock << "#define SHADE_VERTEX\n#define SHADE_VERTEX_CENTER\n"; break;
//case 3: definesBlock << "#define ???\n"; break;
}

View file

@ -6,7 +6,6 @@ layout(set = 0, binding = 2) uniform sampler2D LinearDepth;
layout(set = 0, binding = 3) uniform samplerCubeArray IrradianceMap;
layout(set = 0, binding = 4) uniform samplerCubeArray PrefilterMap;
#if defined(USE_RAYTRACE)
#if defined(SUPPORTS_RAYQUERY)
layout(set = 0, binding = 5) uniform accelerationStructureEXT acc;
@ -29,4 +28,4 @@ layout(set = 0, binding = 7, std430) buffer readonly ElementBuffer { int elem
layout(set = 0, binding = 8, std430) buffer readonly SurfaceIndexBuffer { uint surfaceIndices[]; };
layout(set = 0, binding = 9, std430) buffer readonly SurfaceBuffer { SurfaceInfo surfaces[]; };
layout(set = 0, binding = 10, std430) buffer readonly PortalBuffer { PortalInfo portals[]; };
#endif

View file

@ -3,84 +3,7 @@
#if defined(USE_RAYTRACE)
#include <shaders/lightmap/polyfill_rayquery.glsl>
#include <shaders/lightmap/trace_levelmesh.glsl>
#include <shaders/lightmap/montecarlo.glsl>
float TraceDynLightRay(vec3 origin, float tmin, vec3 direction, float dist)
{
float alpha = 1.0;
for (int i = 0; i < 3; i++)
{
TraceResult result = TraceFirstHit(origin, tmin, direction, dist);
// Stop if we hit nothing - the point light is visible.
if (result.primitiveIndex == -1)
return alpha;
SurfaceInfo surface = GetSurface(result.primitiveIndex);
// Pass through surface texture
alpha = PassRayThroughSurfaceDynLight(surface, GetSurfaceUV(result.primitiveIndex, result.primitiveWeights), alpha);
// Stop if there is no light left
if (alpha <= 0.0)
return 0.0;
// Move to surface hit point
origin += direction * result.t;
dist -= result.t;
// Move through the portal, if any
TransformRay(surface.PortalIndex, origin, direction);
}
return 0.0;
}
float traceHit(vec3 origin, vec3 direction, float dist)
{
#if defined(USE_RAYTRACE_PRECISE)
return TraceDynLightRay(origin, 0.01f, direction, dist);
#else
return TraceAnyHit(origin, 0.01f, direction, dist) ? 0.0 : 1.0;
#endif
}
float traceShadow(vec3 lightpos, float softShadowRadius)
{
vec3 origin = pixelpos.xyz + vWorldNormal.xyz;
vec3 target = lightpos.xyz + 0.01; // nudge light position slightly as Doom maps tend to have their lights perfectly aligned with planes
vec3 direction = normalize(target - origin);
float dist = distance(origin, target);
#if SHADOWMAP_FILTER == 0
return traceHit(origin, direction, dist);
#else
if (softShadowRadius == 0)
{
return traceHit(origin, direction, dist);
}
else
{
vec3 v = (abs(direction.x) > abs(direction.y)) ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
vec3 xdir = normalize(cross(direction, v));
vec3 ydir = cross(direction, xdir);
float sum = 0.0;
const int step_count = SHADOWMAP_FILTER * 4;
for (int i = 0; i < step_count; i++)
{
vec2 gridoffset = getVogelDiskSample(i, step_count, gl_FragCoord.x + gl_FragCoord.y * 13.37) * softShadowRadius;
vec3 pos = target + xdir * gridoffset.x + ydir * gridoffset.y;
sum += traceHit(origin, normalize(pos - origin), dist);
}
return (sum / step_count);
}
#endif
}
#include <shaders/scene/light_trace.glsl>
float shadowAttenuation(vec3 lightpos, int shadowIndex, float softShadowRadius)
{

View file

@ -0,0 +1,79 @@
#include <shaders/lightmap/polyfill_rayquery.glsl>
#include <shaders/lightmap/trace_levelmesh.glsl>
#include <shaders/lightmap/montecarlo.glsl>
float TraceDynLightRay(vec3 origin, float tmin, vec3 direction, float dist)
{
float alpha = 1.0;
for (int i = 0; i < 3; i++)
{
TraceResult result = TraceFirstHit(origin, tmin, direction, dist);
// Stop if we hit nothing - the point light is visible.
if (result.primitiveIndex == -1)
return alpha;
SurfaceInfo surface = GetSurface(result.primitiveIndex);
// Pass through surface texture
alpha = PassRayThroughSurfaceDynLight(surface, GetSurfaceUV(result.primitiveIndex, result.primitiveWeights), alpha);
// Stop if there is no light left
if (alpha <= 0.0)
return 0.0;
// Move to surface hit point
origin += direction * result.t;
dist -= result.t;
// Move through the portal, if any
TransformRay(surface.PortalIndex, origin, direction);
}
return 0.0;
}
float traceHit(vec3 origin, vec3 direction, float dist)
{
#if defined(USE_RAYTRACE_PRECISE)
return TraceDynLightRay(origin, 0.01f, direction, dist);
#else
return TraceAnyHit(origin, 0.01f, direction, dist) ? 0.0 : 1.0;
#endif
}
float traceShadow(vec3 lightpos, float softShadowRadius)
{
vec3 origin = pixelpos.xyz + vWorldNormal.xyz;
vec3 target = lightpos.xyz + 0.01; // nudge light position slightly as Doom maps tend to have their lights perfectly aligned with planes
vec3 direction = normalize(target - origin);
float dist = distance(origin, target);
#if SHADOWMAP_FILTER == 0
return traceHit(origin, direction, dist);
#else
if (softShadowRadius == 0)
{
return traceHit(origin, direction, dist);
}
else
{
vec3 v = (abs(direction.x) > abs(direction.y)) ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
vec3 xdir = normalize(cross(direction, v));
vec3 ydir = cross(direction, xdir);
float sum = 0.0;
const int step_count = SHADOWMAP_FILTER * 4;
for (int i = 0; i < step_count; i++)
{
vec2 gridoffset = getVogelDiskSample(i, step_count, gl_FragCoord.x + gl_FragCoord.y * 13.37) * softShadowRadius;
vec3 pos = target + xdir * gridoffset.x + ydir * gridoffset.y;
sum += traceHit(origin, normalize(pos - origin), dist);
}
return (sum / step_count);
}
#endif
}

View file

@ -44,6 +44,7 @@
vec4 dynlight = uDynLightColor;
vec3 normal = material.Normal;
#if !defined(SHADE_VERTEX)
if (uLightIndex >= 0)
{
ivec4 lightRange = getLightRange();
@ -63,6 +64,9 @@
}
}
}
#else
dynlight.rgb += vLightColor;
#endif
#ifdef LIGHT_BLEND_CLAMPED
@ -80,6 +84,7 @@
#endif
#if !defined(SHADE_VERTEX)
if (uLightIndex >= 0)
{
ivec4 lightRange = getLightRange();
@ -96,6 +101,7 @@
frag = clamp(frag + desaturate(addlight).rgb, 0.0, 1.0);
}
}
#endif
return frag;
}

View file

@ -1,6 +1,89 @@
#include "shaders/scene/bones.glsl"
#if defined(SHADE_VERTEX) && !defined(PBR) && !defined(SPECULAR) && !defined(SIMPLE)
#undef SHADOWMAP_FILTER
#define SHADOWMAP_FILTER 0
#include <shaders/scene/lightmodel_shared.glsl>
#include <shaders/scene/light_trace.glsl>
#include <shaders/scene/light_spot.glsl>
vec3 lightValue(DynLightInfo light)
{
float lightdistance = distance(light.pos.xyz, pixelpos.xyz);
if (light.radius < lightdistance)
return vec3(0.0); // Early out lights touching surface but not this fragment
vec3 lightdir = normalize(light.pos.xyz - pixelpos.xyz);
float dotprod = dot(vWorldNormal.xyz, lightdir);
float attenuation = distanceAttenuation(lightdistance, light.radius, light.strength, light.linearity);
if ((light.flags & LIGHTINFO_SPOT) != 0)
{
attenuation *= spotLightAttenuation(light.pos.xyz, light.spotDir.xyz, light.spotInnerAngle, light.spotOuterAngle);
}
if ((light.flags & LIGHTINFO_ATTENUATED) != 0)
{
attenuation *= clamp(dotprod, 0.0, 1.0);
}
if (attenuation > 0.0) // Skip shadow map test if possible
{
#ifdef USE_RAYTRACE
// light.radius >= 1000000.0 is sunlight(?), skip attenuation
if(light.radius < 1000000.0 && (light.flags & LIGHTINFO_SHADOWMAPPED) != 0)
{
attenuation *= traceShadow(light.pos.xyz, light.softShadowRadius);
}
#endif
return light.color.rgb * attenuation;
}
else
{
return vec3(0.0);
}
return vec3(0.0);
}
vec3 ProcessVertexLight()
{
#if defined(USE_LEVELMESH)
const int lightTileSize = 1;
uLightIndex = int(uint(gl_FragCoord.x) / 64 + uint(gl_FragCoord.y) / 64 * uLightTilesWidth) * lightTileSize;
#endif
vec3 light = vec3(0.0);
if (uLightIndex >= 0)
{
ivec4 lightRange = getLightRange();
if (lightRange.z > lightRange.x)
{
// modulated lights
for(int i=lightRange.x; i<lightRange.y; i++)
{
light += lightValue(getLights()[i]);
}
// subtractive lights
for(int i=lightRange.y; i<lightRange.z; i++)
{
light -= lightValue(getLights()[i]);
}
}
}
return light;
}
#endif
void ModifyVertex();
void main()
@ -42,23 +125,25 @@ void main()
pixelpos.xyz = worldcoord.xyz;
pixelpos.w = -eyeCoordPos.z/eyeCoordPos.w;
if (uGlowTopColor.a > 0 || uGlowBottomColor.a > 0)
{
float topatpoint = (uGlowTopPlane.w + uGlowTopPlane.x * worldcoord.x + uGlowTopPlane.y * worldcoord.z) * uGlowTopPlane.z;
float bottomatpoint = (uGlowBottomPlane.w + uGlowBottomPlane.x * worldcoord.x + uGlowBottomPlane.y * worldcoord.z) * uGlowBottomPlane.z;
glowdist.x = topatpoint - worldcoord.y;
glowdist.y = worldcoord.y - bottomatpoint;
glowdist.z = clamp(glowdist.x / (topatpoint - bottomatpoint), 0.0, 1.0);
}
#if !defined(SIMPLE)
if (uGlowTopColor.a > 0 || uGlowBottomColor.a > 0)
{
float topatpoint = (uGlowTopPlane.w + uGlowTopPlane.x * worldcoord.x + uGlowTopPlane.y * worldcoord.z) * uGlowTopPlane.z;
float bottomatpoint = (uGlowBottomPlane.w + uGlowBottomPlane.x * worldcoord.x + uGlowBottomPlane.y * worldcoord.z) * uGlowBottomPlane.z;
glowdist.x = topatpoint - worldcoord.y;
glowdist.y = worldcoord.y - bottomatpoint;
glowdist.z = clamp(glowdist.x / (topatpoint - bottomatpoint), 0.0, 1.0);
}
if (uObjectColor2.a != 0)
{
float topatpoint = (uGradientTopPlane.w + uGradientTopPlane.x * worldcoord.x + uGradientTopPlane.y * worldcoord.z) * uGradientTopPlane.z;
float bottomatpoint = (uGradientBottomPlane.w + uGradientBottomPlane.x * worldcoord.x + uGradientBottomPlane.y * worldcoord.z) * uGradientBottomPlane.z;
gradientdist.x = topatpoint - worldcoord.y;
gradientdist.y = worldcoord.y - bottomatpoint;
gradientdist.z = clamp(gradientdist.x / (topatpoint - bottomatpoint), 0.0, 1.0);
}
if (uObjectColor2.a != 0)
{
float topatpoint = (uGradientTopPlane.w + uGradientTopPlane.x * worldcoord.x + uGradientTopPlane.y * worldcoord.z) * uGradientTopPlane.z;
float bottomatpoint = (uGradientBottomPlane.w + uGradientBottomPlane.x * worldcoord.x + uGradientBottomPlane.y * worldcoord.z) * uGradientBottomPlane.z;
gradientdist.x = topatpoint - worldcoord.y;
gradientdist.y = worldcoord.y - bottomatpoint;
gradientdist.z = clamp(gradientdist.x / (topatpoint - bottomatpoint), 0.0, 1.0);
}
#endif
if (uSplitBottomPlane.z != 0.0)
{
@ -123,5 +208,9 @@ void main()
gl_PointSize = 1.0;
#if defined(SHADE_VERTEX) && !defined(PBR) && !defined(SPECULAR) && !defined(SIMPLE)
vLightColor = ProcessVertexLight();
#endif
ModifyVertex();
}