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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 130 additions & 13 deletions assets/Shaders/default.frag
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,25 @@ struct Light
};
uniform Light uLight;

struct DynamicLight {
int type;
vec3 position;
vec3 direction;
vec4 color;
float range;
float rangeSquared;
float spotCutoff;
float power;
float exposure;
int isNormalize;
float radius;
int softFalloff;
float softness;
vec2 areaSize;
};
uniform int uDynamicLightCount;
uniform DynamicLight uDynamicLights[16];

// Inputs from vertex shader
in vec3 vNormal;
in vec4 vPosLightSpace0;
Expand Down Expand Up @@ -212,17 +231,7 @@ void main(void)
// Multiply material alpha by it's opacity
finalColor.a *= uOpacity;

/*
* NOTES:
* Unused alpha functions must not be added to the shader
* This has a nasty affect on framerates
*
* A switch case block is also ~30% slower than the else-if
*
* Numbers used are those from the GL.AlphaFunction enum to allow
* for direct casts
*/
if(uAlphaTest.x == 513) // Less
if(uAlphaTest.x == float(ALPHA_LESS)) // Less
{
if(finalColor.a >= uAlphaTest.y)
{
Expand Down Expand Up @@ -252,10 +261,118 @@ void main(void)
*/
float shadow = CalculateShadowFactor();

vec3 dynamicLightSum = vec3(0.0);
if ((uMaterialFlags & 1) == 0 && (uMaterialFlags & 4) == 0 && uDynamicLightCount > 0)
{
// Precalculated constants for faster lighting calculations
const float ONE_OVER_FOUR_PI = 0.0795774715; // 1.0 / (4.0 * PI)
const float TWO_PI = 6.283185307; // 2.0 * PI
const float FOUR_PI = 12.566370614; // 4.0 * PI
vec3 N = normalize(vNormal);
for (int i = 0; i < uDynamicLightCount; i++)
{
vec3 toLight = uDynamicLights[i].position - oViewPos.xyz;
float d2 = dot(toLight, toLight);
if (d2 <= uDynamicLights[i].rangeSquared)
{
float d = sqrt(d2);

// 1. Calculate Intensity: Power in Watts, Exposure
float intensity = uDynamicLights[i].power * exp2(uDynamicLights[i].exposure);
vec3 lightColor = uDynamicLights[i].color.rgb * intensity;

// 2. Attenuation: SoftFalloff, Radius
float denom = d2 + uDynamicLights[i].radius * uDynamicLights[i].radius;
float att = 1.0 / max(denom, 0.0001);
if (uDynamicLights[i].softFalloff != 0)
{
att *= clamp((uDynamicLights[i].range - d) / max(0.001, uDynamicLights[i].range * 0.2), 0.0, 1.0);
}

if (uDynamicLights[i].type == 2)
{
// AREA LIGHT
// Construct orthonormal coordinate basis from the light normal (direction)
vec3 normal = normalize(uDynamicLights[i].direction);
vec3 right = normalize(cross(normal, abs(normal.y) < 0.999 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, 0.0, 1.0)));
vec3 up = cross(right, normal);

vec2 halfSize = uDynamicLights[i].areaSize * 0.5;

// Calculate the four vertices of the rectangular light in view space
vec3 P[4];
P[0] = uDynamicLights[i].position - right * halfSize.x - up * halfSize.y;
P[1] = uDynamicLights[i].position + right * halfSize.x - up * halfSize.y;
P[2] = uDynamicLights[i].position + right * halfSize.x + up * halfSize.y;
P[3] = uDynamicLights[i].position - right * halfSize.x + up * halfSize.y;

// Integrate cosine-weighted illumination analytically using projected solid angle (boundary integration)
float irradiance = 0.0;
vec3 p[4];
for (int j = 0; j < 4; j++)
{
p[j] = normalize(P[j] - oViewPos.xyz);
}

for (int j = 0; j < 4; j++)
{
int next = (j + 1) % 4;
vec3 p1 = p[j];
vec3 p2 = p[next];

float cosTheta = clamp(dot(p1, p2), -1.0, 1.0);
float theta = acos(cosTheta);

vec3 crossP = cross(p1, p2);
float len = length(crossP);
if (len > 0.0001)
{
vec3 g = crossP / len;
irradiance += theta * dot(g, N);
}
}
irradiance = max(irradiance / TWO_PI, 0.0);

// Double-sided / backface check (light only emits forward)
float frontFacing = clamp(dot(N, normalize(toLight)), 0.0, 1.0);
irradiance *= frontFacing;

dynamicLightSum += lightColor * irradiance * att;
}
else
{
// POINT/SPOT LIGHT
vec3 L = toLight / d;
float solidAngle = TWO_PI * (1.0 - uDynamicLights[i].spotCutoff);
bool normalizeSpot = (uDynamicLights[i].type == 1 && uDynamicLights[i].isNormalize != 0);
float normalizedIntensity = intensity / (normalizeSpot ? max(solidAngle, 0.0001) : FOUR_PI);
vec3 normalizedLightColor = uDynamicLights[i].color.rgb * normalizedIntensity;

// Spot Cone Attenuation (Branchless)
vec3 lightToFrag = -L;
float spotDot = dot(lightToFrag, uDynamicLights[i].direction);
float outerCutoff = uDynamicLights[i].spotCutoff;

float softnessFactor = clamp(uDynamicLights[i].softness, 0.0, 1.0);
float innerCutoff = mix(1.0, outerCutoff, 1.0 - softnessFactor);

float intensityFactor = clamp((spotDot - outerCutoff) / max(innerCutoff - outerCutoff, 0.0001), 0.0, 1.0);
float spotAtt = smoothstep(0.0, 1.0, intensityFactor) * step(outerCutoff, spotDot);

att *= mix(1.0, spotAtt, float(uDynamicLights[i].type == 1));

float nDotL = abs(dot(N, L));
dynamicLightSum += normalizedLightColor * nDotL * att;
}
}
}
}

if ((uMaterialFlags & 1) == 0 && (uMaterialFlags & 4) == 0)
{
// Material is not emissive, apply shadow to the light factor
finalColor.rgb *= (oLightResult.rgb * shadow);
// Material is not emissive, apply shadow to the light factor and add dynamic lights
vec3 totalLight = oLightResult.rgb * shadow + dynamicLightSum;
finalColor.rgb *= totalLight;
finalColor.a *= oLightResult.a;
}
else
Expand Down
Loading