277 lines
No EOL
8.7 KiB
GLSL
277 lines
No EOL
8.7 KiB
GLSL
#version 450
|
|
#extension GL_EXT_scalar_block_layout: require
|
|
|
|
// CREDITS: Most of the functions here have been obtained from this link: https://github.com/SaschaWillems/Vulkan
|
|
// developed by Sascha Willems, https://twitter.com/JoeyDeVriez, and licensed under the terms of the MIT License (MIT)
|
|
|
|
layout (constant_id = 0) const int SHADOW_MAP_CASCADE_COUNT = 6;
|
|
layout (constant_id = 1) const int DEBUG_SHADOWS = 0;
|
|
const float PI = 3.14159265359;
|
|
|
|
struct Light {
|
|
vec3 position;
|
|
uint directional;
|
|
float intensity;
|
|
vec3 color;
|
|
};
|
|
struct CascadeShadow {
|
|
mat4 projViewMatrix;
|
|
vec4 splitDistance;
|
|
};
|
|
|
|
layout(location = 0) in vec2 inTextCoord;
|
|
|
|
layout(location = 0) out vec4 outFragColor;
|
|
|
|
layout(set = 0, binding = 0) uniform sampler2D posSampler;
|
|
layout(set = 0, binding = 1) uniform sampler2D albedoSampler;
|
|
layout(set = 0, binding = 2) uniform sampler2D normalsSampler;
|
|
layout(set = 0, binding = 3) uniform sampler2D pbrSampler;
|
|
layout(set = 0, binding = 4) uniform sampler2D emissiveSampler;
|
|
layout(set = 0, binding = 5) uniform sampler2D TranslucencySampler;
|
|
layout(set = 0, binding = 6) uniform sampler2D OpacitySampler;
|
|
layout(set = 0, binding = 7) uniform sampler2D viewPos;
|
|
layout(set = 0, binding = 8) uniform sampler2D ssaoBlur;
|
|
layout(set = 0, binding = 9) uniform sampler2DArray shadowSampler;
|
|
|
|
|
|
layout(scalar, set = 1, binding = 0) readonly buffer Lights {
|
|
Light lights[];
|
|
} lights;
|
|
layout(set = 2, binding = 0) readonly buffer Shadows {
|
|
CascadeShadow cascadeshadows[];
|
|
} shadows;
|
|
layout(scalar, set = 3, binding = 0) uniform SceneInfo {
|
|
vec3 camPos;
|
|
float ambientLightIntensity;
|
|
vec3 ambientLightColor;
|
|
uint numLights;
|
|
mat4 viewMatrix;
|
|
} sceneInfo;
|
|
|
|
float chebyshevUpperBound(vec2 moments, float t) {
|
|
// Surface is fully lit if the current fragment is before the light occluder
|
|
if (t <= moments.x)
|
|
return 1.0;
|
|
|
|
// Compute variance
|
|
float variance = moments.y - (moments.x * moments.x);
|
|
variance = max(variance, 0.00002); // Small epsilon to avoid divide by zero
|
|
|
|
// Compute probabilistic upper bound
|
|
float d = t - moments.x;
|
|
float p_max = variance / (variance + d * d);
|
|
|
|
// Reduce light bleeding
|
|
p_max = smoothstep(0.5, 1.0, p_max);
|
|
|
|
return p_max;
|
|
}
|
|
|
|
float calcVisibility(vec4 worldPosition, uint cascadeIndex, float ShadowBias, vec2 texelSize) {
|
|
vec4 shadowMapPosition = shadows.cascadeshadows[cascadeIndex].projViewMatrix * worldPosition;
|
|
|
|
shadowMapPosition.xyz /= shadowMapPosition.w;
|
|
|
|
vec2 uv = vec2(
|
|
shadowMapPosition.x * 0.5 + 0.5,
|
|
shadowMapPosition.y * -0.5 + 0.5
|
|
);
|
|
|
|
float depth = shadowMapPosition.z;
|
|
|
|
if (uv.x < 0.0 || uv.x > 1.0 ||
|
|
uv.y < 0.0 || uv.y > 1.0 ||
|
|
depth < ShadowBias || depth > 1.0) {
|
|
return 1.0;
|
|
}
|
|
|
|
float shadow = 0.0;
|
|
|
|
for(int x = -1; x <= 1; ++x)
|
|
{
|
|
for(int y = -1; y <= 1; ++y)
|
|
{
|
|
vec2 moments = texture(shadowSampler, vec3((uv + vec2(x, y) * texelSize), cascadeIndex)).rg;
|
|
float visibility = chebyshevUpperBound(moments, depth);
|
|
shadow += visibility;
|
|
}
|
|
}
|
|
shadow /= 9.0;
|
|
|
|
return shadow;
|
|
}
|
|
|
|
float distributionGGX(vec3 N, vec3 H, float roughness) {
|
|
float a = roughness * roughness;
|
|
float a2 = a * a;
|
|
float NdotH = max(dot(N, H), 0.0);
|
|
float NdotH2 = NdotH * NdotH;
|
|
|
|
float nom = a2;
|
|
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
|
|
denom = PI * denom * denom;
|
|
|
|
return nom / denom;
|
|
}
|
|
|
|
float geometrySchlickGGX(float NdotV, float roughness) {
|
|
float r = (roughness + 1.0);
|
|
float k = (r * r) / 8.0;
|
|
|
|
float nom = NdotV;
|
|
float denom = NdotV * (1.0 - k) + k;
|
|
|
|
return nom / denom;
|
|
}
|
|
|
|
float geometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
|
|
float NdotV = max(dot(N, V), 0.0);
|
|
float NdotL = max(dot(N, L), 0.0);
|
|
float ggx2 = geometrySchlickGGX(NdotV, roughness);
|
|
float ggx1 = geometrySchlickGGX(NdotL, roughness);
|
|
|
|
return ggx1 * ggx2;
|
|
}
|
|
|
|
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
|
|
return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
|
|
}
|
|
|
|
vec3 calculatePointLight(Light light, vec3 worldPos, vec3 V, vec3 N, vec3 F0, vec3 albedo, float metallic, float roughness) {
|
|
vec3 tmpSub = light.position - worldPos;
|
|
vec3 L = normalize(tmpSub);
|
|
vec3 H = normalize(V + L);
|
|
|
|
// Calculate distance and attenuation
|
|
float distance = length(tmpSub);
|
|
float attenuation = 1.0 / (distance * distance);
|
|
float intensity = 10.0f;
|
|
vec3 radiance = light.color * light.intensity * attenuation;
|
|
|
|
// Cook-Torrance BRDF
|
|
float NDF = distributionGGX(N, H, roughness);
|
|
float G = geometrySmith(N, V, L, roughness);
|
|
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
|
|
|
|
vec3 numerator = NDF * G * F;
|
|
float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001;
|
|
vec3 specular = numerator / denominator;
|
|
|
|
vec3 kS = F;
|
|
vec3 kD = vec3(1.0) - kS;
|
|
kD *= 1.0 - metallic;
|
|
|
|
float NdotL = max(dot(N, L), 0.0);
|
|
return (kD * albedo / PI + specular) * radiance * NdotL;
|
|
}
|
|
|
|
vec3 calculateDirectionalLight(Light light, vec3 V, vec3 N, vec3 F0, vec3 albedo, float metallic, float roughness, float translucency) {
|
|
vec3 L = normalize(-light.position);
|
|
vec3 H = normalize(V + L);
|
|
|
|
vec3 radiance = light.color * light.intensity;
|
|
|
|
// Cook-Torrance BRDF
|
|
float NDF = distributionGGX(N, H, roughness);
|
|
float G = geometrySmith(N, V, L, roughness);
|
|
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
|
|
|
|
vec3 numerator = NDF * G * F;
|
|
float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001;
|
|
vec3 specular = numerator / denominator;
|
|
|
|
vec3 kS = F;
|
|
vec3 kD = vec3(1.0) - kS;
|
|
kD *= 1.0 - metallic;
|
|
|
|
float NdotL = max(dot(N, L), 0.0);
|
|
vec3 shadowValue = (kD * albedo / PI + specular) * radiance * NdotL;
|
|
vec3 lightValue = (kD * albedo / PI + specular) * radiance;
|
|
return (shadowValue * (1 - translucency)) + (lightValue * translucency);
|
|
}
|
|
|
|
void main() {
|
|
vec3 albedo = texture(albedoSampler, inTextCoord).rgb;
|
|
vec4 normalW = texture(normalsSampler, inTextCoord);
|
|
vec3 normal = normalW.rgb;
|
|
vec4 worldPosW = texture(posSampler, inTextCoord);
|
|
vec3 worldPos = worldPosW.xyz;
|
|
vec4 pbrW = texture(pbrSampler, inTextCoord);
|
|
vec3 pbr = pbrW.rgb;
|
|
vec3 emissive = texture(emissiveSampler, inTextCoord).rgb;
|
|
vec3 Opacity = texture(OpacitySampler, inTextCoord).rgb;
|
|
vec3 translucency = texture(TranslucencySampler, inTextCoord).rgb;
|
|
float Reflectivity = pbrW.a;
|
|
float Refractivity = normalW.a;
|
|
|
|
float emissiveness = emissive.r;
|
|
float translucencyf = translucency.r;
|
|
|
|
float ssao = texture(ssaoBlur, inTextCoord).r;
|
|
// outFragColor = vec4(emissive,1);
|
|
// return;
|
|
|
|
float ShadowBias = 0.05f;
|
|
|
|
float opacityf = Opacity.x + Opacity.y + Opacity.z;
|
|
opacityf = opacityf/3.0;
|
|
|
|
float roughness = pbr.g;
|
|
float metallic = pbr.b;
|
|
|
|
vec3 N = normalize(normal);
|
|
vec3 V = normalize(sceneInfo.camPos - worldPos);
|
|
|
|
|
|
|
|
vec3 F0 = vec3(0.04);
|
|
F0 = mix(F0, albedo, metallic);
|
|
|
|
uint cascadeIndex = 0;
|
|
vec4 viewPos = sceneInfo.viewMatrix * worldPosW;
|
|
for (uint i = 0; i < SHADOW_MAP_CASCADE_COUNT - 1; ++i) {
|
|
if (viewPos.z < shadows.cascadeshadows[i].splitDistance.x) {
|
|
cascadeIndex = i + 1;
|
|
}
|
|
}
|
|
|
|
vec2 texelSizeShadow = 1.0 / textureSize(shadowSampler, 0).rg;
|
|
|
|
float shadow = calcVisibility(vec4(worldPos, 1), cascadeIndex,ShadowBias, texelSizeShadow);
|
|
|
|
vec3 Lo = vec3(0.0);
|
|
for (uint i = 0; i < sceneInfo.numLights; i++) {
|
|
Light light = lights.lights[i];
|
|
if (light.directional == 1) {
|
|
Lo += calculateDirectionalLight(light, V, N, F0, albedo, metallic, roughness,translucencyf) * shadow;
|
|
} else {
|
|
Lo += calculatePointLight(light, worldPos, V, N, F0, albedo, metallic, roughness);
|
|
}
|
|
}
|
|
vec3 ambient = sceneInfo.ambientLightColor * albedo * sceneInfo.ambientLightIntensity * vec3(ssao,ssao,ssao);
|
|
if(emissive.x > 0 || emissive.y > 0 || emissive.z > 0) ambient = emissive;
|
|
outFragColor = vec4(Lo + ambient, 1.0f);
|
|
outFragColor = vec4(outFragColor.xyz/2 + (outFragColor.xyz/2) * vec3(ssao,ssao,ssao),1);
|
|
|
|
if (DEBUG_SHADOWS == 1) {
|
|
switch (cascadeIndex) {
|
|
case 0:
|
|
outFragColor.rgb *= vec3(1.0f, 0.25f, 0.25f);
|
|
break;
|
|
case 1:
|
|
outFragColor.rgb *= vec3(0.25f, 1.0f, 0.25f);
|
|
break;
|
|
case 2:
|
|
outFragColor.rgb *= vec3(0.25f, 0.25f, 1.0f);
|
|
break;
|
|
default:
|
|
outFragColor.rgb *= vec3(1.0f, 1.0f, 0.25f);
|
|
break;
|
|
}
|
|
}
|
|
if (length(normal) < 0.001) {
|
|
outFragColor = vec4(albedo,1.0f);
|
|
return;
|
|
}
|
|
} |