Skip to content

Commit 336bb6a

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 a4a2e7e commit 336bb6a

File tree

6 files changed

+223
-4
lines changed

6 files changed

+223
-4
lines changed

src/engine/renderer/gl_shader.cpp

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

578+
if ( r_sobelFiltering->integer )
579+
{
580+
AddDefine( str, "r_sobelFiltering", 1 );
581+
}
582+
578583
if ( r_wrapAroundLighting->value )
579584
AddConst( str, "r_WrapAroundLighting", r_wrapAroundLighting->value );
580585

@@ -1486,6 +1491,7 @@ GLShader_lightMapping::GLShader_lightMapping( GLShaderManager *manager ) :
14861491
GLDeformStage( this ),
14871492
GLCompileMacro_USE_DELUXE_MAPPING( this ),
14881493
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
1494+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
14891495
GLCompileMacro_USE_PARALLAX_MAPPING( this ),
14901496
GLCompileMacro_USE_PHYSICAL_SHADING( this )
14911497
{
@@ -1542,6 +1548,7 @@ GLShader_vertexLighting_DBS_entity::GLShader_vertexLighting_DBS_entity( GLShader
15421548
GLCompileMacro_USE_VERTEX_SKINNING( this ),
15431549
GLCompileMacro_USE_VERTEX_ANIMATION( this ),
15441550
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
1551+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
15451552
GLCompileMacro_USE_PARALLAX_MAPPING( this ),
15461553
GLCompileMacro_USE_REFLECTIVE_SPECULAR( this ),
15471554
GLCompileMacro_USE_PHYSICAL_SHADING( this )
@@ -1602,6 +1609,7 @@ GLShader_vertexLighting_DBS_world::GLShader_vertexLighting_DBS_world( GLShaderMa
16021609
u_Lights( this ),
16031610
GLDeformStage( this ),
16041611
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
1612+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
16051613
GLCompileMacro_USE_PARALLAX_MAPPING( this ),
16061614
GLCompileMacro_USE_PHYSICAL_SHADING( this )
16071615
{
@@ -1662,6 +1670,7 @@ GLShader_forwardLighting_omniXYZ::GLShader_forwardLighting_omniXYZ( GLShaderMana
16621670
GLCompileMacro_USE_VERTEX_SKINNING( this ),
16631671
GLCompileMacro_USE_VERTEX_ANIMATION( this ),
16641672
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
1673+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
16651674
GLCompileMacro_USE_PARALLAX_MAPPING( this ),
16661675
GLCompileMacro_USE_SHADOWING( this )
16671676
{
@@ -1722,6 +1731,7 @@ GLShader_forwardLighting_projXYZ::GLShader_forwardLighting_projXYZ( GLShaderMana
17221731
GLCompileMacro_USE_VERTEX_SKINNING( this ),
17231732
GLCompileMacro_USE_VERTEX_ANIMATION( this ),
17241733
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
1734+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
17251735
GLCompileMacro_USE_PARALLAX_MAPPING( this ),
17261736
GLCompileMacro_USE_SHADOWING( this )
17271737
{
@@ -1785,6 +1795,7 @@ GLShader_forwardLighting_directionalSun::GLShader_forwardLighting_directionalSun
17851795
GLCompileMacro_USE_VERTEX_SKINNING( this ),
17861796
GLCompileMacro_USE_VERTEX_ANIMATION( this ),
17871797
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
1798+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
17881799
GLCompileMacro_USE_PARALLAX_MAPPING( this ),
17891800
GLCompileMacro_USE_SHADOWING( this )
17901801
{
@@ -1869,6 +1880,7 @@ GLShader_reflection::GLShader_reflection( GLShaderManager *manager ):
18691880
GLCompileMacro_USE_VERTEX_SKINNING( this ),
18701881
GLCompileMacro_USE_VERTEX_ANIMATION( this ),
18711882
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
1883+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
18721884
GLCompileMacro_USE_PARALLAX_MAPPING( this )
18731885
{
18741886
}
@@ -2137,6 +2149,7 @@ GLShader_liquid::GLShader_liquid( GLShaderManager *manager ) :
21372149
u_LightGridOrigin( this ),
21382150
u_LightGridScale( this ),
21392151
GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP( this ),
2152+
GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP( this ),
21402153
GLCompileMacro_USE_PARALLAX_MAPPING( this )
21412154
{
21422155
}

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
{
@@ -2182,6 +2208,7 @@ class GLShader_lightMapping :
21822208
public GLDeformStage,
21832209
public GLCompileMacro_USE_DELUXE_MAPPING,
21842210
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2211+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
21852212
public GLCompileMacro_USE_PARALLAX_MAPPING,
21862213
public GLCompileMacro_USE_PHYSICAL_SHADING
21872214
{
@@ -2215,6 +2242,7 @@ class GLShader_vertexLighting_DBS_entity :
22152242
public GLCompileMacro_USE_VERTEX_SKINNING,
22162243
public GLCompileMacro_USE_VERTEX_ANIMATION,
22172244
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2245+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
22182246
public GLCompileMacro_USE_PARALLAX_MAPPING,
22192247
public GLCompileMacro_USE_REFLECTIVE_SPECULAR,
22202248
public GLCompileMacro_USE_PHYSICAL_SHADING
@@ -2247,6 +2275,7 @@ class GLShader_vertexLighting_DBS_world :
22472275
public u_Lights,
22482276
public GLDeformStage,
22492277
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2278+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
22502279
public GLCompileMacro_USE_PARALLAX_MAPPING,
22512280
public GLCompileMacro_USE_PHYSICAL_SHADING
22522281
{
@@ -2285,6 +2314,7 @@ class GLShader_forwardLighting_omniXYZ :
22852314
public GLCompileMacro_USE_VERTEX_SKINNING,
22862315
public GLCompileMacro_USE_VERTEX_ANIMATION,
22872316
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2317+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
22882318
public GLCompileMacro_USE_PARALLAX_MAPPING,
22892319
public GLCompileMacro_USE_SHADOWING //,
22902320
{
@@ -2324,6 +2354,7 @@ class GLShader_forwardLighting_projXYZ :
23242354
public GLCompileMacro_USE_VERTEX_SKINNING,
23252355
public GLCompileMacro_USE_VERTEX_ANIMATION,
23262356
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2357+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
23272358
public GLCompileMacro_USE_PARALLAX_MAPPING,
23282359
public GLCompileMacro_USE_SHADOWING //,
23292360
{
@@ -2365,6 +2396,7 @@ class GLShader_forwardLighting_directionalSun :
23652396
public GLCompileMacro_USE_VERTEX_SKINNING,
23662397
public GLCompileMacro_USE_VERTEX_ANIMATION,
23672398
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2399+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
23682400
public GLCompileMacro_USE_PARALLAX_MAPPING,
23692401
public GLCompileMacro_USE_SHADOWING //,
23702402
{
@@ -2414,6 +2446,7 @@ class GLShader_reflection :
24142446
public GLCompileMacro_USE_VERTEX_SKINNING,
24152447
public GLCompileMacro_USE_VERTEX_ANIMATION,
24162448
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2449+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
24172450
public GLCompileMacro_USE_PARALLAX_MAPPING
24182451
{
24192452
public:
@@ -2621,6 +2654,7 @@ class GLShader_liquid :
26212654
public u_LightGridOrigin,
26222655
public u_LightGridScale,
26232656
public GLCompileMacro_USE_HEIGHTMAP_IN_NORMALMAP,
2657+
public GLCompileMacro_USE_NORMALMAP_FROM_HEIGHTMAP,
26242658
public GLCompileMacro_USE_PARALLAX_MAPPING
26252659
{
26262660
public:

src/engine/renderer/glsl_source/reliefMapping_fp.glsl

Lines changed: 158 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,175 @@ 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+
59+
// Get texel size
60+
vec2 texelSize = 1.0 / vec2(textureSize(u_HeightMap, 0));
61+
62+
#if defined(r_sobelFiltering)
63+
/* Useful things to know:
64+
65+
- It's required to get height samples (S) surrounding the current texel (T) to do a sobel filter:
66+
67+
S S S
68+
S T S
69+
S S S
70+
71+
- Sobel X kernel is:
72+
73+
[ 1 0 -1 ]
74+
[ 2 0 -2 ]
75+
[ 1 0 -1 ]
76+
77+
See https://en.wikipedia.org/wiki/Sobel_operator#Formulation
78+
79+
- Sobel Y kernel is:
80+
81+
[ 1 2 1 ]
82+
[ 0 0 0 ]
83+
[ -1 -2 -1 ]
84+
85+
Which is the rotated sobel X kernel.
86+
87+
- Tangent space normals are +Z.
88+
89+
*/
90+
91+
ivec3 offsets;
92+
mat3 xCoords;
93+
mat3 yCoords;
94+
mat3 heights;
95+
mat3 sobel;
96+
97+
// Components will be computed by accumulating values.
98+
normal = vec3(0.0, 0.0, 0.0);
99+
100+
// Set offsets:
101+
offsets = ivec3(-1, 0, 1);
102+
103+
// Set sobel X kernel (beware, line is column with this notation):
104+
sobel[0] = vec3( 1.0, 2.0, 1.0);
105+
sobel[1] = vec3( 0.0, 0.0, 0.0);
106+
sobel[2] = vec3(-1.0, -2.0, -1.0);
107+
108+
for (int i = 0; i < 3; i++)
109+
{
110+
for (int j = 0; j < 3; j++)
111+
{
112+
if (i != 1 || j != 1)
113+
{
114+
// Compute coordinates:
115+
xCoords[i][j] = texNormal.x + texelSize.x * offsets[i];
116+
yCoords[i][j] = texNormal.y + texelSize.y * offsets[j];
117+
118+
// Get surrounding samples:
119+
heights[i][j] = abs(texture2D(u_HeightMap, vec2(xCoords[i][j], yCoords[i][j])).r);
120+
121+
if (i != 1)
122+
{
123+
// Sobel computation for X component (use the X sobel kernel):
124+
normal.x += heights[i][j] * sobel[i][j];
125+
}
126+
127+
if (j != 1)
128+
{
129+
// Sobel computation for Y component (use the rotated X sobel kernel as Y one):
130+
normal.y += heights[i][j] * sobel[j][i];
131+
}
132+
}
133+
}
134+
}
135+
136+
/* Reconstruct Z component while making sure to not
137+
take the square root of a negative number.
138+
That may occur because of compression artifacts.
139+
140+
Xonotic texture known to trigger black normalmap artifacts
141+
when doing Z reconstruction from X and Y computed from jpg
142+
heightmap:
143+
textures/map_glowplant/sand_bump
144+
*/
145+
146+
normal.z = sqrt(max(0.0, 1.0 - dot(normal.xy, normal.xy)));
147+
148+
normal.xy = 2.0 * normal.xy;
149+
#else // !r_sobelFiltering
150+
/* Useful thing to know:
151+
152+
- Only three samples are required to determine two vectors
153+
they would be used to generate the normal at this texel:
154+
155+
T S
156+
S
157+
158+
The texel itself is used as sample.
159+
*/
160+
161+
vec2 hOffsets;
162+
vec2 vOffsets;
163+
vec2 hCoords;
164+
vec2 vCoords;
165+
vec3 heights;
166+
vec3 xVector;
167+
vec3 yVector;
168+
169+
// Set horizontal and vertical offsets:
170+
hOffsets = vec2(texelSize.x, 0.0);
171+
vOffsets = vec2(0.0, texelSize.y);
172+
173+
// Compute coordinates:
174+
hCoords = vec2(texNormal.x + hOffsets.x, texNormal.y + hOffsets.y);
175+
vCoords = vec2(texNormal.x + vOffsets.x, texNormal.y + vOffsets.y);
176+
177+
// Get samples:
178+
heights.x = texture2D(u_HeightMap, texNormal).r; // No offset.
179+
heights.y = texture2D(u_HeightMap, hCoords).r;
180+
heights.z = texture2D(u_HeightMap, vCoords).r;
181+
182+
xVector = vec3(hOffsets.x, vOffsets.x, heights.y - heights.x);
183+
yVector = vec3(hOffsets.y, vOffsets.y, heights.z - heights.x);
184+
185+
normal = cross(xVector, yVector);
186+
#endif // !r_sobelFiltering
187+
188+
return normal;
189+
}
190+
#endif // USE_NORMALMAP_FROM_HEIGHTMAP
191+
40192
// compute normal in tangent space
41193
vec3 NormalInTangentSpace(vec2 texNormal)
42194
{
43195
vec3 normal;
44196

45197
#if defined(r_normalMapping)
46-
#if defined(USE_HEIGHTMAP_IN_NORMALMAP)
198+
#if defined(USE_NORMALMAP_FROM_HEIGHTMAP)
199+
normal = NormalFromHeightMap(texNormal);
200+
#elif defined(USE_HEIGHTMAP_IN_NORMALMAP)
47201
// alpha channel contains the height map so do not try to reconstruct normal map from it
48202
normal = texture2D(u_NormalMap, texNormal).rgb;
49203
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
@@ -2864,6 +2864,7 @@ static inline void halfToFloat( const f16vec4_t in, vec4_t out )
28642864
extern cvar_t *r_glowMapping;
28652865
extern cvar_t *r_reflectionMapping;
28662866

2867+
extern cvar_t *r_sobelFiltering;
28672868
extern cvar_t *r_wrapAroundLighting;
28682869
extern cvar_t *r_halfLambertLighting;
28692870
extern cvar_t *r_rimLighting;

0 commit comments

Comments
 (0)