Skip to content

Commit f1e48ec

Browse files
committed
renderer: compute normalmap from heightmap when missing normalmap, aka bumpmap
compute normalmap from heightmap when missing normalmap, also known as bumpmapping two algorithms available: - one naive that does the job, uses 3 samples - one advanced that does better job, uses 8 samples (sobel operation) a new cvar is added: r_sobelFiltering, which would be used to disable or enable various sobel filtering operation including this one the engine is likely to implement other sobel filters in the future, for example DarkPlaces has a postprocessing effect that does such kind of compute, maybe Dæmon based games will want such kind of effect, in this case a common cvar to enable or disable various sobel compute does not look bad the r_sobelFiltering cvar is enabled by default
1 parent 5efa3f4 commit f1e48ec

File tree

6 files changed

+217
-4
lines changed

6 files changed

+217
-4
lines changed

src/engine/renderer/gl_shader.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,11 @@ static std::string GenEngineConstants() {
573573
AddConst( str, "MAX_GLSL_BONES", 4 );
574574
}
575575

576+
if ( r_sobelFiltering->integer )
577+
{
578+
AddDefine( str, "r_sobelFiltering", 1 );
579+
}
580+
576581
if ( r_wrapAroundLighting->value )
577582
AddConst( str, "r_WrapAroundLighting", r_wrapAroundLighting->value );
578583

@@ -1488,6 +1493,7 @@ GLShader_lightMapping::GLShader_lightMapping( GLShaderManager *manager ) :
14881493
GLDeformStage( this ),
14891494
GLCompileMacro_USE_DELUXE_MAPPING( this ),
14901495
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
1496+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
14911497
GLCompileMacro_USE_PARALLAX_MAPPING( this ),
14921498
GLCompileMacro_USE_PHYSICAL_SHADING( this )
14931499
{
@@ -1544,6 +1550,7 @@ GLShader_vertexLighting_DBS_entity::GLShader_vertexLighting_DBS_entity( GLShader
15441550
GLCompileMacro_USE_VERTEX_SKINNING( this ),
15451551
GLCompileMacro_USE_VERTEX_ANIMATION( this ),
15461552
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
1553+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
15471554
GLCompileMacro_USE_PARALLAX_MAPPING( this ),
15481555
GLCompileMacro_USE_REFLECTIVE_SPECULAR( this ),
15491556
GLCompileMacro_USE_PHYSICAL_SHADING( this )
@@ -1604,6 +1611,7 @@ GLShader_vertexLighting_DBS_world::GLShader_vertexLighting_DBS_world( GLShaderMa
16041611
u_Lights( this ),
16051612
GLDeformStage( this ),
16061613
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
1614+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
16071615
GLCompileMacro_USE_PARALLAX_MAPPING( this ),
16081616
GLCompileMacro_USE_PHYSICAL_SHADING( this )
16091617
{
@@ -1664,6 +1672,7 @@ GLShader_forwardLighting_omniXYZ::GLShader_forwardLighting_omniXYZ( GLShaderMana
16641672
GLCompileMacro_USE_VERTEX_SKINNING( this ),
16651673
GLCompileMacro_USE_VERTEX_ANIMATION( this ),
16661674
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
1675+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
16671676
GLCompileMacro_USE_PARALLAX_MAPPING( this ),
16681677
GLCompileMacro_USE_SHADOWING( this )
16691678
{
@@ -1724,6 +1733,7 @@ GLShader_forwardLighting_projXYZ::GLShader_forwardLighting_projXYZ( GLShaderMana
17241733
GLCompileMacro_USE_VERTEX_SKINNING( this ),
17251734
GLCompileMacro_USE_VERTEX_ANIMATION( this ),
17261735
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
1736+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
17271737
GLCompileMacro_USE_PARALLAX_MAPPING( this ),
17281738
GLCompileMacro_USE_SHADOWING( this )
17291739
{
@@ -1787,6 +1797,7 @@ GLShader_forwardLighting_directionalSun::GLShader_forwardLighting_directionalSun
17871797
GLCompileMacro_USE_VERTEX_SKINNING( this ),
17881798
GLCompileMacro_USE_VERTEX_ANIMATION( this ),
17891799
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
1800+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
17901801
GLCompileMacro_USE_PARALLAX_MAPPING( this ),
17911802
GLCompileMacro_USE_SHADOWING( this )
17921803
{
@@ -1871,6 +1882,7 @@ GLShader_reflection::GLShader_reflection( GLShaderManager *manager ):
18711882
GLCompileMacro_USE_VERTEX_SKINNING( this ),
18721883
GLCompileMacro_USE_VERTEX_ANIMATION( this ),
18731884
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
1885+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
18741886
GLCompileMacro_USE_PARALLAX_MAPPING( this )
18751887
{
18761888
}
@@ -2139,6 +2151,7 @@ GLShader_liquid::GLShader_liquid( GLShaderManager *manager ) :
21392151
u_LightGridOrigin( this ),
21402152
u_LightGridScale( this ),
21412153
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
2154+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
21422155
GLCompileMacro_USE_PARALLAX_MAPPING( this )
21432156
{
21442157
}

src/engine/renderer/gl_shader.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,7 @@ class GLCompileMacro
785785
USE_TCGEN_LIGHTMAP,
786786
USE_DELUXE_MAPPING,
787787
USE_HEIGHTMAP_IN_NORMALMAP,
788+
USE_NORMALMAP_FROM_HEIGHTMAP,
788789
USE_PARALLAX_MAPPING,
789790
USE_REFLECTIVE_SPECULAR,
790791
USE_SHADOWING,
@@ -1035,6 +1036,31 @@ class GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP :
10351036
}
10361037
};
10371038

1039+
class GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP :
1040+
GLCompileMacro
1041+
{
1042+
public:
1043+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( GLShader *shader ) :
1044+
GLCompileMacro( shader )
1045+
{
1046+
}
1047+
1048+
const char *GetName() const
1049+
{
1050+
return "USE_NORMALMAP_FROM_HEIGHTMAP";
1051+
}
1052+
1053+
EGLCompileMacro GetType() const
1054+
{
1055+
return EGLCompileMacro::USE_NORMALMAP_FROM_HEIGHTMAP;
1056+
}
1057+
1058+
void SetNormalMapFromHeightMap( bool enable )
1059+
{
1060+
SetMacro( enable );
1061+
}
1062+
};
1063+
10381064
class GLCompileMacro_USE_PARALLAX_MAPPING :
10391065
GLCompileMacro
10401066
{
@@ -2163,6 +2189,7 @@ class GLShader_lightMapping :
21632189
public GLDeformStage,
21642190
public GLCompileMacro_USE_DELUXE_MAPPING,
21652191
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2192+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
21662193
public GLCompileMacro_USE_PARALLAX_MAPPING,
21672194
public GLCompileMacro_USE_PHYSICAL_SHADING
21682195
{
@@ -2196,6 +2223,7 @@ class GLShader_vertexLighting_DBS_entity :
21962223
public GLCompileMacro_USE_VERTEX_SKINNING,
21972224
public GLCompileMacro_USE_VERTEX_ANIMATION,
21982225
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2226+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
21992227
public GLCompileMacro_USE_PARALLAX_MAPPING,
22002228
public GLCompileMacro_USE_REFLECTIVE_SPECULAR,
22012229
public GLCompileMacro_USE_PHYSICAL_SHADING
@@ -2228,6 +2256,7 @@ class GLShader_vertexLighting_DBS_world :
22282256
public u_Lights,
22292257
public GLDeformStage,
22302258
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2259+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
22312260
public GLCompileMacro_USE_PARALLAX_MAPPING,
22322261
public GLCompileMacro_USE_PHYSICAL_SHADING
22332262
{
@@ -2266,6 +2295,7 @@ class GLShader_forwardLighting_omniXYZ :
22662295
public GLCompileMacro_USE_VERTEX_SKINNING,
22672296
public GLCompileMacro_USE_VERTEX_ANIMATION,
22682297
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2298+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
22692299
public GLCompileMacro_USE_PARALLAX_MAPPING,
22702300
public GLCompileMacro_USE_SHADOWING //,
22712301
{
@@ -2305,6 +2335,7 @@ class GLShader_forwardLighting_projXYZ :
23052335
public GLCompileMacro_USE_VERTEX_SKINNING,
23062336
public GLCompileMacro_USE_VERTEX_ANIMATION,
23072337
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2338+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
23082339
public GLCompileMacro_USE_PARALLAX_MAPPING,
23092340
public GLCompileMacro_USE_SHADOWING //,
23102341
{
@@ -2346,6 +2377,7 @@ class GLShader_forwardLighting_directionalSun :
23462377
public GLCompileMacro_USE_VERTEX_SKINNING,
23472378
public GLCompileMacro_USE_VERTEX_ANIMATION,
23482379
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2380+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
23492381
public GLCompileMacro_USE_PARALLAX_MAPPING,
23502382
public GLCompileMacro_USE_SHADOWING //,
23512383
{
@@ -2395,6 +2427,7 @@ class GLShader_reflection :
23952427
public GLCompileMacro_USE_VERTEX_SKINNING,
23962428
public GLCompileMacro_USE_VERTEX_ANIMATION,
23972429
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2430+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
23982431
public GLCompileMacro_USE_PARALLAX_MAPPING
23992432
{
24002433
public:
@@ -2602,6 +2635,7 @@ class GLShader_liquid :
26022635
public u_LightGridOrigin,
26032636
public u_LightGridScale,
26042637
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2638+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
26052639
public GLCompileMacro_USE_PARALLAX_MAPPING
26062640
{
26072641
public:

src/engine/renderer/glsl_source/reliefMapping_fp.glsl

Lines changed: 152 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,169 @@ uniform sampler2D u_NormalMap;
2929
uniform vec3 u_NormalScale;
3030
#endif // r_normalMapping
3131

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)
3433
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)
3637
uniform float u_ParallaxDepthScale;
3738
uniform float u_ParallaxOffsetBias;
3839
#endif // USE_PARALLAX_MAPPING
3940

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+
// and this Wikipedia article:
49+
// https://en.wikipedia.org/wiki/Sobel_operator
50+
//
51+
// Various people recommends doing abs() on sample read in case of float image format:
52+
// http://www.catalinzima.com/2008/01/converting-displacement-maps-into-normal-maps/
53+
// https://community.khronos.org/t/heightmap-to-normalmap/58862
54+
// Or report issues by not doing it:
55+
// https://gamedev.stackexchange.com/questions/165575/calculating-normal-map-from-height-map-using-sobel-operator
56+
// Other people say texture2D clamps it but it's safer to do this.
57+
58+
// Get texel size
59+
vec2 texelSize = 1.0 / vec2(textureSize(u_HeightMap, 0));
60+
61+
#if defined(r_sobelFiltering)
62+
// Useful things to know:
63+
//
64+
// * It's required to get height samples (S) surrounding the current texel (T) to do a sobel filter:
65+
//
66+
// S S S
67+
// S T S
68+
// S S S
69+
//
70+
// * Sobel X kernel is:
71+
//
72+
// [ 1 0 -1 ]
73+
// [ 2 0 -2 ]
74+
// [ 1 0 -1 ]
75+
//
76+
// See https://en.wikipedia.org/wiki/Sobel_operator#Formulation
77+
//
78+
// * Sobel Y kernel is:
79+
//
80+
// [ 1 2 1 ]
81+
// [ 0 0 0 ]
82+
// [ -1 -2 -1 ]
83+
//
84+
// Which is the rotated sobel X kernel.
85+
//
86+
// * Tangent space normals are +Z.
87+
88+
ivec3 offsets;
89+
mat3 xCoords;
90+
mat3 yCoords;
91+
mat3 heights;
92+
mat3 sobel;
93+
94+
// Components will be computed by accumulating values.
95+
normal = vec3(0.0, 0.0, 0.0);
96+
97+
// Set offsets:
98+
offsets = ivec3(-1, 0, 1);
99+
100+
// Set sobel X kernel (beware, line is column with this notation):
101+
sobel[0] = vec3( 1.0, 2.0, 1.0);
102+
sobel[1] = vec3( 0.0, 0.0, 0.0);
103+
sobel[2] = vec3(-1.0, -2.0, -1.0);
104+
105+
for (int i = 0; i < 3; i++)
106+
{
107+
for (int j = 0; j < 3; j++)
108+
{
109+
if (i != 1 || j != 1)
110+
{
111+
// Compute coordinates:
112+
xCoords[i][j] = texNormal.x + texelSize.x * offsets[i];
113+
yCoords[i][j] = texNormal.y + texelSize.y * offsets[j];
114+
115+
// Get surrounding samples:
116+
heights[i][j] = abs(texture2D(u_HeightMap, vec2(xCoords[i][j], yCoords[i][j])).r);
117+
118+
if (i != 1)
119+
{
120+
// Sobel computation for X component (use the X sobel kernel):
121+
normal.x += heights[i][j] * sobel[i][j];
122+
}
123+
124+
if (j != 1)
125+
{
126+
// Sobel computation for Y component (use the rotated X sobel kernel as Y one):
127+
normal.y += heights[i][j] * sobel[j][i];
128+
}
129+
}
130+
}
131+
}
132+
133+
// Reconstruct Z component while making sure to not
134+
// take the square root of a negative number.
135+
// That may occur because of compression artifacts
136+
//
137+
// Xonotic texture known to trigger black normalmap artifacts
138+
// when doing Z reconstruction from X and Y computed from jpg
139+
// heightmap:
140+
// textures/map_glowplant/sand_bump
141+
normal.z = sqrt(max(0.0, 1.0 - dot(normal.xy, normal.xy)));
142+
143+
normal.xy = 2.0 * normal.xy;
144+
#else // !r_sobelFiltering
145+
// Useful thing to know:
146+
//
147+
// * Only three samples are required to determine two vectors
148+
// they would be used to generate the normal at this texel:
149+
//
150+
// T S
151+
// S
152+
//
153+
// The texel itself is used as sample.
154+
155+
vec2 hOffsets;
156+
vec2 vOffsets;
157+
vec2 hCoords;
158+
vec2 vCoords;
159+
vec3 heights;
160+
vec3 xVector;
161+
vec3 yVector;
162+
163+
// Set horizontal and vertical offsets:
164+
hOffsets = vec2(texelSize.x, 0.0);
165+
vOffsets = vec2(0.0, texelSize.y);
166+
167+
// Compute coordinates:
168+
hCoords = vec2(texNormal.x + hOffsets.x, texNormal.y + hOffsets.y);
169+
vCoords = vec2(texNormal.x + vOffsets.x, texNormal.y + vOffsets.y);
170+
171+
// Get samples:
172+
heights.x = texture2D(u_HeightMap, texNormal).r; // No offset.
173+
heights.y = texture2D(u_HeightMap, hCoords).r;
174+
heights.z = texture2D(u_HeightMap, vCoords).r;
175+
176+
xVector = vec3(hOffsets.x, vOffsets.x, heights.y - heights.x);
177+
yVector = vec3(hOffsets.y, vOffsets.y, heights.z - heights.x);
178+
179+
normal = cross(xVector, yVector);
180+
#endif // !r_sobelFiltering
181+
182+
return normal;
183+
}
184+
#endif // USE_NORMALMAP_FROM_HEIGHTMAP
185+
40186
// compute normal in tangent space
41187
vec3 NormalInTangentSpace(vec2 texNormal)
42188
{
43189
vec3 normal;
44190

45191
#if defined(r_normalMapping)
46-
#if defined(USE_HEIGHTMAP_IN_NORMALMAP)
192+
#if defined(USE_NORMALMAP_FROM_HEIGHTMAP)
193+
normal = NormalFromHeightMap(texNormal);
194+
#elif defined(USE_HEIGHTMAP_IN_NORMALMAP)
47195
// alpha channel contains the height map so do not try to reconstruct normal map from it
48196
normal = texture2D(u_NormalMap, texNormal).rgb;
49197
normal = 2.0 * normal - 1.0;

src/engine/renderer/tr_init.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
172172
cvar_t *r_glowMapping;
173173
cvar_t *r_reflectionMapping;
174174

175+
cvar_t *r_sobelFiltering;
175176
cvar_t *r_wrapAroundLighting;
176177
cvar_t *r_halfLambertLighting;
177178
cvar_t *r_rimLighting;
@@ -1205,6 +1206,7 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p
12051206
r_glowMapping = ri.Cvar_Get( "r_glowMapping", "1", CVAR_LATCH );
12061207
r_reflectionMapping = ri.Cvar_Get( "r_reflectionMapping", "0", CVAR_CHEAT );
12071208

1209+
r_sobelFiltering = ri.Cvar_Get( "r_sobelFiltering", "1", CVAR_LATCH );
12081210
r_wrapAroundLighting = ri.Cvar_Get( "r_wrapAroundLighting", "0.7", CVAR_CHEAT | CVAR_LATCH );
12091211
r_halfLambertLighting = ri.Cvar_Get( "r_halfLambertLighting", "1", CVAR_CHEAT | CVAR_LATCH );
12101212
r_rimLighting = ri.Cvar_Get( "r_rimLighting", "0", CVAR_LATCH | CVAR_ARCHIVE );

src/engine/renderer/tr_local.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2862,6 +2862,7 @@ static inline void halfToFloat( const f16vec4_t in, vec4_t out )
28622862
extern cvar_t *r_glowMapping;
28632863
extern cvar_t *r_reflectionMapping;
28642864

2865+
extern cvar_t *r_sobelFiltering;
28652866
extern cvar_t *r_wrapAroundLighting;
28662867
extern cvar_t *r_halfLambertLighting;
28672868
extern cvar_t *r_rimLighting;

0 commit comments

Comments
 (0)