stuff
This commit is contained in:
+251
-37
@@ -3,59 +3,273 @@ out vec4 FragColor;
|
||||
|
||||
in vec2 TexCoord;
|
||||
|
||||
uniform sampler2D gPosition;
|
||||
uniform sampler2D gNormal;
|
||||
uniform sampler2D gAlbedo;
|
||||
uniform sampler2D gLightSpacePos;
|
||||
uniform sampler2DShadow shadowMap;
|
||||
|
||||
struct DirLight {
|
||||
vec3 direction;
|
||||
vec3 ambient;
|
||||
vec3 diffuse;
|
||||
vec3 specular;
|
||||
vec3 color;
|
||||
float intensity;
|
||||
};
|
||||
uniform DirLight dirLight;
|
||||
uniform vec3 viewPos;
|
||||
|
||||
float ShadowCalculation(vec4 fragPosLightSpace)
|
||||
struct PointLight {
|
||||
vec3 position;
|
||||
vec3 color;
|
||||
float intensity;
|
||||
float radius;
|
||||
int shadowTile;
|
||||
};
|
||||
|
||||
struct SpotLight {
|
||||
vec3 position;
|
||||
vec3 direction;
|
||||
vec3 color;
|
||||
float intensity;
|
||||
float radius;
|
||||
float innerCos;
|
||||
float outerCos;
|
||||
int shadowTile;
|
||||
};
|
||||
|
||||
// The array sizes have to match Light.h
|
||||
#define MAX_DIRECTIONAL_LIGHTS 4
|
||||
#define MAX_POINT_LIGHTS 16
|
||||
#define MAX_SPOT_LIGHTS 8
|
||||
#define SHADOW_ATLAS_COLUMNS 6
|
||||
#define SHADOW_ATLAS_TILES (SHADOW_ATLAS_COLUMNS * SHADOW_ATLAS_COLUMNS)
|
||||
|
||||
uniform DirLight dirLight; // the sun, the only light with a shadow map
|
||||
uniform DirLight directionalLights[MAX_DIRECTIONAL_LIGHTS];
|
||||
uniform int directionalLightCount = 0;
|
||||
uniform PointLight pointLights[MAX_POINT_LIGHTS];
|
||||
uniform int pointLightCount = 0;
|
||||
uniform SpotLight spotLights[MAX_SPOT_LIGHTS];
|
||||
uniform int spotLightCount = 0;
|
||||
uniform sampler2DShadow localShadowMap;
|
||||
uniform mat4 shadowTileMatrices[SHADOW_ATLAS_TILES];
|
||||
|
||||
uniform vec3 viewPos;
|
||||
uniform mat4 lightSpaceMatrix;
|
||||
|
||||
uniform sampler2D gPosition;
|
||||
uniform sampler2D gNormal;
|
||||
uniform sampler2D gAlbedo;
|
||||
uniform sampler2D gMaterial;
|
||||
uniform sampler2DShadow shadowMap;
|
||||
uniform samplerCube environmentMap;
|
||||
uniform float envIntensity = 0.8;
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
|
||||
float DistributionGGX(vec3 N, vec3 H, float rough)
|
||||
{
|
||||
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
||||
projCoords = projCoords * 0.5 + 0.5;
|
||||
if (projCoords.z > 1.0) return 0.0;
|
||||
return texture(shadowMap, vec3(projCoords.xy, projCoords.z));
|
||||
float a = rough * rough;
|
||||
float a2 = a * a;
|
||||
float NdotH = max(dot(N, H), 0.0);
|
||||
float NdotH2 = NdotH * NdotH;
|
||||
float denom = NdotH2 * (a2 - 1.0) + 1.0;
|
||||
return a2 / max(PI * denom * denom, 1e-5);
|
||||
}
|
||||
|
||||
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir, vec4 fragPosLS, vec3 albedo)
|
||||
float GeometrySchlickGGX(float NdotV, float rough)
|
||||
{
|
||||
vec3 lightDir = normalize(-light.direction);
|
||||
float diff = max(dot(normal, lightDir), 0.0);
|
||||
vec3 halfwayDir = normalize(lightDir + viewDir);
|
||||
float spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0);
|
||||
float r = rough + 1.0;
|
||||
float k = (r * r) / 8.0;
|
||||
return NdotV / (NdotV * (1.0 - k) + k);
|
||||
}
|
||||
|
||||
vec3 ambient = light.ambient * albedo;
|
||||
vec3 diffuse = light.diffuse * diff * albedo;
|
||||
vec3 specular = light.specular * spec;
|
||||
float GeometrySmith(vec3 N, vec3 V, vec3 L, float rough)
|
||||
{
|
||||
float NdotV = max(dot(N, V), 0.0);
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
return GeometrySchlickGGX(NdotV, rough) * GeometrySchlickGGX(NdotL, rough);
|
||||
}
|
||||
|
||||
float shadow = ShadowCalculation(fragPosLS);
|
||||
return ambient + (diffuse + specular) * shadow;
|
||||
vec3 FresnelSchlick(float cosTheta, vec3 F0)
|
||||
{
|
||||
return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
|
||||
}
|
||||
|
||||
vec3 FresnelSchlickRoughness(float cosTheta, vec3 F0, float rough)
|
||||
{
|
||||
vec3 f = max(vec3(1.0 - rough), F0);
|
||||
return F0 + (f - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
|
||||
}
|
||||
|
||||
vec3 EnvironmentBRDF(vec3 F0, float rough, float NdotV)
|
||||
{
|
||||
const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
|
||||
const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04);
|
||||
vec4 r = rough * c0 + c1;
|
||||
float a004 = min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x + r.y;
|
||||
vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw;
|
||||
return F0 * AB.x + AB.y;
|
||||
}
|
||||
|
||||
// Light from one direction, L points from the surface toward the light
|
||||
vec3 ShadeLight(vec3 L, vec3 radiance, vec3 N, vec3 V, vec3 albedo, float metal, float rough, vec3 F0, float NdotV)
|
||||
{
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
if (NdotL <= 0.0)
|
||||
return vec3(0.0);
|
||||
|
||||
vec3 H = normalize(V + L);
|
||||
vec3 F = FresnelSchlick(max(dot(H, V), 0.0), F0);
|
||||
float NDF = DistributionGGX(N, H, rough);
|
||||
float G = GeometrySmith(N, V, L, rough);
|
||||
vec3 specular = (NDF * G * F) / max(4.0 * NdotV * NdotL, 1e-4);
|
||||
vec3 kD = (vec3(1.0) - F) * (1.0 - metal);
|
||||
return (kD * albedo / PI + specular) * radiance * NdotL;
|
||||
}
|
||||
|
||||
// Inverse square falloff, windowed so it reaches exactly zero at the radius
|
||||
float PointAttenuation(float distanceSquared, float radius)
|
||||
{
|
||||
float ratio = distanceSquared / (radius * radius);
|
||||
float window = clamp(1.0 - ratio * ratio, 0.0, 1.0);
|
||||
return window * window / max(distanceSquared, 0.01);
|
||||
}
|
||||
|
||||
// Which of a point light's 6 atlas tiles covers offset, the order is set in RenderLocalShadows
|
||||
int PointShadowFace(vec3 offset)
|
||||
{
|
||||
vec3 size = abs(offset);
|
||||
if (size.x >= size.y && size.x >= size.z) return offset.x > 0.0 ? 0 : 1;
|
||||
if (size.y >= size.z) return offset.y > 0.0 ? 2 : 3;
|
||||
return offset.z > 0.0 ? 4 : 5;
|
||||
}
|
||||
|
||||
// 1 = lit, 0 = shadowed. Stays inside the tile so the filter can't pick up its neighbours.
|
||||
float LocalShadow(int tile, vec3 worldPos, vec3 N)
|
||||
{
|
||||
vec4 clipPos = shadowTileMatrices[tile] * vec4(worldPos + N * 0.04, 1.0);
|
||||
if (clipPos.w <= 0.0)
|
||||
return 1.0;
|
||||
|
||||
vec3 projCoords = clipPos.xyz / clipPos.w * 0.5 + 0.5;
|
||||
if (any(lessThan(projCoords, vec3(0.0))) || any(greaterThan(projCoords, vec3(1.0))))
|
||||
return 1.0;
|
||||
|
||||
float tileSize = 1.0 / float(SHADOW_ATLAS_COLUMNS);
|
||||
vec2 tileOrigin = vec2(float(tile % SHADOW_ATLAS_COLUMNS), float(tile / SHADOW_ATLAS_COLUMNS)) * tileSize;
|
||||
vec2 texelSize = 1.0 / vec2(textureSize(localShadowMap, 0));
|
||||
vec2 uv = tileOrigin + projCoords.xy * tileSize;
|
||||
|
||||
float shadow = 0.0;
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
vec2 sampleUV = clamp(uv + vec2(x, y) * texelSize, tileOrigin + texelSize, tileOrigin + tileSize - texelSize);
|
||||
shadow += texture(localShadowMap, vec3(sampleUV, projCoords.z - 0.0002));
|
||||
}
|
||||
}
|
||||
return shadow / 9.0;
|
||||
}
|
||||
|
||||
float ShadowCalculation(vec4 fragPosLightSpace, vec3 N, vec3 L)
|
||||
{
|
||||
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
||||
projCoords = projCoords * 0.5 + 0.5;
|
||||
|
||||
if (projCoords.z > 1.0 || any(lessThan(projCoords, vec3(0.0))) ||
|
||||
any(greaterThan(projCoords, vec3(1.0))))
|
||||
return 1.0;
|
||||
|
||||
float bias = max(0.004 * (1.0 - dot(N, L)), 0.001);
|
||||
vec2 texelSize = 1.0 / vec2(textureSize(shadowMap, 0));
|
||||
float shadow = 0.0;
|
||||
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
vec2 offset = vec2(x, y) * texelSize;
|
||||
shadow += texture(shadowMap, vec3(projCoords.xy + offset, projCoords.z - bias));
|
||||
}
|
||||
}
|
||||
return shadow / 9.0;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 fragPos = texture(gPosition, TexCoord).rgb;
|
||||
vec3 normal = normalize(texture(gNormal, TexCoord).rgb);
|
||||
vec4 albedaSample = texture(gAlbedo, TexCoord);
|
||||
vec4 fragPosLS = texture(gLightSpacePos, TexCoord);
|
||||
vec3 normal = texture(gNormal, TexCoord).rgb;
|
||||
|
||||
// Sky (no geometry written) — output clear color
|
||||
if (length(normal) < 0.1) {
|
||||
FragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
return;
|
||||
// Nothing was written here, keep the skybox underneath
|
||||
if (dot(normal, normal) < 0.01)
|
||||
discard;
|
||||
|
||||
vec3 FragPos = texture(gPosition, TexCoord).rgb;
|
||||
vec4 albedoSample = texture(gAlbedo, TexCoord);
|
||||
vec4 material = texture(gMaterial, TexCoord);
|
||||
|
||||
vec3 albedo = albedoSample.rgb;
|
||||
float metal = material.r;
|
||||
float rough = material.g;
|
||||
float ao = material.b;
|
||||
|
||||
vec3 N = normalize(normal);
|
||||
vec3 V = normalize(viewPos - FragPos);
|
||||
float NdotV = max(dot(N, V), 1e-4);
|
||||
|
||||
vec3 F0 = mix(vec3(0.04), albedo, metal);
|
||||
vec3 direct = vec3(0.0);
|
||||
|
||||
// Facing away from the sun has no direct light, so skip the BRDF and the 9 shadow samples
|
||||
vec3 sunL = normalize(-dirLight.direction);
|
||||
if (dot(N, sunL) > 0.0) {
|
||||
float shadow = ShadowCalculation(lightSpaceMatrix * vec4(FragPos, 1.0), N, sunL);
|
||||
direct += ShadeLight(sunL, dirLight.color * dirLight.intensity, N, V, albedo, metal, rough, F0, NdotV) * shadow;
|
||||
}
|
||||
|
||||
vec3 viewDir = normalize(viewPos - fragPos);
|
||||
vec3 lit = CalcDirLight(dirLight, normal, viewDir, fragPosLS, albedaSample.rgb);
|
||||
for (int i = 0; i < directionalLightCount; i++) {
|
||||
vec3 L = normalize(-directionalLights[i].direction);
|
||||
vec3 radiance = directionalLights[i].color * directionalLights[i].intensity;
|
||||
direct += ShadeLight(L, radiance, N, V, albedo, metal, rough, F0, NdotV);
|
||||
}
|
||||
|
||||
FragColor = vec4(lit, albedaSample.a);
|
||||
for (int i = 0; i < pointLightCount; i++) {
|
||||
vec3 toLight = pointLights[i].position - FragPos;
|
||||
float distanceSquared = dot(toLight, toLight);
|
||||
float radius = pointLights[i].radius;
|
||||
if (distanceSquared >= radius * radius)
|
||||
continue;
|
||||
|
||||
vec3 radiance = pointLights[i].color * pointLights[i].intensity * PointAttenuation(distanceSquared, radius);
|
||||
vec3 L = toLight / sqrt(distanceSquared);
|
||||
if (dot(N, L) <= 0.0)
|
||||
continue;
|
||||
|
||||
vec3 lit = ShadeLight(L, radiance, N, V, albedo, metal, rough, F0, NdotV);
|
||||
if (pointLights[i].shadowTile >= 0)
|
||||
lit *= LocalShadow(pointLights[i].shadowTile + PointShadowFace(-toLight), FragPos, N);
|
||||
direct += lit;
|
||||
}
|
||||
|
||||
for (int i = 0; i < spotLightCount; i++) {
|
||||
vec3 toLight = spotLights[i].position - FragPos;
|
||||
float distanceSquared = dot(toLight, toLight);
|
||||
float radius = spotLights[i].radius;
|
||||
if (distanceSquared >= radius * radius)
|
||||
continue;
|
||||
|
||||
vec3 L = toLight / sqrt(distanceSquared);
|
||||
float cone = smoothstep(spotLights[i].outerCos, spotLights[i].innerCos, dot(-L, spotLights[i].direction));
|
||||
if (cone <= 0.0)
|
||||
continue;
|
||||
|
||||
if (dot(N, L) <= 0.0)
|
||||
continue;
|
||||
|
||||
vec3 radiance = spotLights[i].color * spotLights[i].intensity * PointAttenuation(distanceSquared, radius) * cone;
|
||||
vec3 lit = ShadeLight(L, radiance, N, V, albedo, metal, rough, F0, NdotV);
|
||||
if (spotLights[i].shadowTile >= 0)
|
||||
lit *= LocalShadow(spotLights[i].shadowTile, FragPos, N);
|
||||
direct += lit;
|
||||
}
|
||||
|
||||
vec3 ambientF = FresnelSchlickRoughness(NdotV, F0, rough);
|
||||
vec3 ambientKD = (vec3(1.0) - ambientF) * (1.0 - metal);
|
||||
vec3 irradiance = pow(textureLod(environmentMap, N, 7.0).rgb, vec3(2.2)) * envIntensity;
|
||||
vec3 R = reflect(-V, N);
|
||||
vec3 prefiltered = pow(textureLod(environmentMap, R, rough * 7.0).rgb, vec3(2.2)) * envIntensity;
|
||||
vec3 specularIBL = prefiltered * EnvironmentBRDF(F0, rough, NdotV);
|
||||
vec3 ambient = (ambientKD * irradiance * albedo + specularIBL) * ao;
|
||||
|
||||
vec3 emission = albedo * material.a * 4.0;
|
||||
|
||||
FragColor = vec4(direct + ambient + emission, albedoSample.a);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user