Skip to content

Shaders

The Turtle Man ♣ edited this page Oct 24, 2023 · 82 revisions

New shader features

New shader features not found in Quake III Arena 1.32b or ioquake3.

For quake3/q3map2 features see shader manual at robotrenegade.com.

If a keyword says "UNIMPLEMENTED", it means the keyword is suppose to do something but Spearmint just ignores it. Not that the original game did not implement it.

Notable renderer changes

Not strictly speaking related to shader files, but possibly useful to know.

Bmodels are Fogged (Origin: RTCW)

Brush models (func_static, movers, etc) are now fogged.

Support for dynamic Bmodel customShader (Origin: RTCW)

Setting refEntity_t::customShader on brush models now forces all shaders to the custom shader.

Marks on Bmodels (Origin: ET / Spearmint)

Bullet/explosion marks and player shadow blob are now added on bmodels. Use surfaceParm nomarks to disable them.

Implicit 2D shaders can be used in 3D space (Origin: Spearmint 0.2)

Images without an explicit shader that are registed as 2D (i.e., using trap_R_RegisterShader()) for item icons etc do not use depth test in Quake 3 so if drawn in 3D world they're always visible. In Spearmint they use depth test like explicit shaders do by default.

Shader Syntax

Quake 3 requires parentheses in some cases such as around colors (rgbGen const ( 0 0 1 ), fogparms ( 1 1 1 ) 512). FAKK, Alice, MoHAA, and EF2 do not use them, so in Spearmint they are optional. I'm not sure if q3map2 supports not using parentheses, so it's probably best to use them.

Global/Stage Directives

These can be used anywhere in a shader's content (including in shader stages).

if (Origin: FAKK, extended by Alice, EF2, and Spearmint)

Using #if and #endif (or if and endif like in FAKK, Alice, and EF2) you can change what is loaded from a shader.

Note: q3map2 ignores #if, if, #endif, and endif and reads everything inside them. q3map2 will use the first image it finds specified for map, animmap, clampmap, implicitMap, skyparms (up image), etc for lighting. (if finds implicit* first, uses image from last implicit* for lighting.) You can force light image using qer_editorImage or q3map_lightImage before shader stages if needed.

Example: (custom shader stage for r_vertexlight as renderer forces shaders to only 1 shader stage.)

textures/name
{
  mipmaps // general options
#if novertexlight
  ... shader stages
#endif
#if vertexlight
  ... shader stage
#endif
}

Keywords

  • 0 Always skip if-block. (Origin: FAKK)
  • 1 Never skip if-block. (Origin: FAKK)
  • shaderlod <float-value> -- check if r_shaderlod is more than <float-value>. (Origin: Alice)
  • mtex / no_mtex -- check if r_ext_multitexture is enabled and supported by graphics driver. (Origin: FAKK)
  • [no]vertexlight -- check if r_vertexLight is enabled and allowed for graphics driver. (Origin: EF2)
  • [no]detail -- check if r_detailTextures is enabled. (Origin: EF2)

mtex, vertexlight, and detail also support ! prefix in Spearmint that works the same as no prefix.

Cvars

If the token after the if is not a recognized keyword, Spearmint assumes it's a console variable name.

Examples:

  • if com_blood -- check if cvar exists and is not 0.
  • if !com_blood, if nocom_blood -- check if cvar does not exist or is 0.

There is currently no way to compare cvars to values besides 0.

else (Origin: Spearmint)

Using #else or else you can avoid write opposite if statements.

Example: (custom shader stage for r_vertexlight as renderer forces shaders to only 1 shader stage.)

textures/name
{
  mipmaps // general options
#if novertexlight
  ... shader stages
#else
  ... shader stage
#endif
}

endif (Origin: FAKK)

See if.

General Directives

These are usable outside shader stages.

aliasShader (Origin: SoF2)

Usage: aliasShader <shader>

If r_aliasShaders cvar is 1, the shader listed for aliasShader will be used. This can be used to reduce unique shaders used on a map. Put it first or near the beginning of the shader text.

Example:

textures/wall/metal1
{
    surfaceParm metalsteps
    implicitMap -
}

textures/wall/metal2
{
    aliasShader textures/wall/metal1
    surfaceParm metalsteps
    implicitMap -
}

textures/wall/metal3
{
    aliasShader textures/wall/metal1
    surfaceParm metalsteps
    implicitMap -
}

nomipmap (Origin: RTCW MP)

It's the same as nomipmaps keyword.

picmip2 (Origin: RTCW SP)

Use r_picmip2 cvar for picmip level instead of r_picmip cvar. In RTCW SP this is used for character models.

lightgridmulamb (Origin: RTCW SP)

Usage: lightgridmulamb <multiplier>

Specify a value to multiple the ambient light used by models. It does not affect lightmaps.

lightgridmuldir (Origin: RTCW SP)

Usage: lightgridmuldir <multiplier>

Specify a value to multiple the directed light used by models. It does not affect lightmaps.

sunShader (Origin: RTCW, extend by Spearmint)

Set sun shader and (new in Spearmint) optionally set sun shader scale.

Usage: sunShader <shader name> [scale]

Placement of the sun is determined by q3map_sun shader setting. q3map_sun <red> <green> <blue> <intensity> <degrees> <elevation>

Scale must be more than zero. There is no upper limit. If no scale is specified, defaults to RTCW / ET scale — 1.0. You can test different scales using the r_forceSunScale console variable, it overrides sunShader scale.

game scale
Q3 (sun drawing disabled) 2.0
RTCW / ET 1.0
Reaction / ioquake3 0.5

noFog (Origin: RTCW)

Do not apply fog effect to shader.

Usage: noFog

Also see fog on stage directive.

fogParms (Origin: Q3, changed by WolfET, extended by Spearmint)

Fog volume using exponential or linear fog.

Usage: fogParms ( <red> <green> <blue> ) <depthForOpaque> [fog type]

red/green/blue should be in range of 0.0 to 1.0.

Spearmint Extension: Fog type can be "exp" (used by Q3, RTCW, etc) or "linear" (used by ET). If fog type isn't specified, the type set by r_defaultFogParmsType is used. The Spearmint ET patch data sets r_defaultFogParmsType to linear, allowing fogParms to act the same as in ET.

Note: In Wolfenstein Enemy Territory, fogParms was changed from exponential fog to linear fog. Spearmint allows specifying fog type plus a cvar for legacy fogParms.

fogvars (Origin: RTCW, extra info about ET)

Sets the level's global fog. Might be best to put in sky shader as there should only be one 'fogvars' per-map, but you can put it in any shader.

Usage: fogvars ( <red> <green> <blue> ) <density <= 1 or depth for opaque > 1>

red/green/blue should be in range of 0.0 to 1.0.

If the last value is less than or equal to 1, it is the density for exponential fog, otherwise it is the distance for linear fog (depth at which fog is opaque).

In RTCW, the linear fog color was used instead of drawing the sky (which would be visible when the level was far clipped). In ET, the sky is cleared to the linear fog color and then the sky if drawn if possible. Spearmint defaults to ET's behavior but due to RTCW's shaders containing broken sky stages in maps with global linear fog such as norway there is a cvar r_globalLinearFogDrawSky to disable drawing the sky. (r_globalLinearFogDrawSky was added after Spearmint 0.2.)

Another option for adding global fog is to use "_fog" or "fog" key set to a global fog shader name in worldspawn which q3map2 turns into global fog in the BSP. Fogvars seems simpler to use, ET style global fog is fully supported for ET map compatibility however.

As far as I know, there are no benefits for using ET style global fog in Spearmint. Apparently it was easier to add global fog for ET in q3map2 than in the engine. Spearmint adds a fake global fog for maps using fogvars, so the result is the same. fogvars in RTCW/ET use OpenGL fogging, whereas in ET style global fog was software. Both are software in Spearmint, based on the ET code.

viewfogvars (Origin: Spearmint)

Override global fog and water fog when view point is inside brush this shader is applied to. This only works if applied to a separate brush entity, such as func_static, not when used on a worldspawn brush.

Usage: viewfogvars ( <red> <green> <blue> ) <density <= 1 or depth for opaque > 1 or 0 for using setting from global fog>

red/green/blue should be in range of 0.0 to 1.0.

If the last value is 0, the value from the global fog is used (allowing just the color to be changed). If map has no global fog, then view fog will have no effect either.

Like fogvars; If the last value is less than or equal to 1, it is the density for exponential fog. If the last value is more than 1, it is the distance for linear fog (depth at which fog is opaque).

If linear fog is used the sky is visible when the map is far clipped unless r_globalLinearFogDrawSky is disabled. (r_globalLinearFogDrawSky was added in Spearmint 0.3.)

waterfogvars (Origin: RTCW, extend by Spearmint)

Sets the default underwater view fog. For per-volume view fog use viewfogvars.

Usage: waterfogvars ( <red> <green> <blue> ) <density <= 1 or depth for opaque > 1 or 0 for using setting from global fog>

red/green/blue should be in range of 0.0 to 1.0.

New in Spearmint (there are TODOs for this in RTCW): If the last value is 0, the value from the global fog is used allowing just the color to be changed. If map has no global fog, then water fog will have no effect either.

Like fogvars; If the last value is less than or equal to 1, it is the density for exponential fog. If the last value is more than 1, it is the distance for linear fog (depth at which fog is opaque).

In RTCW, the linear fog color was used instead of drawing the sky (which would be visible when the level was far clipped). In Spearmint, the sky is drawn unless r_globalLinearFogDrawSky is disabled. (r_globalLinearFogDrawSky was added in Spearmint 0.3.)

Note: You cannot just change waterfogvars to viewfogvars in a shader. RTCW/ET did not require water to be a separate brush (only supported one water fog per-map) so maps probably require changes to use viewfogvars.

skyfogvars (Origin: RTCW)

Sets the sky fog.

Usage: skyfogvars ( <red> <green> <blue> ) <density>

red/green/blue/density should be in range of 0.0 to 1.0.

Unlike other fogvars settings, linear fog (value more > 1) is not support for sky because it's "infinity far away" so linear fog would just be a solid color.

Note: In RTCW (and Spearmint) the last parsed skyfogvars effects all sky shaders used in the map.

implicitMap (Origin: ET)

For simple opaque shaders, use for images that are opaque.

implicit* keywords replace the need for writing simple shader stages, which are easily messed up.

Usage

  • implicitMap - (uses shader name for image name)
  • implicitMap path/to/image (specify image name)

Common usage:

textures/path/to/cool
{
	surfaceparm metalsteps
	implicitMap -
}

implicitMask (Origin: ET)

For simple masked shaders, use for images that you want to see alpha 128 to 255.

Usage

  • implicitMask - (uses shader name for image name)
  • implicitMask path/to/image (specify image name)

Common usage for map textures: (should probably delete the comments if you use this)

textures/path/to/coolness
{
	cull twosided // render front and back of image (implied by implicitMask in engine, but I think it's still needed for q3map2)
	nomipmaps // do not blur image, may or may not look good depending on the image. Usually map textures have mipmaps enabled
	surfaceparm nomarks // no bullet marks, useful if you want to give the illusion that the invisible part of the image "does not exist" in the world
	surfaceparm alphashadow // tell q3map2 to cast alpha-blend shadows
	surfaceparm pointlight // tell q3map2 to make this vertex lit instead of lightmapped, quite a few WolfET masked shaders use this
	surfaceparm trans // tell q3map2 to compute visibility through this image.
	//surfaceparm nonsolid // enable this if you want game entities to be able to move through this.
	implicitMask -
}

Common usage for other shaders

textures/path/to/coolness
{
	nomipmaps // do not blur image, may or may not look good depending on the image. Usually map textures have mipmaps enabled
	implicitMask -
}

implicitBlend (Origin: ET)

For simple transparent shaders, use for images that you want to see full alpha (0 to 255).

Usage

  • implicitBlend - (uses shader name for image name)
  • implicitBlend path/to/image (specify image name)

The usage examples for implicitMask should work for implicitBlend too.

A notable difference compared to using a stage with blendFunc blend (or any other blend function) is that implicitBlend enables depthwrite. This tells OpenGL not to draw any pixels behind the shader. So depending the order things are drawn in it might not be possible to see things through the transparent part of the shader.

distanceCull (Origin: ET)

distanceCull is used for foliage.

Usage: distanceCull <opaque distance> <transparent distance> <alpha threshold>

force32bit (Origin: FAKK)

UNIMPLEMENTED. Keyword is ignored.

It's suppose to force textures to use 32 bit if supported by hardware, regardless of texture quality cvar setting (r_texturebits).

allowCompress (Origin: RTCW)

If r_ext_compressed_textures cvar is 2 only shaders with allowCompress will be compressed.

Supported by Spearmint 0.2+. In Spearmint 0.1 it's ignored without warning.

noCompress (Origin: RTCW)

No texture compression even if r_ext_compressed_textures cvar is enabled.

Supported by Spearmint 0.2+. In Spearmint 0.1 it's ignored without warning.

noTC (Origin: SoF2)

No texture compression even if r_ext_compressed_textures cvar is enabled.

Supported by Spearmint 0.2+. In Spearmint 0.1 it's ignored without warning.

hitLocation (Origin: SoF2)

UNIMPLEMENTED. Keyword is ignored.

Usage: hitLocation <image>

Image is suppose to be a paletted image with specific indexes meaning different locations.

hitMaterial (Origin: SoF2)

UNIMPLEMENTED. Keyword is ignored.

Usage: hitMaterial <image>

Image is suppose to be a paletted image with specific indexes meaning different materials.

lightColor (Origin: SoF2)

Usage: lightColor ( R G B )

R, G, B should be in range 0.0 to 1.0.

This keyword is ignored by JK2/JA/Spearmint/q3map2. It might not be ignore by SoF2/sof2map.

My research on the elusive "lightcolor";

In the SoF2's shader manual Q3Map section it says "Light Color" (in shader editor) causes surface to emit light. This is right below "Lightimage", which uses averages image to find RGB for light. It sound like lightColor is the same as q3map2's q3map_lightRGB. Though it's odd q3map2 does not support lightColor. The sof2map.exe has "lightColor" string right after "q3map_lightimage" and "q3map_surfacelight". FIXME: Test compiling a SoF2 map with and without lightColor to see if it affects lightmaps. If it does, should be added to q3map2.

In SoF2/JK2, it's only used on sky shaders. JA uses it on some sky shaders, mainly RMG (random mission generator) sky shaders, and 2 non-sky shaders.

In JK2 MP there is code that reads "lightcolor" and if sky shader sets it as sun ambient color (but does not use it). JK2 SP removed parsing the RGB and has a comment saying not used by JK2. There is a comment in JK2 MP tr_local.h saying sun ambient color is used by "John's terrain system". This leads me to believe it was used by the SoF2 renderer for ARIOCHE terrain (because there aren't many terrain systems).

JA SP/MP does not parse RGB from "lightColor", but contain the ARIOCHE terrain system that is absent from JK2. Sun ambient color variable exists and gets color from "_color" in worldspawn (defaulting to 1 1 1, if not set). "ambient" in worldspawn can be used to scale sun ambient color. RMG uses sun ambient color for the whole light grid. How does RMG get ambient color? Does it have a worldspawn? Or did someone forget to port the "lightColor" parsing over to JA? Well, actually the code exists for parsing "lightColor" RGB in JA MP and is commented out, and is missing only setting as sun ambient if it's a sky shader from JK2. "SP skips this so I'm skipping it here too." 😕

While that cynical bit about "does RMG have a worldspawn" was fun, I'm not sure how it handles entities. So maybe it does have worldspawn with "_color" set. Sun ambient color is only used for RMG in JA, so why would they parse it from worldspawn if it wouldn't be usable by RMG? Who knows, the RMG feature isn't used in JA anyway. 😏

I think that about clears things up. I don't need to warn that "lightcolor" being ignored in Spearmint, and might want to use it if I ever try to add RMG for SoF2... Whether q3map2 should support "lightcolor" has an alternate name for q3map_lightRGB is still unknown.

glow (Origin: JA)

UNIMPLEMENTED. Keyword is ignored.

Make surface glow.

fogOnly (Origin: Q3-prerelease, FAKK)

fogOnly

It is ignore by Spearmint. Q3 has it in shaders that aren't used. FAKK has it in shaders that are used.

I think it was used to allow fog shaders without any stages to load without error, and (in Q3 at least) replaced with checking if surfaceParm fog is specified.

surfaceLight (Origin: FAKK)

surfaceLight <value>

It is ignore by Spearmint. FAKK shader manual says it's an alias for q3map_surfaceLight. q3map2 does not support it, so probably only supported by FAKK's map compiler.

surfaceColor (Origin: FAKK)

surfaceColor ( R G B )

It is ignore by Spearmint. FAKK shader manual says it's a replacement for q3map_lightImage (which uses average color for light). q3map2 has q3map_lightRGB which sounds like the same feature.

FIXME: Does q3map2 do more than just average the color of q3map_lightImage?

surfaceDensity (Origin: FAKK)

surfaceDensity <value>

It is ignore by Spearmint. FAKK shader manual says it's the lightmap density.

subdivisions (Origin: FAKK)

subdivisions <value>

FAKK's map compiler uses it for patch surface's subdivisions value use for collision smoothness (lower value is smoother). The default value is 4.

Added in Spearmint 0.2. Spearmint 0.1 will not load shaders with this keyword.

spriteScale (Origin: FAKK)

scaleScale <value>

Sets a multiplier for sprite radius when shader is rendered as a sprite.

spriteGen (Origin: FAKK)

spriteGen <parallel|parallel_upright|parallel_oriented|oriented>

spriteGen tells engine how to render the sprite.

  • parallel Face screen. (Default)
  • parallel_upright Face screen horizontally, but stay upright relative to world. AKA Rotate around up axis to face screen.
  • parallel_oriented Rotate around up axis set by entity to face screen.
  • oriented Face direction of entity axis.

It's possible to see backside of spriteGen oriented and spriteGen parallel_oriented. It's probably use cull twosided with them.

deformVertexes (Origin: Q3, extended by RTCW)

RTCW added special handling for negative frequency to only affect Z, either using model's up axis or refEntity_t::fireRiseDir rotated by model axis. This is supported by Spearmint 0.2+.

novlcollapse (Origin: Quake Live)

Allow shader to have multiple stages when r_vertexLight is enabled.

When r_vertexLight is enabled Quake 3 preforms "vertex lighting collapse" on shaders so they only have one stage. novlcollapse disables this. In Quake Live novlcollapse is used for all player model and UI shaders and select world shaders that need multiple stages to look correct.

Note: In Spearmint, r_vertexLight stage collapse only affects shaders that have a lightmap stage.

Supported by Spearmint 0.6+.

Stage Directives

These are usable inside shader stages.

map (Origin: Q3, extended by RTCW/ET/Spearmint)

Usage: map <string>

  • Custom image: map path/to/image
  • Use lightmap set in map BSP: map $lightmap
    • If Spearmint <= 0.3 or r_missingLightmapUseDiffuseLighting cvar is 1;
      • If surfaceParm nolightmap and not surfaceParm pointlight, uses white image. (Q3 will also use white, but Spearmint has this explicit handling because of ET's code was messing up Dark Salvation.)
      • If used on vertex lit world surfaces (surfaceParm pointlight, q3map2 "vertex-approximated" surfaces [using q3map2 -light -approx <1-255>], misc_model without lightmap, ...), uses white image and rgbGen exactVertex. (Origin: ET)
      • If used on dynamic entity models, uses white image and rgbGen lightingDiffuse. (Origin: Spearmint)
      • If used in 2D drawing, uses white image and rgbGen vertex. (Origin: Spearmint)
    • Else if r_missingLightmapUseDiffuseLighting cvar is 0;
      • If not a lightmapped surface, use white image.
    • Q3 implementation: If not a lightmapped surface, use white image.
    • ET implementation: If not a lightmapped surface, use white image and rgbGen exactVertex. Comment says it's for vertex-approximated surfaces, but it gets applies to everything.
  • Use image for dynamic lights: map $dlight (Origin: RTCW)
  • Use white image: map $whiteimage
    • RTCW hardcodes map *white to be the same as map $whiteimage, whereas in Q3 it goes through image look up to find "*white". Might be faster than Q3 method and prevent warnings about reusing image with different picmip, nomipmaps, etc?

map16/map32, mapcomp/mapnocomp, animmapcomp/animmapnocomp (Origin: RTCW)

RTCW adds 3 pairs of keywords to allow using different image depending on color bit depth (map16, map32), whether texture compression is available and enabled for 'map' and 'animmap' (mapcomp, mapnocomp, animmapcomp, animmapnocomp).

They are are treated as 'map' and 'animmap' after determining whether to use them.

map16 path/to/image_16
map32 path/to/image_32
mapcomp path/to/image_comp
mapnocomp path/to/image_nocomp
animmapcomp <speed> path/to/image_1c path/to/image_2c path/to/image_3c
animmapnocomp <speed> path/to/image_1 path/to/image_2 path/to/image_3

animMap (Origin: Q3, extended by RTCW/ET/JA/FAKK/EF2/SOF2/Spearmint)

Usage: animMap <speed> <image1> [image2] ... [image64]

Q3 supported up to 8 images. There was no warning if more images were listed. Various games increased it or possibly attempted to use more than the engine supported. RTCW/ET: 16, JA: 32, SOF2: at least 38?, FAKK/EF2: at least 47?. Spearmint supports 64 images.

clampAnimMap (Origin: SoF2, extended by Spearmint)

It's the same as animMap, except it uses GL_CLAMP_TO_EGDE instead of GL_REPEAT.

Usage: clampAnimMap <speed> <image1> [image2] ... [image64]

Also supported by JK2 and JA.

SoF2 has a shader with 38 images (which I'm not sure if was actually supported or not). JK2 supported up to 8 images. JA supported up to 32. Spearmint supports 64 images.

oneshotAnimMap (Origin: JK2, extended by Spearmint)

It's the same as animMap, expect it stays at the last image instead of looping.

Usage: oneshotAnimMap <speed> <image1> [image2] ... [image64]

Also supported by JA.

Max images is 8 in JK2 and 32 in JA. Spearmint supports 64 images.

oneshotClampAnimMap (Origin: Spearmint)

It's the same as clampAnimMap, expect it stays at the last image instead of looping.

Usage: oneshotClampAnimMap <speed> <image1> [image2] ... [image64]

clampTexCoords (Origin: Q3-prerelease)

Usage: clampTexCoords

It makes the image specified by map use GL_CLAMP_TO_EGDE instead of GL_REPEAT. It can be specified after the map keyword. clampTexCoords is used by Q3Test and was replaced with clampMap in the Q3 retail release.

It is supported by Spearmint 0.2+ and can be used with any map/animMap keyword such as RTCW's map16.

lightmap (Origin: ET, extended by Spearmint)

Usage: lightmap <string>

  • Custom lightmap: lightmap path/to/image
  • Use lightmap set in map BSP: lightmap $lightmap
  • Use image for dynamic lights: lightmap $dlight
  • Use white image: lightmap $whiteimage or lightmap *white

Special handling for lightmap $lightmap (same as map $lightmap):

  • If Spearmint <= 0.3 or r_missingLightmapUseDiffuseLighting cvar is 1;
    • If surfaceParm nolightmap and not surfaceParm pointlight, uses white image. (Q3 will also use white, but Spearmint has this explicit handling because of ET's code was messing up Dark Salvation.)
    • If used on vertex lit world surfaces (surfaceParm pointlight, q3map2 "vertex-approximated" surfaces [using q3map2 -light -approx <1-255>], misc_model without lightmap, ...), uses white image and rgbGen exactVertex. (Origin: ET)
    • If used on dynamic entity models, uses white image and rgbGen lightingDiffuse. (Origin: Spearmint)
    • If used in 2D drawing, uses white image and rgbGen vertex. (Origin: Spearmint)
  • Else if r_missingLightmapUseDiffuseLighting cvar is 0;
    • If not a lightmapped surface, use white image.
  • Q3 implementation: If not a lightmapped surface, use white image.
  • ET implementation: If not a lightmapped surface, use white image and rgbGen exactVertex. Comment says it's for vertex-approximated surfaces, but it gets applies to everything.

depthfunc disable (Origin: Elite Force)

depthfunc disable is the same as depthfunc lequal.

tcGen cel (Origin: ZEQ2-Lite)

Used to create a cel shading (also called toon shading) effect.

Add an extra stage with tcGen cel using an image such as this one.

The cel shading images are usually 1 pixel tall. Left will be used at darkest lighting on model, moving toward right as gets lighter. The cel shading images can have any number of colors, not limited to gray/white like the linked image above.

models/players/doom/doom
{
	{
		map models/players/doom/doom.tga
		rgbGen identityLighting
	}
	{
		map textures/sfx/celshader.png
		blendfunc blend
		rgbGen identityLighting
		tcGen cel
	}
}

ZEQ2-Lite uses cel shader images which are opaque with blend filter. Turtle Arena uses transparency and blendfunc blend.

ZEQ2-Lite uses rgbGen lightingUniform for the cel stage, which hasn't been added to Spearmint.

tcMod swap (Origin: RTCW)

tcMod swap

Swaps the S and T texture coords, effectively rotating the image 90 degrees. In Spearmint it is equivalent to tcMod transform 0 1 1 0 0 0 -- if you want scale and/or offset too it would be better performance to use a single transform instead.

Supported by Spearmint 0.2+.

tcMod offset (Origin: FAKK)

tcMod offset <s> <t>

Specify values to add to S and T texture coords. In Spearmint it is equivalent to tcMod transform 1 0 0 1 s t -- if you want scale and offset it would be better performance to use a single transform instead.

Supported by Spearmint 0.2+.

alphaFunc GE192 (Origin: SoF2)

Only draw pixel if value greater or equal to 192. Similar to GE128, which draws pixel if value greater or equal to 128.

Also supported by JK2 and JA.

alphaTest (Origin: Alice)

alphaTest <func> <reference value>

The alphaTest keyword allows more control over when to draw or drop pixels based on the alpha value than Quake 3's alphaFunc. Draw the pixel if alpha is greater than the reference value, greaterEqual (greater or equal), less, lessEqual (less or equal), equal, or notEqual (not equal) to the reference value. Reference value must be in the range of 0.00 to 1.00. Convert between reference value and pixel alpha by dividing or multiplying by 256.

Examples:

  • alphaTest greater 0 is equivalent to alphaFunc GT0.
  • alphaTest less 0.50 is equivalent to alphaFunc LT128.
  • alphaTest greaterEqual 0.50 is equivalent to alphaFunc GE128.
  • alphaTest greaterEqual 0.75 is equivalent to alphaFunc GE192.

Alice's shaders only use the greaterEqual function but there is some commented out usage of greater. The other functions were found by testing shaders with the game as there is no shader manual and the text strings are all obfuscated in the EXE. Alice's shaders extensively use alphaFunc <func>. alphaTest is used for more control.

Spearmint only supports two decimal places of precision for the reference value (0.xx). That is the most used in Alice's shaders and Quake 3's alphaFunc. I have not tried to determine the decimal places of precision in Alice's engine.

alphaTest was added in Spearmint 0.5. Spearmint 0.4 and earlier versions will not load shaders with this keyword.

rgbGen lightingDiffuse (Origin: Q3, extended by FAKK)

In Q3A, rgbGen lightingDiffuse can only be used on entity models, not the world itself (models end up solid black or white). In Spearmint, lightingDiffuse uses correct diffuse color gen for world (exactVertex) and 2D (vertex) too.

This is required for FAKK and Alice as they use it for misc_models which can be dynamic or static (part of world).

rgbGen lightingDiffuseEntity (Origin: JA, extended by Spearmint)

rgbGen lightingDiffuseEntity

Scales diffuse lighting using entity shaderRGBA. Allows applying a tint to image and using a lighting diffuse at the same time. Kind of a combo of rgbGen const and rgbGen lightingDiffuse.

In JA, rgbGen lightingDiffuseEntity can only be used on entity models, not the world itself (models end up solid black or white). In Spearmint, lightingDiffuse was extended to support diffuse color gen for world (exactVertex) and 2D (vertex). This was added to lightingDiffuseEntity as well.

Note: Before Spearmint 0.5, PowerPC using AltiVec rendered it without entity color like rgbGen lightingDiffuse.

rgbGen colorwave (Origin: FAKK)

rgbGen colorwave ( <red> <green> <blue> ) <function> <base> <amplitude> <phase> <frequency>

Multiplies RGB colors times waveform value. Allows applying a tint to image and using a waveform at the same time. Kind of a combo of rgbGen const and rgbGen wave.

rgbGen constant (Origin: FAKK)

Alternate name for rgbGen const.

rgbGen fromEntity (Origin: FAKK)

Alternate name for rgbGen entity.

rgbGen fromClient (Origin: FAKK)

Alternate name for rgbGen vertex.

alphaGen constant (Origin: FAKK)

Alternate name for alphaGen const.

alphaGen fromEntity (Origin: FAKK)

Alternate name for alphaGen entity.

alphaGen fromClient (Origin: FAKK)

Alternate name for alphaGen vertex.

alphaGen skyAlpha (Origin: FAKK)

Use refdef_t::skyAlpha for alpha. CGame typically uses "skyalpha" in worldspawn for refdef_t::skyAlpha or set by scripting, allowing dynamically changing the alpha of one of the sky layers.

alphaGen oneMinusSkyAlpha (Origin: FAKK)

Use one minus refdef_t::skyAlpha for alpha. CGame typically uses "skyalpha" in worldspawn for refdef_t::skyAlpha or set by scripting, allowing dynamically changing the alpha of one of the sky layers.

alphaGen normalZFade (Origin: RTCW)

alphaGen normalzfade [max alpha] [dot min] [dot max]

Normal Z Fade allows controlling alpha based on the direction of the model's vertex's normal (direction).

The alpha value is basically calculated by max alpha × entity alpha (set by refEntity_t::shaderRGBA) × value based on dot product of vertex normal and 'up direction'.

The dot product is 1 when vertex normal the same direction as 'up direction' and -1 when facing the opposite direction. If the dot product is less than dot min or more than dot max then alpha of 0 is used. If the dot product is half way between dot min and dot max then full alpha is used and it fades to 0 as it reach dot min or dot max.

The 'up direction' is rotated with the model axis of the refEntity_t, so rotating the model does not affect the fading. The 'up direction' can be set to any direction using refEntity_t::fireRiseDir (fireRiseDir was added in Spearmint 0.2).

normalZFade is supported by Spearmint 0.2+.

depthmask (Origin: FAKK)

Alternate name for depthwrite.

portalsky (Origin: FAKK)

Currently it sets shader as a sky (skyParms also does). I don't know what it does in FAKK.

portalsky keywork is not needed in Spearmint. Spearmint skybox rendering is done by rendering skybox and then world with RDF_NOSKY (disables drawing surfaces with surfaceparm sky).

noDepthTest (Origin: FAKK)

Disables GL_DEPTH_TEST. Always draws on top of everything else, even if other things are closer.

depthTest disable (Origin: Quake Live)

Disables GL_DEPTH_TEST. Always draws on top of everything else, even if other things are closer.

Supported by Spearmint 0.6+.

nomipmaps (Origin: FAKK)

Disable mip maps for this stage only. Make sure to place before map keywords.

nomipmap (Origin: Spearmint)

Disable mip maps for this stage only. Make sure to place before map keywords.

ZTM: Might as well allow the RTCW MP general keyword to be used in stages too.

nopicmip (Origin: FAKK)

Disable pic mip for this stage only. Make sure to place before map keywords.

picmip2 (Origin: Spearmint)

Use r_picmip2 cvar for picmip level instead of r_picmip cvar. Make sure to place before map keywords.

ZTM: Might as well allow the RTCW SP general keyword to be used in stages too.

nextbundle (Origin: FAKK)

Multitexturing is typically enabled automatically for lightmaps in Quake 3. FAKK allows specifically enabling it using nextbundle. I think it might be for improving level load time? Though it's probably more work than it's worth now.

Using nextbundle without lightmap (two regular images) probably doesn't work in OpenGL2. I haven't checked if FAKK or Alice uses nextbundle without lightmap.

Generic FAKK nextbundle texture example:

textures/path/to/image_alt
{
    qer_editorimage textures/path/to/image.tga
if mtex
    {
        map $lightmap
        nextbundle
        map textures/path/to/image
    }
    {
        map textures/path/to/envmap
        blendFunc blend
        tcGen environment
    }
endif
if no_mtex
    {
        map $lightmap
    }
    {
        map textures/path/to/image
        blendFunc GL_DST_COLOR GL_ZERO
    }
endif
}

fog (Origin: RTCW)

Usage: fog on enables fogging the stage if the shader specifies noFog. fog off disables fogging the stage if the shader specifies noFog (which is the default behavior anyway). Spearmint does not support all possible use cases, see below.

In RTCW fog on is used to work around OpenGL fog color being applying to each stage, which can build up if there are multiple blended layers. Typical usage is to add the general directive noFog to the shader and then add fog on to the base stage and (if it exists) the lightmap stage.

Spearmint uses software fogging that is not applied on a per-stage basis. So if any stage has 'fog on', there will be fog coloring added after all stages are rendered. This means the RTCW work around is not needed, but unfortunately also prevents only fogging some stages. Setting r_useGlFog cvar to 1 enables RTCW-like per-stage fogging, but fogParms (fog blocks) are buggy and it's currently only available in OpenGL1 renderer.

Waveform Types

Noise (Origin: Q3, extended by Elite Force)

Waveform is random values between min and max. It's a discontinuous function.

Noise keyword is recognized by Quake 3 but is only usable by "rgbGen wave". Other waveforms (e.g., deformVertexes wave) cause the game to error and drop to the menu. It's fixed in Spearmint using code from Thilo Schulz' ioEF engine.

Random (Origin: Elite Force)

Waveform will be either on or off, randomly, each frame. It's a discontinuous function.

Added from Thilo Schulz' ioEF engine.

Clone this wiki locally