Skip to content

Commit 95610e4

Browse files
authored
Cleanup standard shaders (#510)
* Cleanup standard (blinn-phong) shader * Updated unlit and standard PBR * Updated lambert and added shadow clipping * Fixed parallax mapping and fix feature set not supporting permutations * Fixed material bool parameters not working * Added option to clip edges after parallax pass * Fixed normal mapping was using a TBN in right-handed coordinate system instead of left-handed
1 parent 664bf2c commit 95610e4

File tree

11 files changed

+307
-80
lines changed

11 files changed

+307
-80
lines changed

Resources/Engine/Shaders/Common/Utils.ovfxh

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,53 @@ vec2 TileAndOffsetTexCoords(vec2 texCoords, vec2 tiling, vec2 offset)
1212
return vec2(mod(texCoords.x * tiling.x, 1), mod(texCoords.y * tiling.y, 1)) + offset;
1313
}
1414

15+
// Expects a height map with values in the range [0, 1].
16+
// 1.0 means the height is at the maximum depth, 0.0 means the height is at the minimum depth.
17+
vec2 ApplyParallaxOcclusionMapping(vec2 texCoords, sampler2D heightMap, vec3 tangentViewPos, vec3 tangentFragPos, float heightScale)
18+
{
19+
const vec3 viewDir = normalize(tangentViewPos - tangentFragPos);
20+
21+
// number of depth layers
22+
const float minLayers = 8;
23+
const float maxLayers = 64;
24+
const float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir)));
25+
26+
// calculate the size of each layer
27+
const float layerDepth = 1.0 / numLayers;
28+
// the amount to shift the texture coordinates per layer (from vector P)
29+
const vec2 P = viewDir.xy / viewDir.z * heightScale;
30+
const vec2 deltaTexCoords = P / numLayers;
31+
32+
// get initial values
33+
vec2 currentTexCoords = texCoords;
34+
float currentDepthMapValue = 1.0 - texture(heightMap, currentTexCoords).r;
35+
float currentLayerDepth = 0.0;
36+
37+
while(currentLayerDepth < currentDepthMapValue)
38+
{
39+
// shift texture coordinates along direction of P
40+
currentTexCoords -= deltaTexCoords;
41+
// get depthmap value at current texture coordinates
42+
currentDepthMapValue = 1.0 - texture(heightMap, currentTexCoords).r;
43+
// get depth of next layer
44+
currentLayerDepth += layerDepth;
45+
}
46+
47+
// get texture coordinates before collision (reverse operations)
48+
const vec2 prevTexCoords = currentTexCoords + deltaTexCoords;
49+
50+
// get depth after and before collision for linear interpolation
51+
const float afterDepth = currentDepthMapValue - currentLayerDepth;
52+
const float beforeDepth = 1.0 - texture(heightMap, prevTexCoords).r - currentLayerDepth + layerDepth;
53+
54+
// interpolation of texture coordinates
55+
const float weight = afterDepth / (afterDepth - beforeDepth);
56+
const vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
57+
58+
return finalTexCoords;
59+
}
60+
61+
// [Deprecated] Kept for backward compatibility. Prefer using `ApplyParallaxOcclusionMapping()` instead.
1562
vec2 ApplyParallaxMapping(vec2 texCoords, sampler2D heightMap, vec3 tangentViewPos, vec3 tangentFragPos, float heightScale)
1663
{
1764
if (heightScale > 0)
@@ -24,6 +71,7 @@ vec2 ApplyParallaxMapping(vec2 texCoords, sampler2D heightMap, vec3 tangentViewP
2471
return texCoords;
2572
}
2673

74+
// [Deprecated] Kept for backward compatibility.
2775
bool IsMasked(sampler2D maskMap, vec2 texCoords)
2876
{
2977
return texture(maskMap, texCoords).r == 0.0;
@@ -38,17 +86,23 @@ mat3 ConstructTBN(mat4 model, vec3 normal, vec3 tangent, vec3 bitangent)
3886
);
3987
}
4088

89+
90+
vec3 ComputeNormal(vec2 texCoords, vec3 normal, sampler2D normalMap, mat3 TBN)
91+
{
92+
normal = texture(normalMap, texCoords).rgb;
93+
normal = normalize(normal * 2.0 - 1.0);
94+
normal = normalize(TBN * normal);
95+
return normal;
96+
}
97+
98+
// [Deprecated] Kept for backward compatibility. Prefer using `ComputeNormal()` without the `enableNormalMapping` parameter,
99+
// and handle branching using preprocessor directives instead.
41100
vec3 ComputeNormal(bool enableNormalMapping, vec2 texCoords, vec3 normal, sampler2D normalMap, mat3 TBN)
42101
{
43102
if (enableNormalMapping)
44103
{
45-
normal = texture(normalMap, texCoords).rgb;
46-
normal = normalize(normal * 2.0 - 1.0);
47-
normal = normalize(TBN * normal);
48-
return normal;
49-
}
50-
else
51-
{
52-
return normalize(normal);
104+
return ComputeNormal(texCoords, normal, normalMap, TBN);
53105
}
106+
107+
return normalize(normal);
54108
}

Resources/Engine/Shaders/Lambert.ovfx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,29 @@ uniform sampler2D u_DiffuseMap;
5353
uniform vec2 u_TextureTiling = vec2(1.0, 1.0);
5454
uniform vec2 u_TextureOffset = vec2(0.0, 0.0);
5555

56+
#if defined(SHADOW_PASS)
57+
uniform float u_ShadowClippingThreshold = 0.5;
58+
#endif
59+
5660
out vec4 FRAGMENT_COLOR;
5761

5862
void main()
5963
{
64+
const vec2 texCoords = TileAndOffsetTexCoords(fs_in.TexCoords, u_TextureTiling, u_TextureOffset);
65+
const vec4 diffuse = texture(u_DiffuseMap, texCoords) * u_Diffuse;
66+
6067
#if defined(SHADOW_PASS)
61-
// Empty fragment shader for shadow pass
68+
if (diffuse.a < u_ShadowClippingThreshold)
69+
{
70+
discard;
71+
}
6272
#else
63-
vec2 texCoords = TileAndOffsetTexCoords(fs_in.TexCoords, u_TextureTiling, u_TextureOffset);
6473

6574
const vec3 kLightPosition = vec3(-9000.0, 10000.0, 11000.0);
6675
const vec3 kLightDiffuse = vec3(1.0);
6776
const vec3 kLightAmbient = vec3(0.3);
68-
const vec3 lambert = ComputeLambertLighting(fs_in.FragPos, fs_in.Normal, kLightPosition, kLightDiffuse, kLightAmbient);
77+
const vec3 lambert = diffuse.rgb * ComputeLambertLighting(fs_in.FragPos, fs_in.Normal, kLightPosition, kLightDiffuse, kLightAmbient);
6978

70-
FRAGMENT_COLOR = texture(u_DiffuseMap, texCoords) * u_Diffuse * vec4(lambert, 1.0);
79+
FRAGMENT_COLOR = vec4(lambert, diffuse.a);
7180
#endif
7281
}

Resources/Engine/Shaders/Lighting/BlinnPhong.ovfxh

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,11 @@ vec3 ComputeAmbientSphereLight(mat4 light, vec3 fragPos, vec4 diffuseTexel)
8181
return IsPointInSphere(fragPos, lightPosition, radius) ? diffuseTexel.rgb * lightColor * intensity : vec3(0.0);
8282
}
8383

84-
vec4 ComputeBlinnPhongLighting(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 fragPos, vec4 diffuse, vec3 specular, sampler2D diffuseMap, sampler2D specularMap, float shininess, sampler2D shadowMap, mat4 lightSpaceMatrix)
84+
// This version doesn't sample the diffuse texture, and expects the diffuse color to be passed in directly.
85+
// This is because other parts of the shader may have already sampled the diffuse texture, and we want to avoid double sampling.
86+
vec4 ComputeBlinnPhongLightingNoDiffuseSampling(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 fragPos, vec4 diffuse, vec3 specular, sampler2D specularMap, float shininess, sampler2D shadowMap, mat4 lightSpaceMatrix)
8587
{
8688
vec3 viewDir = normalize(viewPos - fragPos);
87-
vec4 diffuseTexel = texture(diffuseMap, texCoords) * diffuse;
8889
vec4 specularTexel = texture(specularMap, texCoords) * vec4(specular, 1.0);
8990

9091
vec3 lightAccumulation = vec3(0.0);
@@ -96,13 +97,31 @@ vec4 ComputeBlinnPhongLighting(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 f
9697

9798
switch(lightType)
9899
{
99-
case 0: lightAccumulation += ComputePointLight(light, fragPos, diffuseTexel, specularTexel, normal, viewDir, shininess); break;
100-
case 1: lightAccumulation += ComputeDirectionalLight(light, fragPos, diffuseTexel, specularTexel, normal, viewDir, shininess, shadowMap, lightSpaceMatrix); break;
101-
case 2: lightAccumulation += ComputeSpotLight(light, fragPos, diffuseTexel, specularTexel, normal, viewDir, shininess); break;
102-
case 3: lightAccumulation += ComputeAmbientBoxLight(light, fragPos, diffuseTexel); break;
103-
case 4: lightAccumulation += ComputeAmbientSphereLight(light, fragPos, diffuseTexel); break;
100+
case 0: lightAccumulation += ComputePointLight(light, fragPos, diffuse, specularTexel, normal, viewDir, shininess); break;
101+
case 1: lightAccumulation += ComputeDirectionalLight(light, fragPos, diffuse, specularTexel, normal, viewDir, shininess, shadowMap, lightSpaceMatrix); break;
102+
case 2: lightAccumulation += ComputeSpotLight(light, fragPos, diffuse, specularTexel, normal, viewDir, shininess); break;
103+
case 3: lightAccumulation += ComputeAmbientBoxLight(light, fragPos, diffuse); break;
104+
case 4: lightAccumulation += ComputeAmbientSphereLight(light, fragPos, diffuse); break;
104105
}
105106
}
106107

107-
return vec4(lightAccumulation, diffuseTexel.a);
108+
return vec4(lightAccumulation, diffuse.a);
108109
}
110+
111+
vec4 ComputeBlinnPhongLighting(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 fragPos, vec4 diffuse, vec3 specular, sampler2D diffuseMap, sampler2D specularMap, float shininess, sampler2D shadowMap, mat4 lightSpaceMatrix)
112+
{
113+
vec4 diffuseTexel = texture(diffuseMap, texCoords) * diffuse;
114+
115+
return ComputeBlinnPhongLightingNoDiffuseSampling(
116+
texCoords,
117+
normal,
118+
viewPos,
119+
fragPos,
120+
diffuseTexel,
121+
specular,
122+
specularMap,
123+
shininess,
124+
shadowMap,
125+
lightSpaceMatrix
126+
);
127+
}

Resources/Engine/Shaders/Lighting/PBR.ovfxh

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,11 @@ vec3 ComputeAmbientSphereLight(mat4 light, vec3 fragPos)
6363
return IsPointInSphere(fragPos, lightPosition, radius) ? lightColor * intensity : vec3(0.0);
6464
}
6565

66-
vec4 ComputePBRLighting(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 fragPos, vec4 inAlbedo, float inMetallic, float inRoughness, sampler2D albedoMap, sampler2D metallicMap, sampler2D roughnessMap, sampler2D aoMap, sampler2D shadowMap, mat4 lightSpaceMatrix)
66+
// This version doesn't sample the albedo texture, and expects the albedo color to be passed in directly.
67+
// This is because other parts of the shader may have already sampled the albedo texture, and we want to avoid double sampling.
68+
vec4 ComputePBRLightingNoAlbedoSampling(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 fragPos, vec4 inAlbedo, float inMetallic, float inRoughness, sampler2D metallicMap, sampler2D roughnessMap, sampler2D aoMap, sampler2D shadowMap, mat4 lightSpaceMatrix)
6769
{
68-
vec4 albedoRGBA = texture(albedoMap, texCoords) * inAlbedo;
69-
vec3 albedo = pow(albedoRGBA.rgb, vec3(2.2));
70+
vec3 albedo = pow(inAlbedo.rgb, vec3(2.2));
7071
float metallic = texture(metallicMap, texCoords).r * inMetallic;
7172
float roughness = texture(roughnessMap, texCoords).r * inRoughness;
7273
float ao = texture(aoMap, texCoords).r;
@@ -162,5 +163,24 @@ vec4 ComputePBRLighting(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 fragPos,
162163
vec3 ambient = ambientSum * albedo * ao;
163164
vec3 color = ambient + Lo;
164165

165-
return vec4(color, albedoRGBA.a);
166+
return vec4(color, inAlbedo.a);
167+
}
168+
169+
vec4 ComputePBRLighting(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 fragPos, vec4 inAlbedo, float inMetallic, float inRoughness, sampler2D albedoMap, sampler2D metallicMap, sampler2D roughnessMap, sampler2D aoMap, sampler2D shadowMap, mat4 lightSpaceMatrix)
170+
{
171+
vec4 albedoRGBA = texture(albedoMap, texCoords) * inAlbedo;
172+
return ComputePBRLightingNoAlbedoSampling(
173+
texCoords,
174+
normal,
175+
viewPos,
176+
fragPos,
177+
albedoRGBA,
178+
inMetallic,
179+
inRoughness,
180+
metallicMap,
181+
roughnessMap,
182+
aoMap,
183+
shadowMap,
184+
lightSpaceMatrix
185+
);
166186
}

Resources/Engine/Shaders/Standard.ovfx

Lines changed: 65 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
#feature SHADOW_PASS
2+
#feature PARALLAX_MAPPING
3+
#feature ALPHA_CLIPPING
4+
#feature NORMAL_MAPPING
25

36
#shader vertex
47
#version 450 core
@@ -18,8 +21,10 @@ out VS_OUT
1821
vec2 TexCoords;
1922
vec3 Normal;
2023
mat3 TBN;
24+
#if defined(PARALLAX_MAPPING)
2125
flat vec3 TangentViewPos;
2226
vec3 TangentFragPos;
27+
#endif
2328
} vs_out;
2429

2530
#if defined(SHADOW_PASS)
@@ -32,8 +37,11 @@ void main()
3237
vs_out.TexCoords = geo_TexCoords;
3338
vs_out.Normal = normalize(mat3(transpose(inverse(ubo_Model))) * geo_Normal);
3439
vs_out.TBN = ConstructTBN(ubo_Model, geo_Normal, geo_Tangent, geo_Bitangent);
40+
41+
#if defined(PARALLAX_MAPPING)
3542
vs_out.TangentViewPos = transpose(vs_out.TBN) * ubo_ViewPos;
3643
vs_out.TangentFragPos = transpose(vs_out.TBN) * vs_out.FragPos;
44+
#endif
3745

3846
#if defined(SHADOW_PASS)
3947
gl_Position = _LightSpaceMatrix * vec4(vs_out.FragPos, 1.0);
@@ -54,22 +62,38 @@ in VS_OUT
5462
vec2 TexCoords;
5563
vec3 Normal;
5664
mat3 TBN;
65+
#if defined(PARALLAX_MAPPING)
5766
flat vec3 TangentViewPos;
5867
vec3 TangentFragPos;
68+
#endif
5969
} fs_in;
6070

61-
uniform vec2 u_TextureTiling = vec2(1.0, 1.0);
62-
uniform vec2 u_TextureOffset = vec2(0.0, 0.0);
71+
uniform sampler2D u_DiffuseMap;
72+
uniform sampler2D u_SpecularMap;
73+
uniform sampler2D u_MaskMap;
6374
uniform vec4 u_Diffuse = vec4(1.0, 1.0, 1.0, 1.0);
6475
uniform vec3 u_Specular = vec3(1.0, 1.0, 1.0);
76+
uniform vec2 u_TextureTiling = vec2(1.0, 1.0);
77+
uniform vec2 u_TextureOffset = vec2(0.0, 0.0);
6578
uniform float u_Shininess = 100.0;
66-
uniform float u_HeightScale = 0.0;
67-
uniform bool u_EnableNormalMapping = false;
68-
uniform sampler2D u_DiffuseMap;
69-
uniform sampler2D u_SpecularMap;
70-
uniform sampler2D u_NormalMap;
79+
80+
#if defined(PARALLAX_MAPPING)
7181
uniform sampler2D u_HeightMap;
72-
uniform sampler2D u_MaskMap;
82+
uniform bool u_ParallaxClipEdges = false;
83+
uniform float u_HeightScale = 0.05;
84+
#endif
85+
86+
#if defined(NORMAL_MAPPING)
87+
uniform sampler2D u_NormalMap;
88+
#endif
89+
90+
#if defined(ALPHA_CLIPPING)
91+
uniform float u_AlphaClippingThreshold = 0.1;
92+
#endif
93+
94+
#if defined(SHADOW_PASS)
95+
uniform float u_ShadowClippingThreshold = 0.5;
96+
#endif
7397

7498
uniform sampler2D _ShadowMap;
7599
uniform mat4 _LightSpaceMatrix;
@@ -78,23 +102,48 @@ out vec4 FRAGMENT_COLOR;
78102

79103
void main()
80104
{
105+
vec2 texCoords = TileAndOffsetTexCoords(fs_in.TexCoords, u_TextureTiling, u_TextureOffset);
106+
107+
#if defined(PARALLAX_MAPPING)
108+
texCoords = ApplyParallaxOcclusionMapping(texCoords, u_HeightMap, fs_in.TangentViewPos, fs_in.TangentFragPos, u_HeightScale);
109+
if (u_ParallaxClipEdges && (texCoords.x < 0.0 || texCoords.x > 1.0 || texCoords.y < 0.0 || texCoords.y > 1.0))
110+
{
111+
discard;
112+
}
113+
#endif
114+
115+
vec4 diffuse = texture(u_DiffuseMap, texCoords) * u_Diffuse;
116+
diffuse.a *= texture(u_MaskMap, texCoords).r;
117+
81118
#if defined(SHADOW_PASS)
82-
// Empty fragment shader for shadow pass
119+
if (diffuse.a < u_ShadowClippingThreshold)
120+
{
121+
discard;
122+
}
83123
#else
84-
vec2 texCoords = TileAndOffsetTexCoords(fs_in.TexCoords, u_TextureTiling, u_TextureOffset);
85-
texCoords = ApplyParallaxMapping(texCoords, u_HeightMap, fs_in.TangentViewPos, fs_in.TangentFragPos, u_HeightScale);
86124

87-
if (!IsMasked(u_MaskMap, texCoords))
125+
#if defined(ALPHA_CLIPPING)
126+
if (diffuse.a < u_AlphaClippingThreshold)
127+
{
128+
discard;
129+
}
130+
#endif
131+
132+
if (diffuse.a > 0.0)
88133
{
89-
vec3 normal = ComputeNormal(u_EnableNormalMapping, texCoords, fs_in.Normal, u_NormalMap, fs_in.TBN);
90-
FRAGMENT_COLOR = ComputeBlinnPhongLighting(
134+
#if defined(NORMAL_MAPPING)
135+
const vec3 normal = ComputeNormal(texCoords, fs_in.Normal, u_NormalMap, fs_in.TBN);
136+
#else
137+
const vec3 normal = normalize(fs_in.Normal);
138+
#endif
139+
140+
FRAGMENT_COLOR = ComputeBlinnPhongLightingNoDiffuseSampling(
91141
texCoords,
92142
normal,
93143
ubo_ViewPos,
94144
fs_in.FragPos,
95-
u_Diffuse,
145+
diffuse,
96146
u_Specular,
97-
u_DiffuseMap,
98147
u_SpecularMap,
99148
u_Shininess,
100149
_ShadowMap,

0 commit comments

Comments
 (0)