Quake 3 Enhancements
Usage Guide
Created: May 1, 2001
Printed: April 28, 2002
Surfacesprites is a
shader pass similar to those you are already familiar with. It shares kinship with surface textures,
lightmaps, and other multipass texture layers.
The main difference is that surfacesprites are not flat on the surface
they are placed on, but rather stand up away from the surface. Another important distinction is that a
single surfacesprite pass typically doesn’t generate one single image, but
rather multiple images evenly distributed over that surface.
Example:
Take any shader:
textures/prague/cobblestone
{
{
map $lightmap
}
{
map
textures/prague/cobblestone
blendFunc GL_DST_COLOR
GL_SRC_COLOR
}
…and add a pass to it
like this:
{
map
textures/colombia/grass_clump
surfaceSprites
vertical 24 24 16 1024
ssWind 0.5
alphaFunc GE128
blendFunc
GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
depthWrite
}
}
As in any shader pass,
the map is the graphic used, but in this case the graphic is not laid on top of
the surface, it instead is distributed over it.
The alpha and blend properties are optional as with any shader, but the
above configuration is highly recommended for “solid” materials. Surfacesprites will not be placed unless the
polygon is facing more-or-less upward (with a couple of exceptions below), so
if cube room is textured with a surfacesprite shader, the stuff will only appear
on the floor.
surfaceSprites
<type> <width> <height> <density> <fadedist>
<type>: One of three
keywords are valid:
vertical: Stuff that sticks up off the ground, such as grass, reeds,
etc.
oriented: An image that always
faces the user. This can be used for
rocks, debris, whatever you want to break up a surface. These should stay fairly small if possible.
effect: An image that always faces
the user, but it grows and fades out over time.
When the effect fades completely out, it reappears in another location.
<width>: Horizontal
size of each individual sprite, in world dimensions. This is the base width, which can be
added to with the variance command below.
<height>: Vertical
size of each individual sprite, in world dimensions. This is the base height, which can be
added to with the variance command below.
<density>: Density
defines the interval at which the sprites are placed, e.g. a 32 means that a
sprite will be placed every 32 units on the ground. Lower equals denser. Note that the more dense the sprites are, the
more slowdown you will see, so you should lower the fadedist if you up
the density.
<fadedist>: Fade
distance defines the minimum distance at which the surfacesprites will
be drawn in full. At a distance greater
than this, sprites are visible, but various sprites start to fade out at
different distances. 1000 is a fair draw
distance for normal grass-like items, smaller numbers are more appropriate for
smaller sprites.
The Surfacesprites have several optional parameters that, unless
overridden, use default values. These
are entered as separate lines beginning with “ss”, after the initial
surfacesprite call:
ssFademax
<fademax>
Fade max defines the maximum distance at
which any Surfacesprites at all will be drawn at all. The sprites will fade out between the fadedist
and the fademax. This value
defaults to 1.33 times the fadedist.
ssFadescale
<fadescale>
Fade scale indicates a fractional amount of the
sprite’s width that it will scale between the fadedist and fademax. That is, if:
Surfacesprites vertical 8.0 8.0 32 1000
SsFademax 1500
SsFadescale 1.0
…then between distances of 1000 and 1500, this sprite of
base width 8 will grow from 8 to 16. Of
course after 1500, the sprites are no longer drawn (see fademax). Default for fadescale is zero.
ssVariance
<varwidth> <varheight>
Variance defines a factored amount to add to the
width and height randomly. That is, if
the base width is 8 and the varwidth is 1.0, the width will be randomly between
8 and 16. If the variance is not
specified, they default to zero (e.g. every sprite is the same width and
height).
<varwidth>:
Fraction of the base width of the sprite to add randomly. Default is zero.
<varheight>:
Fraction of the base height of the sprite to add randomly. Default is zero.
ssHangdown
ssAnyangle
Hangdown is an optional keyword that when set, the
sprites are placed on the ceiling instead of on the ground, such as for hanging
vines.
Anyangle is an optional keyword that when set, the
sprites can come out of any surface, rather than just the top. Mutually exclusive with hangdown. NOTE:
FOR SPEED CONSIDERATIONS, ANYANGLE IS NOT CURRENTLY IMPLEMENTED.
ssFaceup
Oriented and Effect
sprites only
Faceup is an optional keyword for oriented and
effect sprites. When set, the sprites
are placed horizontally on the ground.
Currently the surface must be completely horizontal, or the sprites
won’t appear. Used for stains on the
ground, or water rings. Height is
ignored on faceup sprites, they are always square. Not valid for vertical sprites.
ssWind <wind>
Vertical and Effect
sprites only
Wind is an optional variable for vertical and effect sprites. Vertical sprites (such as grass) can wave
according to wind settings (see below).
Without this variable, the sprites are not affected by wind.
<wind>: The wind
value is how much to factor in these effects, as a multiple of the effects of
wind. 1.0 would be for limp grass
blowing in the wind. 0.5 would be more
rigid. 0.0 means the sprite sits rigidly
upward. A rigid sprite is cheaper to
render, so leave the wind variable out if you don’t need it! NOTE:
Currently not implemented on effect sprites. Default is zero.
The wind idle value is a factor from 0 to
1.0. The higher this number is, the more
vertical sprites will sway when there is no wind.
<windidle>:
The windidle value is how much to factor in these effects, and
typically varies from 0 to 1. 1.0 is
limp grass swaying when there is no wind.
0.25 offers very subtle movement.
0.0 means the sprite sits rigidly upward when there is no wind. A rigid sprite is cheaper to render, so leave
windidle at zero if you do not need it.
This value defaults to zero, or to the wind value if it is set.
ssVertSkew <skew>
The vertical skew value is a random factor that is
added to the surfacesprites to make their “base” position something other than
vertical. The best use for this is for
example a thin graphic for bamboo, which might stick up at strange angles.
<skew>: This
value indicates the fraction of the sprites height to add randomly to the
sprites’ top point. For example, if a
sprite has a height of 16, and the skew value is 1.0, then the sprite may lean
between 16 to the left and 16 to the right.
In exact terminology, the X and Y coordinates of the top of the sprite
could be between a -16 and 16 offset.
This value defaults to zero.
The duration value is a length of time that an
effect sprite grows or shrinks before it disappears. This is how long the effect lasts before
looping, in milliseconds. That
is, 1000 equals 1 second. Duration
defaults to 1000 (1 second).
The grow value is a proportional value that indicates
how much of the base width and height changes over an effects lifetime (i.e.
its duration). A base width of 8
and a grow value of 1.0 means the effect grows from 8 to 16. A negative value means it shrinks, so a
sprite with a base width of 8 (or any base width) and a grow value of –1.0,
will shrink to nothing over its duration.
The default for growwidth and growheight are 0.0.
ssFXAlphaRange
<alphastart> <alphaend>
The effect
alpha range sets two values between 0 and 1, that indicates what amount to
fade out to over the course of the effect’s lifetime. That is, if the alpha range is 0.8 and 0.3,
then an effect surfacesprite will start out with an alpha of 0.8 and end with
an alpha of 0.3.
<alphastart>: This indicates the alpha value that is shown
at the start of each individual surfacesprite effect. The alpha transparency of a sprite will
smoothly go from this value to the alphaend value. The default for the alphastart is 1.0, or
opaque.
<alphaend>: This indicates the alpha value that is shown
at the end of an individual surfacesprite effect. The alpha transparency of a sprite will
smoothly go from the alphaend value to this value. The default for the alphaend is 0.0, or fully
transparent.
SsFXWeather
Weather is an optional keyword for effect
sprites. If this keyword is present,
then the density of the sprites is reliant on the r_surfaceWeather cvar
as detailed below.
--------------------------------------------------------------------------------------------
There are a few cvars
that this feature uses right now:
r_surfaceSprites 0: Turns surfacesprites off.
r_surfaceSprites 1: Turns surfacesprites on.
r_surfaceSprites 3: Reports how many are being drawn per frame
r_windSpeed
<speed>: Sets the windspeed. Defaults to 0. This value is ignored if the weather system
is present, as the weather wind will take control.
r_windAngle
<angle>: Sets the angle that the
wind blows at. Defaults to 0. This value is ignored if the weather system
is present, as the weather wind will take control.
r_windDampFactor
<factor>: Sets how quickly the wind
responds to changes. Defaults to 0.1
r_surfaceWeather
<value>: Sets the amount of
weather-oriented effects to display.
Defaults to 1.0. This value is
typically 0 to 1, representing 0-100% of the weather, so in the case of rain
splashes, 0.4 is a light rain.
--------------------------------------------------------------------------------------------
You must also add a few
elements to make the surface light properly:
First you cannot use
lightmaps, the surface must be instead vertex lit, else the light will look
fullbright.
q3map_nolightmap
q3map_onlyvertexlighting
If the polygon surfaces
are large, you will not see the sprites fade properly. You must force the BSP to break up the
surface with a command like this:
q3map_tesssize 256
--------------------------------------------------------------------------------------------
The sprites or graphics
that it uses are hard-alpha channeled images of reasonable resolution. They should have sides (e.g. not tile). Grass is done with “clump” images. A wide, flat edge at the bottom of the sprite
is not usually desirable. It is highly
recommended that the sprites have the same overall coloring as the ground
texture.
The system currently
randomly flips any surfacesprites horizontally for variety.
You can use any blend
mode you wish, so if you want the sprites to be additive, simply set blendfunc
GL_ONE GL_ONE. Beware of highly
transparent objects if the sprites are very tall (e.g. vertical surfacesprites
such as tall grass). The transparency
will reveal sorting issues, and fogged sprites will look especially
strange. So for tall “in your face” sprites
such as grass, use a hard-edged alpha mask and an alphatest setting such as alphafunc
GE128, or even crisper like alphafunc GE192 (a new alphafunc
recently added). However, there is a
tradeoff. When the alphafunc is set
higher, the sprites will not be able to fade as smoothly, and will appear to
“pop” in a bit more.
If you want additional
sprites in a single texture, you can add in another pass, but don’t abuse it or
your overtaxed machine will punish you.
If you use the exact same density on a second pass, the second set of
sprites will be directly on top of the first, which can be useful. If you want roughly the same density but
don’t wish for this, simply add or subtract a small amount from the density, the
overall effect will be the same. BE
CAREFUL WITH THIS SYSTEM, BECAUSE THE SPRITES AIN’T FREE!
In the future, there
will be adjustments for this feature for scalability, namely, if the user has a
weaker machine, the sprites will be less in number and fade out quicker. The very worst machines might have absolutely
no surfacesprites at all. So, don’t rely
on this for 100% of a level’s appearance, so for example you might want to
leave in a few grass models just in case.
You can turn off the sprites with the cvar above to see how it looks.
If you want to have a
texture covered with sprites in some places, not in others (such as raindrops
outdoors but not inside), two shaders should be made with the same graphic, but
one not possessing the surfacesprite pass.
I’d suggest a clever naming convention for this.
Weather such as rain and
snow will eventually be on a trigger rather than a cvar. This, however, requires some coordination
with Rick and Jeff D.
--------------------------------------------------------------------------------------------
Currently surfacesprites
are NOT supported in shaderEd. This is
important, because if a shader with surfacesprites is loaded up in shaderEd,
the surfacesprites command will be eliminated when it is saved out again, destroying
the surfacesprites effect. For now,
these files must be edited by hand, and you should not put surfacesprites in
typically used shader files.
--------------------------------------------------------------------------------------------
Shader examples:
Grasses that blow in the
wind:
textures/test/grnd01c_sprites
{
qer_editorimage textures/kamchatka/grnd01c
q3map_material Gravel
q3map_nolightmap
q3map_onlyvertexlighting
{
map
textures/kamchatka/grnd01c
blendFunc GL_DST_COLOR GL_ZERO
}
{
map models/objects/colombia/jungle/tall_grass
surfaceSprites
vertical 16 32 48 1000
ssVariance 1.0
2.0
ssWind 1.0
blendFunc
GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
alphaFunc GE128
depthWrite
}
}
Rain splashes on water:
textures/colombia/rain_water
{
qer_editorimage textures/colombia/water_terrain
q3map_material Water
q3map_nolightmap
q3map_onlyvertexlighting
q3map_tesssize 256
{
map
textures/colombia/water_terrain
blendFunc GL_ONE
GL_ONE_MINUS_SRC_ALPHA
depthWrite
rgbGen exactVertex
alphaGen const 0.9
tcMod turb 0 0.08
0.04 0.08
tcMod scroll -0.05
-0.001
tcMod scale 3 3
}
{
map
textures/colombia/water_terrain2
blendFunc GL_SRC_ALPHA
GL_ONE_MINUS_SRC_ALPHA
rgbGen exactVertex
tcMod turb 0 0.08
0.04 0.08
tcMod scale 3 3
}
{
map
gfx/sprites/rainhit
surfaceSprites effect
2.5 2.5 16 400
ssFXDuration 135
ssFXGrow 6 6
ssVariance 1.0
0.75
ssFXWeather
blendFunc GL_ONE
GL_ONE
}
{
map
gfx/sprites/rainring
surfaceSprites effect
2.0 2.0 28 350
ssFXDuration 220
ssFXGrow 2.5 2.5
ssFaceup
ssVariance 2.0 1.0
ssWeather
blendFunc GL_ONE GL_ONE
}
}
Random debris on the
ground:
textures/common/ground
{
qer_editorimage textures/colombia/ground_jungle
q3map_material gravel
q3map_nolightmap
q3map_onlyvertexlighting
{
map textures/colombia/ground_jungle
blendFunc GL_ONE
GL_ZERO
}
{
map
gfx/sprites/rock1
surfaceSprites
oriented 4 4 80 256
ssVariance 0.25 0.0
blendFunc
GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
alphaFunc GE128
depthWrite
}
{
map
gfx/sprites/rock2
surfaceSprites
oriented 2 2 92 200
ssVariance 0.25 0.0
blendFunc
GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
alphaFunc GE128
depthWrite
}
}
--------------------------------------------------------------------------------------------
All weather is started
and controlled with r_we.
Snow:
Start a snow effect:
r_we snow init <particles>
Remove the snow effect:
r_we snow remove
Set the snow alpha
transparency:
r_we snow alpha <float>
default: 0.09
Set the area around the
player the snow covers:
r_we snow spread ( minX minY minZ ) ( maxX maxY maxZ )
default: ( -600 -600 -200 ) ( 600 600 250 )
Set the random range
that sets the speed the snow falls:
r_we snow velocity ( minX minY minZ ) ( maxX maxY maxZ )
default: ( -15 -15 -20 ) ( 15 15 -70 )
Set an area of snow
blowing:
r_we snow wind ( windOriginX windOriginY windOriginZ ) ( windVelocityX windVelocityY windVelocityZ ) ( sizeX sizeY sizeZ )
Set snow blowing data:
r_we snow blowing duration <int>
r_we snow blowing low <int> default:
3
r_we snow blowing velocity ( min max )
default:
( 30 70 )
r_we snow blowing size ( minX minY minZ )
default:
( 1000 300 300 )
Start a fog effect in
the snow:
r_we snow fog
Set the density of the
snow fog:
r_we snow fog density <alpha>
default: 0.3
Rain:
Start a rain effect:
r_we rain init <particles>
Remove the rain effect:
r_we rain remove
Add a fog effect to the
rain:
r_we rain fog
Set the density of the
rain fog:
r_we rain fog density <alpha>
default: 0.3
Set the range of speeds
at which the rain falls:
r_we rain fall ( minVelocity maxVelocity )
default: ( -60
-50 )
Set the area around the
player that the rain falls in:
r_we rain spread ( radius height )
default: ( 20 20
)
Set the alpha
transparency of the raindrops:
r_we rain alpha <float>
default:
0.1
Set the height (length)
of the raindrops:
r_we rain height <float>
default:
5
Set the slope at which
the rain falls (in the current wind direction):
r_we rain angle <float>
default:
1.0
Debug:
r_we debug wind