Parallax mapping

From The DarkMod Wiki
Revision as of 21:28, 4 January 2025 by Stgatilov (talk | contribs)
Jump to navigationJump to search

This article describes how to set up a material for parallax mapping, which was added in TDM 2.13.


Material

The new parallax stage is a part of an interaction group, i.e. a group of stages which together define the reaction of the surface to the surrounding light sources. This sample material contains only one interaction group that consists of diffuse, bump, and parallax stages (no specular):

textures/test/test_parallax
{
    diffusemap       textures/test_parallax/parallax_test2_d
    bumpmap          textures/test_parallax/parallax_test_local2
    {
        blend parallaxmap
        map textures/test_parallax/parallax_test_height
        min -0.2
        max 0.0
    }
}

Note that both height/parallax map (parallax_test_height) and normal/bump map (parallax_test_local2) are necessary! Parallax stage alone only displaces the surface fragments according to the relief, but does not affect lighting in any way. You need bump stage with matching normal map in order to make lighting behave properly according to the relief.

The min and max parameters specify how the colors in heightmap are converted into physical displacements. In this specific case, fragments where heightmap = 1.0 (white) stay on the mesh geometry, while fragments where heightmap = 0.0 (black) are displaced inwards by 1/5 of texture tile length.

It is important to set correct value of (max - min), otherwise lighting will look incorrect. Normally, you should get this number from the software when you bake height and normal maps. And if you decide to change this value afterwards, then you need to rebake the normal map.


Amount of displacement

These parameters define the amount of displacements for full-black and full-white texels in the heightmap respectively:

min {expression} (default = -0.1)
max {expression} (default = 0.0)

Positive value corresponds to displacement along surface normal (outwards), while negative pushes the fragments in the opposite way (inwards).

The numbers specify the amount of displacement in UV space / in relative sense. If you have a quad of unit size which is covered by the texture exactly, then max = 0.35 will displace full-white texels by 0.35. If the same quad is resized to 800 doom units, then max = 0.35 will mean displacement by 0.35 * 800 = 280 doom units. So if you stretch the texture, the displacements are magnified proportionally in order to keep the match between heightmap and normalmap.

Generally speaking, the case max = 0 and min < 0 is the most useful, because it pushes the fragments inside the wall, which is usually solid and impassable by the player. Sometimes max > 0 might make more sense, but special case should be taken to ensure the player cannot cross the relief.

These parameters are the most important, and you need to set at least one of them explicitly in most cases.


Falling shadows

Parallax mapping is a hack, the mesh geometry is still used for many purposes, even inside rendering. For example, if you let a cubic crate case shadow onto parallax-mapped wall, the shadow edges are still straight lines by default, so the surface relief does not affect the shadows falling onto it.

This flag deforms the shadow falling onto the parallax-mapped surface according to the relief:

offsetExternalShadows {0 or 1}  (default = 0)

However, it has two additional caveats:

  1. It does not work with stencil shadows.
  2. It also disables shadows cast by the parallax-mapped surface itself.


Grazing angles

The parallax algorithm does not work well under grazing viewing angles. That's why the parallax effect is gradually reduced to zero for low angles.

It is controlled by the parameter:

grazingAngle {float}  (default = 0.3)

The parallax gets flattened under view angles lower than this value in radians.


Quality and complexity

The engine uses simple Parallax Occlusion Mapping algorithm, which intersects a ray with height map per each fragment. The raycasting algorithm is approximate and can miss intersections sometimes. This usually results in visual artifacts along the silhouette edges of relief, which look like staircases.

Displacement

These parameters controls how many iterations are performed:

linearSteps {integer}  (default = 20)
refineSteps {integer}  (default = 5)

The first parameter is more important. Increasing it reduces the number of missed intersections, making raycasting more reliable but slower. The second parameter makes detected intersections more precise, but does not help with missed intersections.

Normally, you should not touch this, but sometimes you might get away with low values without breakage.

Shadows

The self-shadows of parallax-mapped surface are rendered with special algorithm which involves raycasting again =) This is how many linear iterations are performed when searching for shadow ray intersections:

shadowSteps {integer}  (default = 20)

As usual, increasing it makes self-shadows more reliable but decreases performance. If the number of shadow steps gets too low, then penumbra will become dithered, which can be annoying.

Setting shadowSteps = 0 completely disables self-shadows.

The following parameters defines how soft self-shadows are:

shadowSoftness {expression}  (default = ?)

The default value is computed automatically from other parameters, and is around 0.005 if all the other parameters are at defaults. Increasing softness can also help hide dithering from low shadowSteps.

Caveats

The edges of parallax-mapped material usually look weird, especially when you more camera around. You can occlude the edges from the viewer with some other geometry. Or you can create such a heightmap which makes zero displacement on the edges.

As of now, parallax mapping only works on interactions, it does not affect the ambient stages of a material.