ferdinand list

[id tech 3] Water shading

A series of tests on shading water in id tech 3 including sample file |

I've known for a while that Hipshot has used q3map2's alpha blending features in order to fade out the edges of water volume in order to make the transition to lit surfaces more subtle and less contrasty (for instance on solitude). However, I never actually got to try it myself so I made this little sample map to get an overview of some water shading methods in id tech 3.

Post contents

Preface §

Origin of the investigation was Hipshot's water shader that consists of two grainy alpha channel textures blended on top of each other with a slight turb effect. Key here is the alphaGen vertex directive that makes the shader's alpha channel depend on q3map2 alpha mod shaders placed on the water volume's vertices.

// hipshot's water shader for alpha blending to zero

textures/ct_testWaterBlend/water_01
{
qer_editorimage textures/ct_testWaterBlend/hipshot_water.tga
q3map_globaltexture
surfaceparm trans
surfaceparm nonsolid
surfaceparm nomarks
surfaceparm nolightmap
nopicmip
noMipMaps
cull none
{
map textures/ct_testWaterBlend/hipshot_water.tga
blendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
rgbGen identity
alphaGen vertex
tcmod scale 1 1
tcMod turb 0 .15 0 .015
}
{
map textures/ct_testWaterBlend/hipshot_water2.tga
blendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
rgbGen identity
alphaGen vertex
tcmod scale 1 1
tcMod turb 0 -.15 0 -.015
}
}

Read more about alphaMod shaders here.
In case you are missing the alphaMod shaders, you can download them here. For reference, I included a sample shader below.

// alpha fade shader
// (c) 2004 randy reddig
// http://www.shaderlab.com
// distribution, in part or in whole, in any medium, permitted

textures/common/alpha_100
{
qer_trans 0.5
q3map_alphaMod volume
q3map_alphaMod scale 1.0
surfaceparm nodraw
surfaceparm nonsolid
surfaceparm trans
}

The alphaMod shaders are placed on small brush poles that get aligned with the vertices of the water volume to be alpha blended. Gathering the water volume brushes as well as the alphaMod shader brushes in a func_group will limit the blending effect to that particular group. See the sample map at the bottom of the page as an example.

Editor view



Alpha blending the edges of water volumes §

As a result of alpha blending the water volume edges, you receive a smooth transition to the neighboring materials. In this example, the water shader fades to zero. However any alpha channel intensity is possible to use.



Additional tests §

Below you can see additional tests that I executed.

Tests include:

The shader including a solid color stage can be an option for cases where you are already using a thicker level wide fog and want to have your water a little more diffuse without producing fog conflicts.

Here is the complete modification of hipshot's water shader including solid color stage and envmap stage.

// hipshot's water shader for alpha blending to zero
// with a solid color layer and an envmap

textures/ct_testWaterBlend/water_03
{
qer_editorimage textures/ct_testWaterBlend/hipshot_water.tga
q3map_globaltexture
surfaceparm trans
surfaceparm nonsolid
surfaceparm nomarks
surfaceparm nolightmap
nopicmip
noMipMaps
cull none
{
map textures/ct_testWaterBlend/hipshot_water.tga
blendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
rgbGen identity
alphaGen vertex
tcmod scale 1 1
tcMod turb 0 .15 0 .015
}
{
map textures/ct_testWaterBlend/hipshot_water2.tga
blendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
rgbGen identity
alphaGen vertex
tcmod scale 1 1
tcMod turb 0 -.15 0 -.015
}
{
map textures/ct_testWaterBlend/waterColor_02_50.tga
blendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
rgbGen identity
alphaGen vertex
}
{
map textures/ct_testWaterBlend/waterFX_01.tga
rgbGen identity
tcGen environment
blendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
alphaGen vertex
tcmod scale 0.5 0.5
}
}

A notable experiment to me is using an alpha channel controled solid color envmap. While the envmap image itself is just a solid light grey, the alpha channel is a ripple kind b/w texture as seen below. This method allows for controling the envmap color separately from the opacity.



Combining level wide fog and water fog §

As a part of this sample map I wanted to reinvestigate on the issue of using multiple fog volumes in a map. While combining two thick fog volumes produces rendering errors as seen in image 1, using a faint level wide fog along with a thick water fog is not actually problematic as seen in image 3. I am a fan of using thin level wide fog as it eases the contrast of the lightmap just a little and can be used to give the overall lighting a subtle tint. Comparing images 2 and 3 you can see that while in image 2 there are pitch black areas with actual information loss, in image 3 these areas get pulled up just a tiny bit revealing the underlaying geometry. Additionally, shadows in image 3 appear with a slight blue tint instead of just greyscale.

Conflicting fog volumes



Working with envmaps (update 2022-07-29) §

On a real water surface you would normally be able to observe a reflection of the environment, continuously distorted by the water's movement. The tool we have in id tech 3 to simulate this effect is using an tcgen environment shader stage, commonly referred to as envmap. The challenge here however is to bring in the element of distortion and not make the water surface look like a mirror.
I have extended the sample .map with a row of examples on how envmaps could be used, ranging from tesselation of flat surfaces with q3map2's tessSize over different deformVertex approaches and using actual tesselated deformed meshes created in blender in combination with deformVertex move and tcmod modifications of the envmap.

Envmap on mesh with tcmod turb

As a conclusion I would say that the most satisfying results are definitely produced by applying your water shader to a tesselated deformed mesh in combination with deformVertex move or tcmod turb on the envmap stage. While both approaches work, deformVertex move may actually not be feasible geometrically in some scenarios as the entire mesh will actually appear moving. In these cases using a turb, maybe in combination with a slight scroll or stretch, on the envmap is a fine compromise.

Worth mentioning as well is the approach of using deformVertex normal in order to produce an interesting simulated reflection. While the examples included in the sample map use a low tessSize of 16, good compromise in terms of performance may be found with a little higher tessSize. Overall these methods of shading water will take a hit on your tris count if used extensively. While I do not have any extensive details on the matter, using the mesh without the forceMeta flag, should keep it cheap in terms of performance.

Tesselated deformed mesh
The tesselated and deformed mesh with r_showtris 1


Water shader with deformVertex move used on mesh


Water shader with tcmod turb on the envmap used on mesh

Here is the complete turb shader used on the mesh, inclding alpha blended dirt stages.


// hipshot's water shader for alpha blending to zero
// with an envmap with tcmod turb
// for use on a tesselated mesh
textures/ct_testWaterBlend/water_tess_01-turb
{
qer_editorimage textures/ct_testWaterBlend/hipshot_water.tga
q3map_globaltexture
surfaceparm trans
surfaceparm nonsolid
surfaceparm nomarks
surfaceparm nolightmap
nopicmip
noMipMaps
cull none
deformVertexes wave 1000 sin -2 2 0 0.2
{
map textures/ct_testWaterBlend/hipshot_water.tga
blendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
rgbGen identity
alphaGen vertex
tcmod scale 1 1
tcMod turb 0 .15 0 .015
}
{
map textures/ct_testWaterBlend/hipshot_water2.tga
blendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
rgbGen identity
alphaGen vertex
tcmod scale 1 1
tcMod turb 0 -.15 0 -.015
}
{
map textures/ct_testWaterBlend/waterFX_02-green.tga
rgbGen identity
tcGen environment
blendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
alphaGen vertex
tcmod scale 2 2
tcMod turb 0 .025 0 .15
}
}

If you want to reside to a conventional envmap on brushes, you might want to check out tessSize whatsoever as the way q3map2 triangulates the geometry may affect the appearance of the envmap.

Lastly, one thing I did in fact not know was that alphaMod brushes can be used in a linear way instead of just placing them on single vertices. Additionally, if you want to alpha blend an imported mesh, you cannot have your alphaMod brushes in a func_group as models cannot be part of that group and thus the blending declarative will have no effect on any geometry. Leave everything as part of the worldspawn entity.

alphaMod brushes on mesh

Download §

This map's water shader and sky shader, as well as the corresponding materials were originally created by Hipshot. The .map source file is included with the pk3.
I included the mesh .obj used for the envmap tests with the pk3. Also included is the psd file for the envmap. Special thanks to quBit for giving me a lot of input on shading possiblities. Thanks to nigrum/garux for netradiant custom and answering a lot of my questions.