ToyEngine/Exp1/Engine/Shaders/pbr.comp

241 lines
7.3 KiB
Plaintext

#version 450 core
layout (local_size_x = 8, local_size_y = 8) in;
layout(rgba8, binding = 0) uniform image2D gBaseColor;
uniform sampler2D gNormal;
uniform sampler2D gPosition;
uniform sampler2D gMetallicRoughness;
uniform sampler2DArray gShadowMap;
uniform samplerCube irradianceMap;
uniform samplerCube prefilterMap;
uniform sampler2D brdfLUT;
layout (std140, binding = 0) uniform LightSpaceMatrices
{
mat4 lightSpaceMatrices[16];
};
uniform mat4 view;
uniform float farPlane;
uniform float shadowCascadePlaneDistances[16];
uniform float shadowBiases[16];
uniform int shadowCascadeCount;
uniform float shadowBlendRatio;
uniform vec3 mainLightDirection;
uniform vec3 mainLightRadiance;
uniform vec3 camPos;
const float PI = 3.14159265359;
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(1.0 - cosTheta, 5.0);
}
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness)
{
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
}
vec3 ambientLighting(vec3 N, vec3 V, vec3 F0, vec3 albedo, float metallic, float roughness, float ao)
{
// ambient lighting (we now use IBL as the ambient term)
vec3 F = fresnelSchlickRoughness(clamp(dot(N, V),0.,1.), F0, roughness);
vec3 kS = F;
vec3 kD = 1.0 - kS;
kD *= 1.0 - metallic;
vec3 irradiance = texture(irradianceMap, N).rgb;
vec3 diffuse = irradiance * albedo;
// sample both the pre-filter map and the BRDF lut and combine them together as per the Split-Sum approximation to get the IBL specular part.
const float MAX_REFLECTION_LOD = 4.0;
vec3 R = reflect(-V, N);
vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * MAX_REFLECTION_LOD).rgb;
vec2 brdf = texture(brdfLUT, vec2(clamp(dot(N, V),0.,1.), roughness)).rg;
vec3 specular = prefilteredColor * (F * brdf.x + brdf.y);
return (kD * diffuse + specular) * ao;
}
const int pcfRadius = 3;
float getShadowFromLayer(vec3 fragPosWorldSpace, vec3 normal,int layer)
{
float normalBias = 1.2*4. /** (1+pcfRadius)*/ * shadowBiases[layer]*max((1.0 - dot(normal, mainLightDirection)), 0.1)/textureSize(gShadowMap, 0).x;
normalBias = max(normalBias, 0.05);
vec4 fragPosLightSpace = lightSpaceMatrices[layer] * vec4(fragPosWorldSpace+normal*normalBias, 1.0);
// perform perspective divide
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
// transform to [0,1] range
projCoords = projCoords * 0.5 + 0.5;
// get depth of current fragment from light's perspective
float currentDepth = projCoords.z;
// keep the shadow at 0.0 when outside the far_plane region of the light's frustum.
if (currentDepth > 1.0)
{
return 0.0;
}
// PCF
float shadow = 0.0;
vec2 texelSize = 1.0 / vec2(textureSize(gShadowMap, 0));
for(int x = -pcfRadius; x <= pcfRadius; ++x)
{
for(int y = -pcfRadius; y <= pcfRadius; ++y)
{
float pcfDepth = texture(gShadowMap, vec3(projCoords.xy + vec2(x, y) * texelSize, layer)).r;
shadow += currentDepth > pcfDepth ? 1.0 : 0.0;
}
}
shadow /= (2*pcfRadius+1)*(2*pcfRadius+1);
return shadow;
}
float ShadowCalculation(vec3 fragPosWorldSpace, vec3 normal, out int layer)
{
// select cascade layer
vec4 fragPosViewSpace = view * vec4(fragPosWorldSpace, 1.0);
float depthValue = abs(fragPosViewSpace.z);
layer = -1;
for (int i = 0; i < shadowCascadeCount; ++i)
{
if (depthValue < shadowCascadePlaneDistances[i])
{
layer = i;
break;
}
}
// if (layer == -1)
// {
// layer = shadowCascadeCount;
// }
float shadow = getShadowFromLayer(fragPosWorldSpace,normal,layer);
float nextLayerBeginDepth = layer==0? (1-shadowBlendRatio)*shadowCascadePlaneDistances[layer]
:(1-shadowBlendRatio)*shadowCascadePlaneDistances[layer]+shadowBlendRatio*shadowCascadePlaneDistances[layer-1];
if(depthValue > nextLayerBeginDepth)
{
float shadowNext = getShadowFromLayer(fragPosWorldSpace,normal,layer+1);
shadow = mix(shadow,shadowNext, (depthValue-nextLayerBeginDepth)/(shadowCascadePlaneDistances[layer]-nextLayerBeginDepth));
}
return shadow;
}
vec4 encodeRGBM(vec3 color)
{
if(dot(color,color)==0)
return vec4(0,0,0,1);
vec4 rgbm;
float range = 8;
color /= range;
rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 1e-6)), 0.0, 1.0);
rgbm.a = ceil(rgbm.a * 255.0) / 255.0;
rgbm.rgb = color / rgbm.a;
return rgbm;
}
void main()
{
ivec2 pixelLocation = ivec2(gl_GlobalInvocationID.xy);
//vec3 albedo = pow(texelFetch(gBaseColor, pixelLocation, 0).rgb, vec3(2.2));
vec3 albedo = pow(imageLoad(gBaseColor, pixelLocation).rgb, vec3(2.2));
float metallic = texelFetch(gMetallicRoughness, pixelLocation, 0).r;
float roughness = texelFetch(gMetallicRoughness, pixelLocation, 0).g;
vec3 worldPos = texelFetch(gPosition, pixelLocation,0).xyz;
vec3 normal = texelFetch(gNormal, pixelLocation,0).xyz;
if(normal==vec3(0))
{
//vec3 color = mainLightRadiance;
//imageStore(gBaseColor, pixelLocation, vec4(color, 1.0));
imageStore(gBaseColor, pixelLocation, vec4(0));
return;
}
normal = normalize(normal);
vec3 V = normalize(camPos - worldPos);
vec3 F0 = vec3(0.04);
F0 = mix(F0, albedo, metallic);
// calculate light radiance
vec3 L = normalize(mainLightDirection);
vec3 H = normalize(V + L);
//float distance = length(lightPositions[i] - WorldPos);
//float attenuation = 1.0 / (distance * distance);
vec3 radiance = mainLightRadiance ;//* attenuation;
// cook-torrance brdf
float NDF = DistributionGGX(normal, H, roughness);
float G = GeometrySmith(normal, V, L, roughness);
vec3 F = fresnelSchlick(clamp(dot(H, V),0.,1.), F0);
//F = clamp(F,vec3(0),vec3(1));
vec3 kS = F;
vec3 kD = vec3(1.0) - kS;
kD *= 1.0 - metallic;
vec3 nominator = NDF * G * F;
float denominator = 4.0 * max(dot(normal, V), 0.0) * max(dot(normal, L), 0.0) + 0.001;
vec3 specular = nominator / denominator;
float NdotL = max(dot(normal, L), 0.0);
vec3 Lo = (kD * albedo / PI + specular) * radiance * NdotL;
vec3 ambient = ambientLighting(normal, V, F0, albedo, metallic, roughness, 1);
int debugLayer;
float shadow = ShadowCalculation(worldPos, normal, debugLayer);
vec3 color = (1-shadow)*Lo + ambient;
imageStore(gBaseColor, pixelLocation, encodeRGBM(color));
}