Skip to content

Commit 50ed18e

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 63f9653 commit 50ed18e

File tree

6 files changed

+190
-4
lines changed

6 files changed

+190
-4
lines changed

src/engine/renderer/gl_shader.cpp

Lines changed: 12 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
}

src/engine/renderer/gl_shader.h

Lines changed: 33 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:

src/engine/renderer/glsl_source/reliefMapping_fp.glsl

Lines changed: 140 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@ 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
@@ -43,7 +44,142 @@ vec3 NormalInTangentSpace(vec2 texNormal)
4344
vec3 normal;
4445

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

176+
cvar_t *r_sobelFiltering;
176177
cvar_t *r_wrapAroundLighting;
177178
cvar_t *r_halfLambertLighting;
178179
cvar_t *r_rimLighting;
@@ -1207,6 +1208,7 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p
12071208
r_glowMapping = ri.Cvar_Get( "r_glowMapping", "1", CVAR_LATCH );
12081209
r_reflectionMapping = ri.Cvar_Get( "r_reflectionMapping", "0", CVAR_CHEAT );
12091210

1211+
r_sobelFiltering = ri.Cvar_Get( "r_sobelFiltering", "1", CVAR_LATCH );
12101212
r_wrapAroundLighting = ri.Cvar_Get( "r_wrapAroundLighting", "0.7", CVAR_CHEAT | CVAR_LATCH );
12111213
r_halfLambertLighting = ri.Cvar_Get( "r_halfLambertLighting", "1", CVAR_CHEAT | CVAR_LATCH );
12121214
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
@@ -2863,6 +2863,7 @@ static inline void halfToFloat( const f16vec4_t in, vec4_t out )
28632863
extern cvar_t *r_glowMapping;
28642864
extern cvar_t *r_reflectionMapping;
28652865

2866+
extern cvar_t *r_sobelFiltering;
28662867
extern cvar_t *r_wrapAroundLighting;
28672868
extern cvar_t *r_halfLambertLighting;
28682869
extern cvar_t *r_rimLighting;

src/engine/renderer/tr_shade.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,8 @@ static void Render_lightMapping( int stage )
12401240

12411241
gl_lightMappingShader->SetHeightMapInNormalMap( hasHeightMapInNormalMap );
12421242

1243+
gl_lightMappingShader->SetNormalMapFromHeightMap( hasHeightMap && !hasNormalMap );
1244+
12431245
gl_lightMappingShader->SetParallaxMapping( parallaxMapping );
12441246

12451247
tess.vboVertexSprite = false;

0 commit comments

Comments
 (0)