This commit is contained in:
2026-11-02 19:35:36 -05:00
parent a6103e453d
commit 7b32ad674e
52 changed files with 3285 additions and 1488 deletions
+3 -3
View File
@@ -12,7 +12,6 @@ uniform vec2 pixelScale;
uniform vec2 pixelPos;
uniform vec4 Color = {0.0f,0.0f,0.0f,1.0f};
uniform vec4 BackgroundColor = {0.0f,0.0f,0.0f,1.0f};
void main()
{
@@ -27,10 +26,11 @@ void main()
vec2 absrelcoord = abs(fragPosTopLeft - pixelScale/2 - pixelPos);
if (!(absrelcoord.x < r1.x || absrelcoord.y < r1.y)) {
if (pow(absrelcoord.y-r1.y,2) > pow(roundingfactor,2) - pow((absrelcoord.x-r1.x),2)) {
vec2 cornerOffset = absrelcoord - r1;
if (dot(cornerOffset, cornerOffset) > roundingfactor * roundingfactor) {
discard;
}
}
}
}
}
+3 -3
View File
@@ -12,7 +12,6 @@ uniform vec2 pixelScale;
uniform vec2 pixelPos;
uniform vec4 Color = {0.0f,0.0f,0.0f,1.0f};
uniform vec4 BackgroundColor = {0.0f,0.0f,0.0f,1.0f};
uniform sampler2D tex;
@@ -29,10 +28,11 @@ void main()
vec2 absrelcoord = abs(fragPosTopLeft - pixelScale/2 - pixelPos);
if (!(absrelcoord.x < r1.x || absrelcoord.y < r1.y)) {
if (pow(absrelcoord.y-r1.y,2) > pow(roundingfactor,2) - pow((absrelcoord.x-r1.x),2)) {
vec2 cornerOffset = absrelcoord - r1;
if (dot(cornerOffset, cornerOffset) > roundingfactor * roundingfactor) {
discard;
}
}
}
}
}
+7 -1
View File
@@ -11,6 +11,10 @@ uniform vec2 pixelScale;
uniform float z;
uniform float rotation;
uniform int uvMode = 0; // 0 = stretch, 1 = tile
uniform vec3 tileScale = vec3(1.0);
uniform vec2 uvOffset = vec2(0.0);
void main()
{
vec2 unitPos = aPos * 0.5 + 0.5;
@@ -20,5 +24,7 @@ void main()
gl_Position = vec4(ndc, 0.0f, 1.0f);
TexCoords = aTexCoords;
// Screen y runs down here, so flip v to keep images the right way up
vec2 uv = vec2(aTexCoords.x, 1.0 - aTexCoords.y);
TexCoords = (uvMode == 1 ? uv * tileScale.xy : uv) + uvOffset;
}
+4 -2
View File
@@ -11,12 +11,14 @@ out vec3 FragPos;
uniform mat4 view;
uniform mat4 proj;
uniform int uvMode = 0; // 0 = stretch, 1 = tile
uniform vec3 tileScale = vec3(1.0);
uniform vec2 uvOffset = vec2(0.0);
void main()
{
FragPos = vec3(modl * vec4(aPos, 1.0));
normal = aNormal;
texCoord = aTex;
texCoord = (uvMode == 1 ? aPos.xy * tileScale.xy : aTex) + uvOffset;
gl_Position = proj* view * modl * vec4(aPos,1.0f);
}
+6 -6
View File
@@ -4,6 +4,8 @@ out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D screenTexture;
uniform sampler2D bloomTexture;
uniform float bloomStrength = 0.0f;
uniform float brightness = 0.0f;
uniform float colorContrast = 0.0f;
uniform float saturation = 1.0f;
@@ -21,21 +23,19 @@ vec3 ACESFilm(vec3 x)
void main()
{
vec3 originalcolor = texture(screenTexture, TexCoords).rgb;
// Added before tonemapping, while everything is still in HDR
vec3 originalcolor = texture(screenTexture, TexCoords).rgb
+ texture(bloomTexture, TexCoords).rgb * bloomStrength;
vec3 finalcolor = ACESFilm(originalcolor * exposure);
const float gamma = 2.2;
finalcolor = pow(finalcolor, vec3(1.0 / gamma));
vec3 contrast = vec3(1.0f) - finalcolor;
finalcolor = mix(finalcolor,vec3(1.0f) - finalcolor,colorContrast);
float average = (finalcolor[0]+finalcolor[1]+finalcolor[2])/3.0f;
for (int i = 0; i < 3; i++) {
finalcolor[i] = average + saturation * (finalcolor[i] - average);
}
finalcolor = mix(vec3(average), finalcolor, saturation);
FragColor = vec4(finalcolor,1.0f);
}
+23
View File
@@ -0,0 +1,23 @@
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D image;
uniform int horizontal;
const float weights[5] = float[](0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
void main()
{
vec2 texel = 1.0 / vec2(textureSize(image, 0));
vec2 direction = horizontal != 0 ? vec2(texel.x, 0.0) : vec2(0.0, texel.y);
vec3 color = texture(image, TexCoords).rgb * weights[0];
for (int i = 1; i < 5; i++) {
color += texture(image, TexCoords + direction * float(i)).rgb * weights[i];
color += texture(image, TexCoords - direction * float(i)).rgb * weights[i];
}
FragColor = vec4(color, 1.0);
}
+26
View File
@@ -0,0 +1,26 @@
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D screenTexture;
uniform float threshold;
void main()
{
// Averaged, since a quarter size target would otherwise skip most of the screen's pixels
vec2 texel = 1.0 / vec2(textureSize(screenTexture, 0));
vec3 color = (texture(screenTexture, TexCoords + texel * vec2(-1.0, -1.0)).rgb
+ texture(screenTexture, TexCoords + texel * vec2(1.0, -1.0)).rgb
+ texture(screenTexture, TexCoords + texel * vec2(-1.0, 1.0)).rgb
+ texture(screenTexture, TexCoords + texel * vec2(1.0, 1.0)).rgb) * 0.25;
// A single blown out specular pixel would otherwise flash a huge glow
color = min(color, vec3(20.0));
// Keeps the hue of whatever is over the threshold, and fades in instead of cutting off
float brightness = max(color.r, max(color.g, color.b));
float contribution = max(brightness - threshold, 0.0) / max(brightness, 1e-4);
FragColor = vec4(color * contribution, 1.0);
}
+150 -13
View File
@@ -12,7 +12,42 @@ struct DirLight {
float intensity;
};
uniform DirLight dirLight;
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 sampler2DShadow shadowMap;
@@ -23,6 +58,7 @@ uniform int useTexture = 0;
uniform float metallic = 0.0;
uniform float roughness = 0.7;
uniform float ao = 1.0;
uniform float emissive = 0.0;
uniform float envIntensity = 0.8;
const float PI = 3.14159265359;
@@ -72,6 +108,65 @@ vec3 EnvironmentBRDF(vec3 F0, float rough, float NdotV)
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;
@@ -112,21 +207,63 @@ void main()
vec3 N = normalize(normal);
vec3 V = normalize(viewPos - FragPos);
vec3 L = normalize(-dirLight.direction);
vec3 H = normalize(V + L);
float NdotV = max(dot(N, V), 1e-4);
float NdotL = max(dot(N, L), 0.0);
vec3 F0 = mix(vec3(0.04), albedo, metal);
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);
vec3 radiance = dirLight.color * dirLight.intensity;
vec3 direct = vec3(0.0);
float shadow = ShadowCalculation(fragposlightspace, N, L);
vec3 direct = (kD * albedo / PI + specular) * radiance * NdotL * shadow;
// 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(fragposlightspace, N, sunL);
direct += ShadeLight(sunL, dirLight.color * dirLight.intensity, N, V, albedo, metal, rough, F0, NdotV) * shadow;
}
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);
}
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);
@@ -136,5 +273,5 @@ void main()
vec3 specularIBL = prefiltered * EnvironmentBRDF(F0, rough, NdotV);
vec3 ambient = (ambientKD * irradiance * albedo + specularIBL) * ao;
FragColor = vec4(direct + ambient, albedoSample.a);
FragColor = vec4(direct + ambient + albedo * emissive * 4.0, albedoSample.a);
}
+29 -5
View File
@@ -13,11 +13,35 @@ uniform mat4 proj;
uniform mat4 lightSpaceMatrix;
uniform int uvMode = 0; // 0 = stretch, 1 = tile
uniform vec3 tileScale = vec3(1.0);
uniform vec2 uvOffset = vec2(0.0);
// Same direction as transpose(inverse(m)) for normals, without the per vertex inverse
mat3 CofactorMatrix(mat3 m)
{
return mat3(cross(m[1], m[2]), cross(m[2], m[0]), cross(m[0], m[1]));
}
// Tiling ignores the mesh's uvs and projects from whichever axis the surface faces,
// so every face of a stretched cube repeats at the same size
vec2 SurfaceUV()
{
if (uvMode == 0) return aTex + uvOffset;
vec3 n = abs(aNormal);
vec3 p = aPos * tileScale;
if (n.y >= n.x && n.y >= n.z) return p.xz + uvOffset;
if (n.x >= n.z) return p.zy + uvOffset;
return p.xy + uvOffset;
}
void main()
{
FragPos = vec3(modl * vec4(aPos, 1.0));
normal = transpose(inverse(mat3(modl))) * aNormal;
texCoord = aTex;
fragposlightspace = lightSpaceMatrix * vec4(FragPos, 1.0);
gl_Position = proj* view * modl * vec4(aPos,1.0f);
vec4 worldPos = modl * vec4(aPos, 1.0);
FragPos = worldPos.xyz;
normal = CofactorMatrix(mat3(modl)) * aNormal;
texCoord = SurfaceUV();
fragposlightspace = lightSpaceMatrix * worldPos;
gl_Position = proj * view * worldPos;
}
+251 -37
View File
@@ -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);
}
+28 -18
View File
@@ -6,36 +6,46 @@ in vec2 TexCoord;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedo;
uniform sampler2D gLightSpacePos;
uniform sampler2DShadow shadowMap;
uniform sampler2D gMaterial;
uniform sampler2D depthTex;
uniform sampler2D shadowTex;
uniform sampler2D bloomTex;
uniform int debugMode; // 0 = final, 1 = position, 2 = normal, 3 = albedo, 4 = lightspace, 5 = depth
uniform float nearPlane;
uniform float farPlane;
uniform int debugMode; // 1 = position, 2 = normal, 3 = albedo, 4 = material, 5 = depth, 6 = shadow map, 7 = bloom
void main()
{
FragColor = vec4(0.0, 0.0, 0.0, 1.0);
if (debugMode == 1) {
vec3 v = texture(gPosition, TexCoord).rgb;
FragColor = vec4(v * 0.01 + 0.5, 1.0);
return;
}
if (debugMode == 2) {
vec3 n = normalize(texture(gNormal, TexCoord).rgb);
FragColor = vec4(n * 0.5 + 0.5, 1.0);
return;
else if (debugMode == 2) {
vec3 n = texture(gNormal, TexCoord).rgb;
if (length(n) >= 0.1)
FragColor = vec4(normalize(n) * 0.5 + 0.5, 1.0);
}
if (debugMode == 3) {
FragColor = texture(gAlbedo, TexCoord);
return;
else if (debugMode == 3) {
FragColor = vec4(pow(texture(gAlbedo, TexCoord).rgb, vec3(1.0 / 2.2)), 1.0);
}
if (debugMode == 4) {
FragColor = texture(gLightSpacePos, TexCoord);
return;
else if (debugMode == 4) {
FragColor = vec4(texture(gMaterial, TexCoord).rgb, 1.0);
}
if (debugMode == 5) {
float d = texture(depthTex, TexCoord).r;
else if (debugMode == 5) {
// Perspective depth is nearly 1.0 everywhere, so linearize it and spread it out
float z = texture(depthTex, TexCoord).r * 2.0 - 1.0;
float linearDepth = (2.0 * nearPlane * farPlane) / (farPlane + nearPlane - z * (farPlane - nearPlane));
float d = log(1.0 + linearDepth) / log(1.0 + farPlane);
FragColor = vec4(vec3(d), 1.0);
return;
}
FragColor = vec4(0.0);
else if (debugMode == 6) {
FragColor = vec4(vec3(texture(shadowTex, TexCoord).r), 1.0);
}
else if (debugMode == 7) {
FragColor = vec4(texture(bloomTex, TexCoord).rgb, 1.0);
}
}
+21 -9
View File
@@ -1,23 +1,35 @@
#version 330 core
layout (location = 0) out vec3 gPosition;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gAlbedo;
layout (location = 3) out vec4 gLightSpacePos;
layout (location = 2) out vec4 gAlbedo; // linear rgb + alpha
layout (location = 3) out vec4 gMaterial; // r = metallic, g = roughness, b = ao, a = emissive
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoord;
in vec4 FragPosLightSpace;
uniform sampler2D tex;
uniform vec4 colorOverride = vec4(0.0);
uniform int useTexture = 0;
uniform float metallic = 0.0;
uniform float roughness = 0.7;
uniform float ao = 1.0;
uniform float emissive = 0.0;
void main()
{
vec4 albedo = texture(tex, TexCoord);
if (albedo.a < 0.1) discard;
vec4 albedo = vec4(0.8, 0.8, 0.8, 1.0);
if (useTexture != 0) {
albedo = texture(tex, TexCoord);
if (albedo.a < 0.1)
discard;
albedo.rgb = pow(albedo.rgb, vec3(2.2));
}
gPosition = FragPos;
gNormal = normalize(Normal);
gAlbedo = albedo;
gLightSpacePos = FragPosLightSpace;
albedo.rgb = mix(albedo.rgb, colorOverride.rgb, colorOverride.w);
gPosition = FragPos;
gNormal = normalize(Normal);
gAlbedo = albedo;
gMaterial = vec4(clamp(metallic, 0.0, 1.0), clamp(roughness, 0.04, 1.0), ao, clamp(emissive, 0.0, 1.0));
}
+25 -5
View File
@@ -6,19 +6,39 @@ layout (location = 2) in vec2 aTexCoord;
out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoord;
out vec4 FragPosLightSpace;
uniform mat4 proj;
uniform mat4 view;
uniform mat4 modl;
uniform mat4 lightSpaceMatrix;
uniform int uvMode = 0; // 0 = stretch, 1 = tile
uniform vec3 tileScale = vec3(1.0);
uniform vec2 uvOffset = vec2(0.0);
// Same direction as transpose(inverse(m)) for normals, without the per vertex inverse
mat3 CofactorMatrix(mat3 m)
{
return mat3(cross(m[1], m[2]), cross(m[2], m[0]), cross(m[0], m[1]));
}
// Tiling ignores the mesh's uvs and projects from whichever axis the surface faces,
// so every face of a stretched cube repeats at the same size
vec2 SurfaceUV()
{
if (uvMode == 0) return aTexCoord + uvOffset;
vec3 n = abs(aNormal);
vec3 p = aPos * tileScale;
if (n.y >= n.x && n.y >= n.z) return p.xz + uvOffset;
if (n.x >= n.z) return p.zy + uvOffset;
return p.xy + uvOffset;
}
void main()
{
vec4 worldPos = modl * vec4(aPos, 1.0);
FragPos = worldPos.xyz;
Normal = normalize(mat3(transpose(inverse(modl))) * aNormal);
TexCoord = aTexCoord;
FragPosLightSpace = lightSpaceMatrix * worldPos;
Normal = normalize(CofactorMatrix(mat3(modl)) * aNormal);
TexCoord = SurfaceUV();
gl_Position = proj * view * worldPos;
}
+48 -15
View File
@@ -8,27 +8,60 @@ layout (location = 4) in vec4 weights;
out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoord;
out vec4 FragPosLightSpace;
uniform mat4 proj;
uniform mat4 view;
uniform mat4 modl;
uniform mat4 lightSpaceMatrix;
uniform mat4 finalBonesMatrices[100];
const int MAX_BONES = 100;
const int MAX_BONE_INFLUENCE = 4;
uniform mat4 finalBonesMatrices[MAX_BONES];
uniform int uvMode = 0; // 0 = stretch, 1 = tile
uniform vec3 tileScale = vec3(1.0);
uniform vec2 uvOffset = vec2(0.0);
// Same direction as transpose(inverse(m)) for normals, without the per vertex inverse
mat3 CofactorMatrix(mat3 m)
{
return mat3(cross(m[1], m[2]), cross(m[2], m[0]), cross(m[0], m[1]));
}
// Tiling ignores the mesh's uvs and projects from whichever axis the surface faces.
// Done on the bind pose, so the pattern stays stuck to the character as it moves.
vec2 SurfaceUV()
{
if (uvMode == 0) return aTexCoord + uvOffset;
vec3 n = abs(aNormal);
vec3 p = aPos * tileScale;
if (n.y >= n.x && n.y >= n.z) return p.xz + uvOffset;
if (n.x >= n.z) return p.zy + uvOffset;
return p.xy + uvOffset;
}
void main()
{
mat4 boneTransform = finalBonesMatrices[boneIds[0]] * weights[0];
boneTransform += finalBonesMatrices[boneIds[1]] * weights[1];
boneTransform += finalBonesMatrices[boneIds[2]] * weights[2];
boneTransform += finalBonesMatrices[boneIds[3]] * weights[3];
vec4 totalPosition = vec4(0.0);
vec3 totalNormal = vec3(0.0);
vec4 localPos = boneTransform * vec4(aPos, 1.0);
vec4 worldPos = modl * localPos;
for (int i = 0; i < MAX_BONE_INFLUENCE; i++)
{
if (boneIds[i] == -1) continue;
if (boneIds[i] >= MAX_BONES)
{
totalPosition = vec4(aPos, 1.0);
totalNormal = aNormal;
break;
}
totalPosition += (finalBonesMatrices[boneIds[i]] * vec4(aPos, 1.0)) * weights[i];
totalNormal += mat3(finalBonesMatrices[boneIds[i]]) * aNormal * weights[i];
}
FragPos = worldPos.xyz;
Normal = normalize(mat3(transpose(inverse(modl * boneTransform))) * aNormal);
TexCoord = aTexCoord;
FragPosLightSpace = lightSpaceMatrix * worldPos;
gl_Position = proj * view * worldPos;
}
vec4 worldPos = modl * totalPosition;
FragPos = worldPos.xyz;
Normal = normalize(CofactorMatrix(mat3(modl)) * totalNormal);
TexCoord = SurfaceUV();
gl_Position = proj * view * worldPos;
}
+30 -6
View File
@@ -20,7 +20,30 @@ out vec3 normal;
out vec2 texCoord;
out vec3 FragPos;
out vec4 fragposlightspace;
uniform int uvMode = 0; // 0 = stretch, 1 = tile
uniform vec3 tileScale = vec3(1.0);
uniform vec2 uvOffset = vec2(0.0);
// Same direction as transpose(inverse(m)) for normals, without the per vertex inverse
mat3 CofactorMatrix(mat3 m)
{
return mat3(cross(m[1], m[2]), cross(m[2], m[0]), cross(m[0], m[1]));
}
// Tiling ignores the mesh's uvs and projects from whichever axis the surface faces.
// Done on the bind pose, so the pattern stays stuck to the character as it moves.
vec2 SurfaceUV()
{
if (uvMode == 0) return tex + uvOffset;
vec3 n = abs(norm);
vec3 p = pos * tileScale;
if (n.y >= n.x && n.y >= n.z) return p.xz + uvOffset;
if (n.x >= n.z) return p.zy + uvOffset;
return p.xy + uvOffset;
}
void main()
{
vec4 totalPosition = vec4(0.0);
@@ -40,9 +63,10 @@ void main()
totalPosition += (finalBonesMatrices[boneIds[i]] * vec4(pos, 1.0))* weights[i];
totalNormal += mat3(finalBonesMatrices[boneIds[i]]) * norm * weights[i];
}
FragPos = vec3(modl * totalPosition);
fragposlightspace = lightSpaceMatrix * vec4(FragPos, 1.0);
gl_Position = proj * view * modl * totalPosition;
normal = normalize(mat3(transpose(inverse(modl))) * totalNormal);
texCoord = tex;
vec4 worldPos = modl * totalPosition;
FragPos = worldPos.xyz;
fragposlightspace = lightSpaceMatrix * worldPos;
gl_Position = proj * view * worldPos;
normal = normalize(CofactorMatrix(mat3(modl)) * totalNormal);
texCoord = SurfaceUV();
}
+1 -2
View File
@@ -10,6 +10,5 @@ void main()
{
TexCoords = aPos;
vec4 pos = proj * view * vec4(aPos, 1.0);
//gl_Position = pos.xyww
gl_Position = pos;
gl_Position = pos.xyww; // sits on the far plane, so it only draws where no geometry is
}