Skip to content

Instantly share code, notes, and snippets.

@qoh
Last active May 11, 2020 04:50
Show Gist options
  • Save qoh/6729709 to your computer and use it in GitHub Desktop.
Save qoh/6729709 to your computer and use it in GitHub Desktop.
Replace shaders/renderCsm_frag.glsl with this and re-apply your shader settings. Read notes in the code before complaining.
#version 120
#extension GL_EXT_texture_array : require
#extension GL_EXT_texture_array : enable
// NOTE: this is a development release - expect tons of bugs
// this /will/ decrease your fps roughly (soft_distance*2/soft_stepsize+1)³ times,
// depends on cost of individual operations
// soft_distance is the space to spread out shadow blurring samples over
// soft_stepsize controls how close each sample is
const float soft_distance = 0.05f;
const float soft_stepsize = 0.025f;
// Varying.
varying vec4 vPos;
varying vec3 worldNormal;
varying vec3 worldPos;
// Global directional light uniforms.
uniform vec4 dirLightDir;
uniform vec4 dirLightColor;
uniform vec4 dirLightAmbient;
uniform vec4 dirShadowColor;
// Misc uniforms.
uniform vec3 camPos;
uniform mat4 obj2World;
uniform mat4 world2Cam;
uniform int isParticle;
uniform int doColorMultiply;
uniform int glow;
uniform sampler2DArray stex;
uniform sampler2D tex;
// Surface calculations, including specular power.
varying vec2 texCoord;
vec4 viewDelta;
float specular;
float NdotL;
vec3 reflectVec;
void calculateSurface(vec4 color, inout vec4 albedo)
{
viewDelta.xyz = worldPos - camPos;
viewDelta.w = length(viewDelta.xyz);
viewDelta.xyz = -normalize(viewDelta.xyz);
vec4 texAlbedo = texture2D(tex, texCoord);
albedo.rgb = mix(color.rgb, texAlbedo.rgb, texAlbedo.a);
if(doColorMultiply == 1)
albedo *= gl_Color;
albedo.a = color.a;
NdotL = max(dot(worldNormal, dirLightDir.xyz), 0.0f);
reflectVec = normalize(reflect(-dirLightDir.xyz, worldNormal));
specular = pow(max(dot(reflectVec, viewDelta.xyz), 0.0f), 12.0f) * length(texAlbedo.rgb);
//albedo.rgb = normalize(viewDelta.xyz);
}
// Fogging.
uniform vec4 fogBaseColor;
uniform vec4 fogConsts;
uniform sampler2D fogTex;
varying vec2 fogCoords;
void applyFog(inout vec4 albedo)
{
// Calculate fog.
vec4 fogColor = texture2D(fogTex, fogCoords) * fogBaseColor;
// Blend it.
albedo = mix(albedo, fogColor, fogColor.a);
}
// Shadowing
uniform vec4 far_d;
uniform vec2 texSize; // x - size, y - 1/size
uniform vec4 zScale;
uniform int shadowSplitCount;
void calculateShadowCoords(in vec3 offset, inout vec4 shadow_coordA, inout vec4 shadow_coordB, out float blend)
{
vec4 rPos = vPos + vec4(offset, 0.0f);
int index = 3;
// NOTE: fudgeKey controls the shadow acne <-> peter panning factor
// make this as precise as possible, a too low or too high value /will/ be terribly ugly
// default value is 0.1f, tested values are 0.55f, 0.175f (seems good for now)
float fudgeKey = 0.175f;
float fudgeFactorA = 0.0f;
float fudgeFactorB = 0.0f;
fudgeFactorA = 4.0f * fudgeKey / zScale.w;
fudgeFactorB = 4.0f * fudgeKey / zScale.w;
blend = 0.0f;
// find the appropriate depth map to look up in based on the depth of this fragment
if(rPos.y < far_d.x)
{
index = 0;
if(shadowSplitCount > 1)
blend = clamp( (rPos.y - (far_d.x * 0.9f)) / (far_d.x * 0.1f), 0.0f, 1.0f);
fudgeFactorA = fudgeKey / zScale.x;
fudgeFactorB = 2.0f * fudgeKey / zScale.y;
}
else if(rPos.y < far_d.y)
{
index = 1;
if(shadowSplitCount > 2)
blend = clamp( (rPos.y - (far_d.y * 0.9f)) / (far_d.x * 0.1f), 0.0f, 1.0f);
fudgeFactorA = 2.0f * fudgeKey / zScale.y;
fudgeFactorB = 3.0f * fudgeKey / zScale.z;
}
else if(rPos.y < far_d.z)
{
index = 2;
if(shadowSplitCount > 3)
blend = clamp( (rPos.y - (far_d.z * 0.9f)) / (far_d.x * 0.1f), 0.0f, 1.0f);
fudgeFactorA = 3.0f * fudgeKey / zScale.z;
fudgeFactorB = 4.0f * fudgeKey / zScale.w;
}
// transform this fragment's position from view space to scaled light clip space
// such that the xy coordinates are in [0;1]
// note there is no need to divide by w for orthogonal light sources
shadow_coordA = gl_TextureMatrix[index]*rPos;
shadow_coordA.w = shadow_coordA.z - fudgeFactorA; // Figure the input coordinate for PCF sampling if appropriate.
shadow_coordA.z = float(index); // Encode the layer to sample.
//don't have to set second shadow coord if we're not blending
if(blend > 0.0f)
{
shadow_coordB = gl_TextureMatrix[index + 1]*rPos;
shadow_coordB.w = shadow_coordB.z - fudgeFactorB;
shadow_coordB.z = float(index + 1);
}
}
// Point lighting
uniform vec4 pointLightPos0;
uniform vec4 pointLightColor0;
uniform float pointLightRadius0;
uniform vec4 pointLightPos1;
uniform vec4 pointLightColor1;
uniform float pointLightRadius1;
uniform vec4 pointLightPos2;
uniform vec4 pointLightColor2;
uniform float pointLightRadius2;
uniform vec4 pointLightPos3;
uniform vec4 pointLightColor3;
uniform float pointLightRadius3;
uniform vec4 pointLightPos4;
uniform vec4 pointLightColor4;
uniform float pointLightRadius4;
uniform vec4 pointLightPos5;
uniform vec4 pointLightColor5;
uniform float pointLightRadius5;
uniform vec4 pointLightPos6;
uniform vec4 pointLightColor6;
uniform float pointLightRadius6;
uniform vec4 pointLightPos7;
uniform vec4 pointLightColor7;
uniform float pointLightRadius7;
vec4 accumulatePointLights()
{
vec4 pointLightTotal = vec4(0.0f, 0.0f, 0.0f, 0.0f);
vec3 lightDelta = vec3(0.0f, 0.0f, 0.0f);
float lightDot = 0.0f;
float ratio = 0.0f;
// Calculate effects of the 8 point lights.
lightDelta = worldPos.xyz - pointLightPos0.xyz;
lightDot = max(dot(-normalize(lightDelta), worldNormal), 0.0f);
ratio = 1.0f - (length(lightDelta) / pointLightRadius0);
ratio = ratio * ratio * ratio * 0.4f;
ratio = max(ratio, 0.0f);
pointLightTotal.xyz += ratio * lightDot * pointLightColor0.xyz;
lightDelta = worldPos.xyz - pointLightPos1.xyz;
lightDot = max(dot(-normalize(lightDelta), worldNormal), 0.0f);
ratio = 1.0f - (length(lightDelta) / pointLightRadius1);
ratio = ratio * ratio * ratio * 0.4f;
ratio = max(ratio, 0.0f);
pointLightTotal.xyz += ratio * lightDot * pointLightColor1.xyz;
lightDelta = worldPos.xyz - pointLightPos2.xyz;
lightDot = max(dot(-normalize(lightDelta), worldNormal), 0.0f);
ratio = 1.0f - (length(lightDelta) / pointLightRadius2);
ratio = ratio * ratio * ratio * 0.4f;
ratio = max(ratio, 0.0f);
pointLightTotal.xyz += ratio * lightDot * pointLightColor2.xyz;
lightDelta = worldPos.xyz - pointLightPos3.xyz;
lightDot = max(dot(-normalize(lightDelta), worldNormal), 0.0f);
ratio = 1.0f - (length(lightDelta) / pointLightRadius3);
ratio = ratio * ratio * ratio * 0.4f;
ratio = max(ratio, 0.0f);
pointLightTotal.xyz += ratio * lightDot * pointLightColor3.xyz;
lightDelta = worldPos.xyz - pointLightPos4.xyz;
lightDot = max(dot(-normalize(lightDelta), worldNormal), 0.0f);
ratio = 1.0f - (length(lightDelta) / pointLightRadius4);
ratio = ratio * ratio * ratio * 0.4f;
ratio = max(ratio, 0.0f);
pointLightTotal.xyz += ratio * lightDot * pointLightColor4.xyz;
lightDelta = worldPos.xyz - pointLightPos5.xyz;
lightDot = max(dot(-normalize(lightDelta), worldNormal), 0.0f);
ratio = 1.0f - (length(lightDelta) / pointLightRadius5);
ratio = ratio * ratio * ratio * 0.4f;
ratio = max(ratio, 0.0f);
pointLightTotal.xyz += ratio * lightDot * pointLightColor5.xyz;
lightDelta = worldPos.xyz - pointLightPos6.xyz;
lightDot = max(dot(-normalize(lightDelta), worldNormal), 0.0f);
ratio = 1.0f - (length(lightDelta) / pointLightRadius6);
ratio = ratio * ratio * ratio * 0.4f;
ratio = max(ratio, 0.0f);
pointLightTotal.xyz += ratio * lightDot * pointLightColor6.xyz;
lightDelta = worldPos.xyz - pointLightPos7.xyz;
lightDot = max(dot(-normalize(lightDelta), worldNormal), 0.0f);
ratio = 1.0f - (length(lightDelta) / pointLightRadius7);
ratio = ratio * ratio * ratio * 0.4f;
ratio = max(ratio, 0.0f);
pointLightTotal.xyz += ratio * lightDot * pointLightColor7.xyz;
return pointLightTotal;
}
vec4 accumulateParticlePointLights()
{
vec4 pointLightTotal = vec4(0.0f, 0.0f, 0.0f, 0.0f);
vec3 lightDelta = vec3(0.0f, 0.0f, 0.0f);
float ratio = 0.0f;
// Calculate effects of the 8 point lights.
lightDelta = worldPos.xyz - pointLightPos0.xyz;
ratio = 1.0f - (length(lightDelta) / pointLightRadius0);
ratio = ratio * ratio * ratio * 0.4f;
ratio = max(ratio, 0.0f);
pointLightTotal.xyz += ratio * pointLightColor0.xyz;
lightDelta = worldPos.xyz - pointLightPos1.xyz;
ratio = 1.0f - (length(lightDelta) / pointLightRadius1);
ratio = ratio * ratio * ratio * 0.4f;
ratio = max(ratio, 0.0f);
pointLightTotal.xyz += ratio * pointLightColor1.xyz;
return pointLightTotal;
}
// Combine specular and direct lighting terms.
// note: if we make combinedColor "out" only, it throws a potentially uninitialized value warning, so we've made it inout
void applyLighting(inout vec4 combinedColor, vec4 albedo, float occlusionFactor)
{
//large normal means glowing object
if(glow == 1 || (worldNormal.x + worldNormal.y + worldNormal.z) > 2.0f)
{
combinedColor = albedo;
return;
}
vec4 dirLightSpecular = occlusionFactor * specular * dirLightColor;
dirLightSpecular *= 0.5f; //arbitrary adjustment
vec4 dirLightDirect = ((NdotL * dirLightColor) * occlusionFactor) + (dirLightAmbient * occlusionFactor) + (dirShadowColor * (1.0f - occlusionFactor));
if(NdotL <= 0.04f)
{
dirLightDirect = dirShadowColor;
dirLightSpecular = vec4(0.0f, 0.0f, 0.0f, 0.0f);
}
else if(NdotL <= 0.1)
{
float val = (NdotL - 0.04f) / (0.1f - 0.04f);
dirLightDirect = (dirLightDirect * val) + (dirShadowColor * (1.0f - val));
dirLightSpecular = dirLightSpecular * val;
}
dirLightDirect += accumulatePointLights();
dirLightSpecular.a = length(dirLightSpecular.rgb);
dirLightDirect.a *= min(occlusionFactor + 0.75f, 1.0f);
combinedColor.rgb = dirLightDirect.rgb * albedo.rgb;
combinedColor.a = albedo.a;
combinedColor += dirLightSpecular;
}
float doShadowCoef(vec3 offset) {
float blend = 0.0f;
vec4 shadow_coordA = vec4(0.0f, 0.0f, 0.0f, 0.0f);
vec4 shadow_coordB = vec4(0.0f, 0.0f, 0.0f, 0.0f);
calculateShadowCoords(offset, shadow_coordA, shadow_coordB, blend);
float shadowA = texture2DArray(stex, shadow_coordA.xyz).x; // get the stored depth
float diffA = shadowA - shadow_coordA.w; // get the difference of the stored depth and the distance of this fragment to the light
if(diffA > 0.0f)
diffA = 1.0f;
else
diffA = 0.0f;
if(blend > 0.0f)
{
float shadowB = texture2DArray(stex, shadow_coordB.xyz).x; // get the stored depth
float diffB = shadowB - shadow_coordB.w; // get the difference of the stored depth and the distance of this fragment to the light
if(diffB > 0.0f)
diffB = 1.0f;
else
diffB = 0.0f;
float total = (diffB * blend) + (diffA * (1.0f - blend));
total = clamp(total, 0.0f, 1.0f);
return total;
}
else
{
return diffA;
}
}
float shadowCoef()
{
// NOTE: uncomment to completely disable soft shadows
// return doShadowCoef(vec3(0.0f, 0.0f, 0.0f));
float total = 0.0f;
int samples = 0;
for (float x = -soft_distance; x <= soft_distance; x += soft_stepsize) {
for (float y = -soft_distance; y <= soft_distance; y += soft_stepsize) {
for (float z = -soft_distance; z <= soft_distance; z += soft_stepsize) {
total += doShadowCoef(vec3(x, y, z));
samples += 1;
}
}
}
return total / samples;
}
void main()
{
vec4 albedo = vec4(0.0f, 0.0f, 0.0f, 0.0f);
calculateSurface(gl_Color, albedo);
float occlusionFactor = 0.0f;
if(NdotL > -0.01f)
{
if(shadowSplitCount <= 0)
occlusionFactor = 1.0f;
else
occlusionFactor = shadowCoef();
}
// Apply lighting and fog.
vec4 fragColor = vec4(0.0f, 0.0f, 0.0f, 0.0f);
if(isParticle == 1)
{
vec4 texAlbedo = texture2D(tex, texCoord);
vec4 dirLightDirect = (dirLightColor * occlusionFactor) + (dirLightAmbient * occlusionFactor) + (dirShadowColor * (1.0f - occlusionFactor));
vec4 plt = accumulateParticlePointLights();
vec4 lightTotal = dirLightDirect + plt;
lightTotal.x = clamp(lightTotal.x, 0.0f, 1.2f);
lightTotal.y = clamp(lightTotal.y, 0.0f, 1.2f);
lightTotal.z = clamp(lightTotal.z, 0.0f, 1.2f);
fragColor = texAlbedo * gl_Color * lightTotal;
applyFog(fragColor);
fragColor.a = texAlbedo.a * gl_Color.a;
}
else
{
applyLighting(fragColor, albedo, occlusionFactor);
applyFog(fragColor);
}
// Uncomment to viz depth in B.
//fragColor.z = vPos.y * 0.01f;
gl_FragColor = fragColor;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment