@@ -29,24 +29,181 @@ uniform sampler2D u_NormalMap;
29
29
uniform vec3 u_NormalScale;
30
30
#endif // r_normalMapping
31
31
32
- #if defined(USE_PARALLAX_MAPPING)
33
- #if ! defined(USE_HEIGHTMAP_IN_NORMALMAP)
32
+ #if (defined(USE_PARALLAX_MAPPING) && ! defined(USE_HEIGHTMAP_IN_NORMALMAP)) || defined(USE_NORMALMAP_FROM_HEIGHTMAP)
34
33
uniform sampler2D u_HeightMap;
35
- #endif // !USE_HEIGHTMAP_IN_NORMALMAP
34
+ #endif // (USE_PARALLAX_MAPPING && !USE_HEIGHTMAP_IN_NORMALMAP) || USE_NORMALMAP_FROM_HEIGHTMAP
35
+
36
+ #if defined(USE_PARALLAX_MAPPING)
36
37
uniform float u_ParallaxDepthScale;
37
38
uniform float u_ParallaxOffsetBias;
38
39
#endif // USE_PARALLAX_MAPPING
39
40
41
+ #if defined(USE_NORMALMAP_FROM_HEIGHTMAP)
42
+ vec3 NormalFromHeightMap(vec2 texNormal)
43
+ {
44
+ vec3 normal;
45
+
46
+ /* Major inspiration was this post by jollyjeffers:
47
+ https://www.gamedev.net/forums/topic/475213-generate-normal-map-from-heightmap-algorithm/#post_4117038
48
+
49
+ and this Wikipedia article:
50
+ https://en.wikipedia.org/wiki/Sobel_operator
51
+
52
+ Various people recommends doing abs() on sample read in case of float image format:
53
+ http://www.catalinzima.com/2008/01/converting-displacement-maps-into-normal-maps/
54
+ https://community.khronos.org/t/heightmap-to-normalmap/58862
55
+
56
+ Or report issues by not doing it:
57
+ https://gamedev.stackexchange.com/questions/165575/calculating-normal-map-from-height-map-using-sobel-operator
58
+
59
+ Other people say texture2D clamps it but it's safer to do this.
60
+ */
61
+
62
+ // Get texel size
63
+ vec2 texelSize = 1.0 / vec2 (textureSize(u_HeightMap, 0 ));
64
+
65
+ #if defined(r_sobelFiltering)
66
+ /* Useful things to know:
67
+ - It's required to get height samples (S) surrounding the current texel (T) to do a sobel filter:
68
+
69
+ S S S
70
+ S T S
71
+ S S S
72
+
73
+ - Sobel X kernel is:
74
+
75
+ [ 1 0 -1 ]
76
+ [ 2 0 -2 ]
77
+ [ 1 0 -1 ]
78
+
79
+ See https://en.wikipedia.org/wiki/Sobel_operator#Formulation
80
+
81
+ - Sobel Y kernel is:
82
+
83
+ [ 1 2 1 ]
84
+ [ 0 0 0 ]
85
+ [ -1 -2 -1 ]
86
+
87
+ Which is the rotated sobel X kernel.
88
+
89
+ - Tangent space normals are +Z.
90
+ */
91
+
92
+ ivec3 offsets;
93
+ mat3 xCoords;
94
+ mat3 yCoords;
95
+ mat3 heights;
96
+ mat3 sobel;
97
+
98
+ // Components will be computed by accumulating values.
99
+ normal = vec3 (0.0 , 0.0 , 0.0 );
100
+
101
+ // Set offsets:
102
+ offsets = ivec3 (- 1 , 0 , 1 );
103
+
104
+ // Set sobel X kernel (beware, line is column with this notation):
105
+ sobel[0 ] = vec3 ( 1.0 , 2.0 , 1.0 );
106
+ sobel[1 ] = vec3 ( 0.0 , 0.0 , 0.0 );
107
+ sobel[2 ] = vec3 (- 1.0 , - 2.0 , - 1.0 );
108
+
109
+ for (int i = 0 ; i < 3 ; i++ )
110
+ {
111
+ for (int j = 0 ; j < 3 ; j++ )
112
+ {
113
+ if (i != 1 || j != 1 )
114
+ {
115
+ // Compute coordinates:
116
+ xCoords[i][j] = texNormal.x + texelSize.x * offsets[i];
117
+ yCoords[i][j] = texNormal.y + texelSize.y * offsets[j];
118
+
119
+ // Get surrounding samples:
120
+ heights[i][j] = abs (texture2D (u_HeightMap, vec2 (xCoords[i][j], yCoords[i][j])).r);
121
+
122
+ if (i != 1 )
123
+ {
124
+ // Sobel computation for X component (use the X sobel kernel):
125
+ normal.x += heights[i][j] * sobel[i][j];
126
+ }
127
+
128
+ if (j != 1 )
129
+ {
130
+ // Sobel computation for Y component (use the rotated X sobel kernel as Y one):
131
+ normal.y += heights[i][j] * sobel[j][i];
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ /* Reconstruct Z component while making sure to not take the square root
138
+ of a negative number. That may occur because of compression artifacts.
139
+
140
+ Xonotic texture known to produce black normalmap artifacts when doing
141
+ Z reconstruction from X and Y computed from jpg heightmap:
142
+ textures/map_glowplant/sand_bump
143
+ */
144
+
145
+ normal.z = sqrt (max (0.0 , 1.0 - dot (normal.xy, normal.xy)));
146
+
147
+ normal.xy = 2.0 * normal.xy;
148
+ #else // !r_sobelFiltering
149
+ /* Useful thing to know:
150
+ - Only three samples are required to determine two vectors
151
+ they would be used to generate the normal at this texel:
152
+
153
+ T S
154
+ S
155
+
156
+ The texel itself is used as sample.
157
+ */
158
+
159
+ vec2 hOffsets;
160
+ vec2 vOffsets;
161
+ vec2 hCoords;
162
+ vec2 vCoords;
163
+ vec3 heights;
164
+ vec3 xVector;
165
+ vec3 yVector;
166
+
167
+ // Set horizontal and vertical offsets:
168
+ hOffsets = vec2 (texelSize.x, 0.0 );
169
+ vOffsets = vec2 (0.0 , texelSize.y);
170
+
171
+ // Compute coordinates:
172
+ hCoords = vec2 (texNormal.x + hOffsets.x, texNormal.y + hOffsets.y);
173
+ vCoords = vec2 (texNormal.x + vOffsets.x, texNormal.y + vOffsets.y);
174
+
175
+ // Get samples:
176
+ heights.x = texture2D (u_HeightMap, texNormal).r; // No offset.
177
+ heights.y = texture2D (u_HeightMap, hCoords).r;
178
+ heights.z = texture2D (u_HeightMap, vCoords).r;
179
+
180
+ xVector = vec3 (hOffsets.x, vOffsets.x, heights.y - heights.x);
181
+ yVector = vec3 (hOffsets.y, vOffsets.y, heights.z - heights.x);
182
+
183
+ normal = cross (xVector, yVector);
184
+ #endif // !r_sobelFiltering
185
+
186
+ return normal;
187
+ }
188
+ #endif // USE_NORMALMAP_FROM_HEIGHTMAP
189
+
40
190
// compute normal in tangent space
41
191
vec3 NormalInTangentSpace(vec2 texNormal)
42
192
{
43
193
vec3 normal;
44
194
45
195
#if defined(r_normalMapping)
46
- #if defined(USE_HEIGHTMAP_IN_NORMALMAP)
196
+ #if defined(USE_NORMALMAP_FROM_HEIGHTMAP)
197
+ normal = NormalFromHeightMap(texNormal);
198
+ #elif defined(USE_HEIGHTMAP_IN_NORMALMAP)
47
199
// alpha channel contains the height map so do not try to reconstruct normal map from it
48
200
normal = texture2D (u_NormalMap, texNormal).rgb;
49
201
normal = 2.0 * normal - 1.0 ;
202
+
203
+ // HACK: the GLSL code is currently assuming
204
+ // DirectX normal map format (+X -Y +Z)
205
+ // but engine is assuming the OpenGL way (+X +Y +Z)
206
+ normal.y *= - 1 ;
50
207
#else // !USE_HEIGHTMAP_IN_NORMALMAP
51
208
// the Capcom trick abusing alpha channel of DXT1/5 formats to encode normal map
52
209
// https://github.com/DaemonEngine/Daemon/issues/183#issuecomment-473691252
@@ -74,6 +231,11 @@ vec3 NormalInTangentSpace(vec2 texNormal)
74
231
// This might happen with other formats too. So we must take care not to
75
232
// take the square root of a negative number here.
76
233
normal.z = sqrt (max (0 , 1.0 - dot (normal.xy, normal.xy)));
234
+
235
+ // HACK: the GLSL code is currently assuming
236
+ // DirectX normal map format (+X -Y +Z)
237
+ // but engine is assuming the OpenGL way (+X +Y +Z)
238
+ normal.y *= - 1 ;
77
239
#endif // !USE_HEIGHTMAP_IN_NORMALMAP
78
240
/* Disable normal map scaling when normal Z scale is set to zero.
79
241
@@ -87,11 +249,6 @@ vec3 NormalInTangentSpace(vec2 texNormal)
87
249
{
88
250
normal *= u_NormalScale;
89
251
}
90
-
91
- // HACK: the GLSL code is currently assuming
92
- // DirectX normal map format (+X -Y +Z)
93
- // but engine is assuming the OpenGL way (+X +Y +Z)
94
- normal.y *= - 1 ;
95
252
#else // !r_normalMapping
96
253
// Flat normal map is {0.5, 0.5, 1.0} in [ 0.0, 1.0]
97
254
// which is stored as {0.0, 0.0, 1.0} in [-1.0, 1.0].
0 commit comments