1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
#version 330 core
in vec2 vUV;
in vec3 vNormal;
in vec3 vFragPos;
in vec4 vFragPosLightSpace;
out vec4 FragColor;
uniform sampler2D albedo;
uniform sampler2D normalMap;
uniform int normalEnabled;
uniform float normalStrength;
uniform vec3 tint;
uniform int radialEnabled;
uniform float radialInner;
uniform float radialOuter;
struct DirectionalLight
{
vec3 direction;
vec3 color;
float intensity;
};
uniform DirectionalLight dirLight;
uniform vec3 ambientColor = vec3(0.15, 0.15, 0.15);
uniform sampler2D shadowMap;
uniform float shadowBiasMin;
uniform float shadowBiasScale;
uniform int pcfRadius;
uniform sampler2D ssao;
uniform float aoStrength;
uniform float screenWidth;
uniform float screenHeight;
// Fog
uniform vec3 cameraPos;
uniform vec3 fogColor;
uniform float fogDensity; // e.g. 0.02
uniform float fogAmount; // 0..1 blend factor
float ShadowCalculation(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir)
{
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
if (projCoords.z > 1.0)
return 0.0;
float currentDepth = projCoords.z;
float bias = max(shadowBiasScale * (1.0 - max(dot(normal, lightDir), 0.0)), shadowBiasMin);
ivec2 texSize = textureSize(shadowMap, 0);
vec2 texelSize = 1.0 / vec2(texSize);
float vis = 0.0;
int samples = 0;
for (int x = -pcfRadius; x <= pcfRadius; ++x)
{
for (int y = -pcfRadius; y <= pcfRadius; ++y)
{
vec2 offset = vec2(x, y) * texelSize;
float depthSample = texture(shadowMap, projCoords.xy + offset).r;
// texture returns stored light-space depth. If our fragment's depth (minus bias)
// is less than or equal to the sampled depth, it's visible to the light.
vis += (currentDepth - bias <= depthSample) ? 1.0 : 0.0;
samples++;
}
}
float visibility = vis / float(samples);
float shadow = 1.0 - visibility;
return shadow;
}
void main()
{
vec4 tex = texture(albedo, vUV);
vec3 color = tex.rgb * tint;
float alpha = tex.a;
vec3 finalNormal = vec3(0.0, 0.0, 1.0);
if (normalEnabled == 1)
{
vec3 n = texture(normalMap, vUV).rgb;
n = n * 2.0 - 1.0;
finalNormal = normalize(vec3(n.x * normalStrength, n.y * normalStrength, 1.0));
}
if (radialEnabled == 1)
{
float dist = distance(vUV, vec2(0.5, 0.5));
float mask = 1.0 - smoothstep(radialInner, radialOuter, dist);
alpha *= mask;
}
vec3 N = normalize(finalNormal);
vec3 L = normalize(dirLight.direction);
float diff = max(dot(N, L), 0.0);
vec3 diffuse = dirLight.color * dirLight.intensity * diff;
float shadow = ShadowCalculation(vFragPosLightSpace, N, L);
float shadowFactor = 1.0 - shadow * 0.9;
float ao = 1.0;
if (aoStrength > 0.0)
{
vec2 ssaoUV = gl_FragCoord.xy / vec2(screenWidth, screenHeight);
ao = texture(ssao, ssaoUV).r;
}
float aoFactor = mix(1.0, ao, aoStrength);
vec3 result = color * (ambientColor * aoFactor + diffuse * shadowFactor);
// Compute fog based on distance from camera
float dist = length(vFragPos - cameraPos);
float fogFactor = 1.0 - exp(-fogDensity * dist);
fogFactor = clamp(fogFactor * fogAmount, 0.0, 1.0);
vec3 finalColor = mix(result, fogColor, fogFactor);
FragColor = vec4(finalColor, alpha);
}
|