shader_environment is intended for opaque and alpha-tested surfaces and is typically used for the majority of shaders in level BSPs, though it can also be used on gbxmodels. A key feature of this shader is its ability to blend between two detail maps, making it ideal for outdoor ground shaders. It also supports cube maps, bump maps, detail maps, and masked specularity.

Detail map blending

When this shader's type is set to blended or blended base specular, the base map's alpha channel will be used as a blending mask between the primary and secondary detail maps.

This allows you to use two detail maps varyingly across a surface and is typically used for ground shaders which need both grassy and dirt areas. In this case the base map provides the general colouring across the ground of the level, while the detail maps tile and provide fine-level detail. There are a few options for how the detail maps blend with the base map.

Alpha testing

When the alpha tested flag is checked, the bump map's alpha channel can be used as a kind of transparency mask. The shader will either be fully opaque or fully transparent depending on if the alpha value is lighter or darker than 50% gray.

You should use this feature for transparent shaders that need to maintain the appearance of sharp edges regardless of distance and where semi-transparency is not needed. Some examples of this include the 2D "billboard" trees outside Timberland, the floor grates in Derelict, or foliage textures on scenery.

Depending on the texture, you may find that at a distance small transparent or opaque details become lost, such as a chain link fence becoming totally invisible. This is the result of mipmapping in the bump map since the alpha chanel is "blurring" as it becomes smaller and details are being lost. It can be important to tune the bump map's mipmap generation to avoid this:

  • Limiting mipmap count results in more aliasing but preserves the impression of fine detail.
  • Tuning the alpha bias lightens or darkens the alpha channel in mipmaps to result in more or less pixels passing the 50% alpha test.

Bump maps

Bump maps are special textures that encode the "bumpiness" of the surfaces they map to. They are used to represent fine details like cracks and grooves that affect specular reflections and lighting to make the surface look more geometrically detailed than it actually is.

Artists can create them as simple grayscale height maps in TIFF format, and once compiled by Tool or invader-bitmap into a bitmap tag with "height map" usage, they are represented as standard normal maps for use in-engine. Halo CE does not use height maps and doesn't support tessellation or parallax occlusion. The alpha channel of the bump map is unused unless the shader is alpha tested.

The lighting effect of bump maps can be seen under both dynamic lights and static lightmaps (sometimes called environmental bump maps), with the latter only natively supported in H1A and H1X unless using the CEnshine shader port for H1PC/H1CE. Environmental bump mapping uses incoming light directions which have been precalculated and stored per-vertex during radiosity.

Invalid bump maps

Artists should ensure that the bump map referenced by a shader is a valid normal map. Don't forget to set the bitmap's usage to height map if you're using tool to import a greyscale height map. Also, don't simply reuse a diffuse or a multipurpose map for a bump map. Failure to use a valid normal map will result in the surface appearing extremely dark or black.

Modders who are porting older Custom Edition maps to MCC may find that existing shaders have this problem, since environmental bump mapping was unsupported in H1CE and the original mappers would not have seen this darkening.

Shading artifacts

Left: default stock shaders in Chiron TL-34 and The Silent Cartographer. Right: The same shaders with the alternate bump mapping flag enabled.

By default, environmental bump mapping is rendered by darkening surfaces based on the dot product (angle difference) between incoming light and the bump map. However, in some locations and lighting setups this can result in strange triangular shading artifacts that look like bad smoothing despite level geometry having the intended normals:

  • Where small or point-like light sources are very close to surfaces.
  • Where sharp shadows should be, but the area has either low geometric complexity and/or uses shaders with low radiosity detail levels.

The artifact could be considered a problem with the legacy lighting model; the baked lightmap already accounts for diffuse attenuation and it shouldn't be doubly applied. It is made worse by the limited resolution of both the intermediate lightmap mesh and baked lightmap texture which results in light bleeding, and how per-vertex incident radiosity vectors cannot represent quickly changing light directions across a surface.

You can set the new alternate bump attenuation flag to use a different bump mapping method (similar to Halo 2's) which removes these artifacts, but comes at the cost of possibly overexposing highlights from coloured lights and generally lightening surfaces. Using the flag or not is an artistic choice.

Custom Edition mappers never encountered this artifact due to CE's broken bump mapping. If you're porting a map from CE to MCC you may find that this this artifact is now noticeable. The new flag can be a quick fix to better maintain the original map's appearance and, if applied to all shaders, will generally brighten up a map and make it look more like CE.

If you want to avoid this artifact without using the flag because you prefer the classic look for a shader, here are some workarounds:

  • Use high radiosity detail level for affected shader(s) if you aren't already.
  • Tesselate surfaces where sharp shadows lie, especially where shadow umbras and penumbras would lie. This limits light bleeding by forcing the lightmapper to resolve a higher level of detail than it normally would based on quality settings, and results in more incident radiosity vectors being stored to better match the baked lighting texture, but has the cost of increasing triangle count.
  • Replace nearby point light sources with large diffuse invisible light casting surfaces that avoid sharp shadows.
  • Avoid putting scenery lights too close to surfaces or in locations that would cast sharp shadows.

Use by gbxmodels

When a gbxmodel references this shader type it will not render correctly in H1CE due to renderer bugs. Specular masking and tinting don't work and sky fog does not render over it. Some affected scenery include the teleporter base and human barricades. It is not recommended to use this shader type for custom objects when targeting Custom Edition, but it is safe to use in H1A.

Related HaloScript

Function/global

Type

(rasterizer_environment_alpha_testing [boolean])

Toggles alpha testing for BSP shader_environment. These shaders are rendered opaquely when disabled.

Global
(rasterizer_environment_reflection_mirrors [boolean])

Toggles the rendering of dynamic mirrors.

Global

Structure and fields

FieldTypeComments
shader environment flagsbitfield
FlagMaskComments
alpha tested0x1

Causes the shader to become alpha tested, using the bump map's alpha channel as the transparency mask. Where the alpha channel is darker than 50% grey this shader will not be drawn, and otherwise is fully opaque. This is commonly used for foliage and grates. Note that these surfaces will still appear fully opaque to lightmaps and completely block light unless marked render-only (!) to cast no shadows.

bump map is specular mask0x2
true atmospheric fog0x4
use alternate bump attenuation0x8

Causes the shader to use an alternate shading method for bump maps which prevents certain artifacts from appearing near light sources close to surfaces and near shadow edges in geometrically sparse regions, at the cost of sometimes having over-exposed bump map highlights.

shader environment typeenum
OptionValueComments
normal0x0
blended0x1
blended base specular0x2
lens flare spacingfloat

Determines how far apart BSP lens flares are generated, in world units, along a surface part using this shader. If set to 0, a single lens flare will be created at the center of the surface.

lens flareTagDependency: lens_flare

References the lens flare to be rendered at the generated lens flare markers. If empty, no lens flares will be generated.

diffuse flagsbitfield
FlagMaskComments
rescale detail maps0x1
rescale bump map0x2
base mapTagDependency: bitmap
detail map functionenum

This controls how the detail map is blended with the base map.

OptionValueComments
double biased multiply0x0

The detail colour is multiplied with the base colour and multipied by 2, then clamped to 0-1. Where detail is masked, this has no effect. Unlike the S-shaped response curve of Photoshop's Overlay blending mode, this function continues to brighten exponentially and quickly saturates when the detail map is lighter than 50% gray.

detail = lerp(0.5, detail.rgb, detail_mask);
result = saturate(base.rgb * detail * 2.0);
multiply0x1

The detail map is simply multiplied with the base colour, generally darkening the result. Where detail is masked, this has no effect.

detail = lerp(1.0, detail.rgb, detail_mask);
result = saturate(base.rgb * detail);
double biased add0x2

Has the effect of adding or subtracting the detail map to/from the base map. Where detail is masked or 50% gray, this has no effect.

detail = lerp(0.5, detail.rgb, detail_mask);
biased_detail = 2.0 * detail - 1.0;
result = saturate(base.rgb + biased_detail);
primary detail map scalefloat
  • Default: 1
primary detail mapTagDependency: bitmap
secondary detail map scalefloat
  • Default: 1
secondary detail mapTagDependency: bitmap
micro detail map functionenum?
micro detail map scalefloat
  • Default: 1
micro detail mapTagDependency: bitmap
material colorColorRGB
bump map scalefloat
  • Default: 1
bump mapTagDependency: bitmap
bump map scale xyPoint2D
  • Cache only
FieldTypeComments
xfloat
yfloat
u animation functionenum
OptionValueComments
one0x0
zero0x1
cosine0x2
cosine variable period0x3
diagonal wave0x4
diagonal wave variable period0x5
slide0x6
slide variable period0x7
noise0x8
jitter0x9
wander0xA
spark0xB
u animation periodfloat
  • Unit: seconds
  • Default: 1
u animation scalefloat
  • Unit: base map repeats
  • Default: 1
v animation functionenum?
v animation periodfloat
  • Unit: seconds
  • Default: 1
v animation scalefloat
  • Unit: base map repeats
  • Default: 1
self illumination flagsbitfield
FlagMaskComments
unfiltered0x1
primary on colorColorRGB
primary off colorColorRGB
primary animation functionenum?
primary animation periodfloat
  • Unit: seconds
  • Default: 1
primary animation phasefloat
  • Unit: seconds
secondary on colorColorRGB
secondary off colorColorRGB
secondary animation functionenum?
secondary animation periodfloat
  • Unit: seconds
  • Default: 1
secondary animation phasefloat
  • Unit: seconds
plasma on colorColorRGB
plasma off colorColorRGB
plasma animation functionenum?
plasma animation periodfloat
  • Unit: seconds
  • Default: 1
plasma animation phasefloat
  • Unit: seconds
map scalefloat
  • Default: 1
mapTagDependency: bitmap
specular flagsbitfield
FlagMaskComments
overbright0x1
extra shiny0x2
lightmap is specular0x4
brightnessfloat
  • Min: 0
  • Max: 1
perpendicular colorColorRGB
parallel colorColorRGB
reflection flagsbitfield
FlagMaskComments
dynamic mirror0x1
reflection typeenum
OptionValueComments
bumped cube map0x0
flat cube map0x1
bumped radiosity0x2
lightmap brightness scalefloat
  • Min: 0
  • Max: 1
perpendicular brightnessfloat
  • Min: 0
  • Max: 1
parallel brightnessfloat
  • Min: 0
  • Max: 1
reflection cube mapTagDependency: bitmap

Acknowledgements

Thanks to the following individuals for their research or contributions to this topic:

  • Conscars (Notes on bump mapping and alpha testing)
  • Kavawuvi (Invader tag definitions)
  • MosesOfEgypt (Tag structure research)