// G-BUFFER #ifndef UNLIT // - Position in view space uniform sampler2DRect positionBuffer; // - Albedo + Roughness uniform sampler2DRect albedoBuffer; // - Normal buffer uniform sampler2DRect normalBuffer; #endif // - Emission + Metallic uniform sampler2DRect emissionBuffer; // LIGHT ATTRIBUTES #ifdef AMBIENT_LIGHT uniform samplerCube ambientMap; uniform samplerCube reflectMap; uniform sampler2D brdfLUT; uniform mat3 inverseViewMatrix; #elif !defined UNLIT uniform vec3 lightColor; #endif #ifdef SHADOWMAP #ifdef POINT_LIGHT uniform samplerCube shadowMap; uniform mat3 inverseViewMatrix; #else uniform sampler2DShadow shadowMap; uniform mat4 viewToLightMatrix; #endif #endif #if defined POINT_LIGHT uniform vec3 pointLight; uniform float range; #elif defined DIRECTIONNAL_LIGHT uniform vec3 dirLight; #elif defined SPOT_LIGHT uniform vec3 pointLight; uniform float attenuation; uniform vec3 dirLight; uniform float cutoff; #endif // FRAGMENT POSITIONNING in vec2 screenPos; uniform mat4 inverseProjectionMatrix; // OUTPUT LIGHT layout(location = 0)out vec4 outColor; // CONSTANTS const float PI = 3.14159265359; const float MAX_REFLECTION_LOD = 4.0; // FUNCTIONS #ifdef AMBIENT_LIGHT vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) { return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); } vec3 GGX( in vec3 albedoColor, in float metallic, in float roughness, in vec3 N, in vec3 V) { float NdotV = max(dot(N, V), 0.0); vec3 R = reflect(-V, N); vec3 albedo = pow(albedoColor, vec3(2.2)); // calculate reflectance at normal incidence; if dia-electric (like plastic) use F0 // of 0.04 and if it's a metal, use their albedo color as F0 (metallic workflow) vec3 F0 = vec3(0.04); F0 = mix(F0, albedo, metallic); // ambient lighting (we now use IBL as the ambient term) (fresnelSchlickRoughness function) vec3 F = fresnelSchlickRoughness(NdotV, F0, roughness); vec3 kS = F; vec3 kD = 1.0 - kS; kD *= 1.0 - metallic; mat3 viewToWorld = inverseViewMatrix; vec3 irradiance = texture(ambientMap, viewToWorld * 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. vec3 prefilteredColor = textureLod(reflectMap, viewToWorld * R, roughness * MAX_REFLECTION_LOD).rgb; vec2 brdf2 = texture(brdfLUT, vec2(NdotV, roughness)).rg; vec3 specular = prefilteredColor * (F * brdf2.x + brdf2.y); // combining light return kD * diffuse + specular; } #elif !defined UNLIT 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 denom = (NdotH * NdotH * (a2 - 1.0) + 1.0); denom = PI * denom * denom; return a2 / denom; } float GeometrySchlickGGX(float cosAlpha, float roughness) { float r = (roughness + 1.0); float k = (r*r) / 8.0; return cosAlpha / (cosAlpha * (1.0 - k) + k); } vec3 GGX( in vec3 albedoColor, in float metallic, in float roughness, in vec3 radiance, in vec3 N, in vec3 L, in vec3 H, in vec3 V) { vec3 albedo = pow(albedoColor, vec3(2.2)); vec3 F0 = mix(vec3(0.04), albedo, metallic); float NdotL = max(dot(N, L), 0.0); float NdotV = max(dot(N, V), 0.0); // Cook-Torrance BRDF float NDF = DistributionGGX(N, H, roughness); float G = GeometrySchlickGGX(NdotV, roughness) * GeometrySchlickGGX(NdotL, roughness); vec3 F = F0 + (1.0 - F0) * pow(1.0 - max(dot(H, V), 0.0), 5.0); vec3 nominator = NDF * G * F; float denominator = 4 * NdotV * NdotL + 0.001; // 0.001 to prevent divide by zero. vec3 brdf = nominator / denominator; vec3 kD = 1.0 - F; kD *= 1.0 - metallic; return (kD * albedo / PI + brdf) * radiance * NdotL; } #endif // MAIN PROGRAM void main(void) { // get fragment information from the G-Buffer ivec2 texCoord = ivec2(gl_FragCoord.xy); #ifdef UNLIT outColor = vec4(texelFetch(emissionBuffer, texCoord).rgb, 1); #else vec3 normal = texelFetch(normalBuffer, texCoord).xyz; vec4 albedoTexel = texelFetch(albedoBuffer, texCoord); vec3 albedo = albedoTexel.rgb; float roughness = albedoTexel.a; vec4 emissionTexel = texelFetch(emissionBuffer, texCoord); vec3 emission = emissionTexel.rgb; float metallic = emissionTexel.a; vec4 fragPos = texelFetch(positionBuffer, texCoord); // compute shadow #ifdef SHADOWMAP #ifdef POINT_LIGHT float shadow = 1; #else vec4 fragInLightSpace = viewToLightMatrix * fragPos; fragInLightSpace.z = fragInLightSpace.z - 0.002; float shadow = texture(shadowMap, fragInLightSpace.xyz/fragInLightSpace.w); #endif #else float shadow = 1; #endif float att = 1; #ifdef POINT_LIGHT vec3 dirLight = pointLight - fragPos.xyz; //vec4 pointShadowParam = vec4(inverseViewMatrix * dirLight, length(dirLight)); //att = texture(shadowMap, pointShadowParam); //outColor = vec4(vec3(texture(shadowMap, vec4(inverseViewMatrix * dirLight, length(dirLight)/range))), 1); //outColor = vec4(vec3(texture(shadowMap, vec3(inverseViewMatrix * dirLight)).r), 1); //outColor = vec4(vec3(length(dirLight)/range), 1); //return; float dist = length(dirLight); att = clamp(1 - dist/range, 0, 1); dirLight = normalize(dirLight); #endif vec3 viewDir = normalize(-fragPos.xyz); #ifdef AMBIENT_LIGHT outColor = vec4(emission + GGX(albedo, metallic, roughness, normal, viewDir), 1); #else vec3 halfVec = normalize(viewDir + dirLight); vec3 light = GGX(albedo, metallic, roughness, lightColor, normal, dirLight, halfVec, viewDir); outColor = vec4(mix(vec3(0.0), light*shadow, att*att), 1); #endif #endif // UNLIT }