Infected (shader): Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
m (Corrected category from List of Shader parameters to Shaders)
(I spent a couple weeks trying to make this shader work for me. It it so complicated but here is an update based on what I learned.)
Line 9: Line 9:
{{note|This shader only works on models.}}
{{note|This shader only works on models.}}


== Usage ==


{{note|The shader was only tested in Source Filmmaker. As of such, and due to the fact that the source code is inaccessible, many shader parameters and features are not further explained and require further testing (especially inside of the game itself).}}


Check out <code>left4dead2/materials/models/infected/common/l4d2/bp_head_include.vmt</code> for a sample material that uses the shader.
=== How the Shader Constructs the actual Base Texture ===
This shader is quite complex and no texture supplied to this shader is actually a base texture (despite the name of <code>$basetexture</code>).
 
Here is how the Actual Base Texture is constructed.
 
All of this layer's color is sampled from the <code>$gradienttexture</code>. Two samples are taken from the <code>$gradienttexture</code>:
: <code>PalletRowSelector_A = RandomInteger{from 0 to 7}</code>
: <code>PalletRowSelector_B = RandomInteger{from 0 to 7}</code>
: <code>Sample_A.color = $gradienttexture.sample($basetexture.alphaChannel.sample(u,v), 1/32 + PalletRowSelector_A*1/16)</code>  • Generally used for Clothing
: <code>Sample_B.color = $gradienttexture.sample($basetexture.alphaChannel.sample(u,v), 1/32 + PalletRowSelector_B*1/16 + 0.5)</code>  • Generally used for Skin
 
Notice Sample_B is always sampled 0.5 above Sample_A. The blue channel from <code>$basetexture</code> is then used to linearly interpolate between these 2 colors.
* A blue value of 0.00 will return the color of Sample_A.
* A blue value of 1.00 will return the color of Sample_B.
* A blue value of 0.35 will return 65% of Sample_A and 35% of Sample_B mixed together.
 
Each of these samples are always taken from the same row of pixels. From row 0 to 7 for Sample_A and from row 8 to 15 for Sample_B. This allows for variation of color between otherwise duplicate models.
{{note|Currently I did not figure out if the row could be set. Right now it seems that the game chooses during runtime.}}
 
 
== Shader Parameters ==
 
{{note|Due to the fact that the source code is inaccessible, many shader parameters and features are not further explained and require further testing.}}
Check out <code>left4dead2/materials/models/infected/common/l4d2/ci_body_include.vmt</code> for a sample material that uses the shader.
 


=== General ===
=== General ===


; <code>$basetexture <[[texture]]></code>
; <code>$basetexture <[[texture]]></code>
: Unlike other shaders, this parameter serves a completely different role. It acts as a mask texture, where each channel serves a different purpose:
: Unlike other shaders, this parameter serves a completely different role. The <code>$basetexture</code> acts as an assembly texture, where each channel supplies the information needed to construct one complex texture. It is quite complicated.
: '''Red*:''' Detail blend mask
: Each channel's purpose is as follows:
: '''Green*:''' Blood blend mask
 
: '''Blue:''' Skin mask
:: '''Red:''' 2 Functions
: '''Alpha*:''' Eyeglow mask
::: ◘ ??? [000-127] Does something but I can't quite figure it out.
: Additionally, the vtf is split up similarly to a sprite sheet. All channels marked with a * are affected by this.
::: ◘ Detail blend mask. [128-255] Less then 128 does nothing. 255 fully blends detail.
: To compensate, scale down these channels to half their size and place them in one of the four quarters of the texture. This allows for more "variations" without using animation frames.
 
:: '''Green:''' 2 Functions
::: ◘ Eye Glow. [000-127] Glows on contact with flashlight. 0 is full glowyness. Greater than 127 does nothing.
::: ◘ Blood. [128-255] Less than 128 does nothing. 255 fully blends blood using the color <code>$bloodcolor</code>. It seems to blend similar to the "color burn" method.
 
:: '''Blue:''' Blends between 2 samples from the <code>$gradienttexture</code>. If your textures are well planned, you might find it useful to blend with this channel. Otherwise it is really only useful to use values 0 and 255 (nothing between). If you really know how to use an image editor like a magician, you could technically have 256 gradients to work with.
 
:: '''Alpha:''' U-Coordinate of Gradient Texture Sampler. Think of this as an indexed image. This channel provides values from 0-255. These values point to the gradient texture which always has a width of 256 (0-255 inclusive).
 
: ◘ Functions marked with a '◘' are split up into quadrants similar to a sprite sheet. This allows for more variations without using animation frames. A random quadrant per model will be chosen during runtime (when the models spawns in game).
 
::: When creating this <code>$basetexture</code> be VERY sure that you "save color values for transparent pixels". If you don't do this, you will get seemingly random artifacts. With out this setting: All pixels with alpha=0 will also have red, green, and blue values set to 0.
 
; <code>[[$nocull]] <[[bool]]></code>
; <code>[[$nocull]] <[[bool]]></code>
: Determines whether the backsides of faces can be seen. If a model is sliced open, you will see the insides of the model as well.
: Determines whether the backsides of faces can be seen. If a model is sliced open, you will see the insides of the model as well.
Line 36: Line 70:
: Unknown. Possibly allows for a translucent render pass.
: Unknown. Possibly allows for a translucent render pass.
; <code>$gradienttexture <texture></code>
; <code>$gradienttexture <texture></code>
: Unknown. Possibly related to wounds.
: Less of a gradient, more of a color palette. This is the texture that will be sampled to give the "true basetexture" it's color.
 
:::IF you are creating your own <code>$gradienttexture</code> textures THEN I highly recommend choosing lossless vtf formats such as <"Normal Format = "BGR888" OR "BGRA8888"> and <"Alpha Format" = "BGRA8888">. Also; For <code>$basetexture</code> and <code>$gradienttexture</code>, you may want to set the VTF's flag "Point Sample" to true. This does turn your texture pixelated but any other interpolation methods may create rainbows. Rainbows occur when the <code>$gradienttexture</code> is not very gradienty. For example: In your <code>$basetexture</code>: If you have two pixels the are next to each other and one has value 15 and the other has a value of 100... well, linear interpolation will grab all the colors of the <code>$gradienttexture</code> that are from 15 to 100. Most likey you don't want that.}}
 
; <code>$sheetindex <int></code>
; <code>$sheetindex <int></code>
; <code>$colortintgradient <int></code>
; <code>$colortintgradient <int></code>
: Unknown. Possibly controlled using code?
: Unknown. Possibly controlled during runtime. (It would make sense to me if this set PalletRowSelector_A as I stated above. Allowing 8 different variations of color.)
; <code>[[$ambientocclusion]] <bool></code>
; <code>[[$ambientocclusion]] <bool></code>
: Enables ambient occlusion on the model (for example in Source Filmmaker).
: Enables ambient occlusion on the model (for example in Source Filmmaker).
Line 92: Line 129:
=== Detail ===
=== Detail ===


The detail pass is controlled by the red channel of the $basetexture. Unlike other textures in Source, this texture is actually transformed differently.
The detail pass is controlled by the top half of the red channel of the <code>$basetexture</code>. Unlike other textures in Source, this texture is actually transformed differently.


In order to convert a regular $basetexture to the equivalent [[$detail]] texture (with same UV mapping), you will need to:
In order to convert a regular <code>$basetexture</code> to the equivalent [[$detail]] texture (with same UV mapping), you will need to:


# Take the original vtf file
# Mirror it horizontally
# Mirror it horizontally
# Rotate it by 90° counter-clockwise
# Rotate it by 90° counter-clockwise
Line 102: Line 138:


Besides these parameters, [[$detailframe]] and $detailblendfactor are also supported.
Besides these parameters, [[$detailframe]] and $detailblendfactor are also supported.
{{todo|How is the alpha channel handled?}}
{{todo|Is the $basetexture transformed like this as well?}}


=== Skin ===
=== Skin ===


The skin pass is controlled by the blue channel of the $basetexture. It also is the only channel that does not allow for variation using texture indexes (see below).
There is not really a skin pass as it is part of the Actual Base Texture. Although the original images from the game L4D2 seem to use it on the skin, eyes, and teeth; I don't believe this is entirely necessary. The only time it would be a problem to not follow this rule is: if you really need different phongexponents for skin and non-skin. I'm sure it was so they could use a palette for the clothes and a separate palette for the skin. (see "How the Shader Constructs the actual Base Texture" above)


; <code>$skintintgradient <int></code>
; <code>$skintintgradient <int></code>
: Unknown. Possibly controlled using code?
: Unknown. Possibly controlled during runtime. (It would make sense to me if this set PalletRowSelector_B as I stated above. Allowing 8 different variations of color.)
; <code>$skinphongexponent <float></code>
; <code>$skinphongexponent <float></code>
: Equivalent to $phongexponent, except for skin parts of the texture (blue channel).
: Equivalent to <code>$phongexponent</code>, except for skin parts of the texture (blue channel).


=== Blood ===
=== Blood ===
Line 121: Line 153:


; <code>$bloodcolor <color></code>
; <code>$bloodcolor <color></code>
: Color of the blood pass. {{todo|Is this multiplied with anything, or just overlaid as it is?}}
: Color of the blood pass.  
; <code>$bloodphongexponent <float></code>
; <code>$bloodphongexponent <float></code>
: Equivalent to $phongexponent, except for blood parts of the texture (green channel).
: Equivalent to $phongexponent, except for blood parts of the texture (green channel).
Line 129: Line 161:
: Unknown. Seems to modify the phong shape on blood.
: Unknown. Seems to modify the phong shape on blood.


{{todo|Something seems wrong! A fully red basetexture is affected by the values of $bloodspecboost and $bloodphongexponent, and $phongboost and $phongexponent do nothing!}}
{{todo|Something seems wrong! A fully red basetexture is affected by the values of $bloodspecboost and $bloodphongexponent, and $phongboost and $phongexponent do nothing! ???}}


=== Burning ===
=== Burning ===
Line 142: Line 174:
=== Eyeglow ===
=== Eyeglow ===


The eyeglow pass is controlled by the alpha channel of the $basetexture.
The eyeglow pass is controlled by the bottom half of the red channel of the <code>$basetexture</code>.


; <code>$eyeglow <bool></code>
; <code>$eyeglow <bool></code>

Revision as of 17:35, 27 March 2020

The Infected shader is used in Left 4 Dead 2 Left 4 Dead 2 to dynamically render wounded zombies and body parts.

Instead of relying on gibbed variations of all models, this shader is used on the models to remove whichever body part was shot at to look more realistic, and allows for adding different types of slashes and dynamic gore and flesh effects.

The model will simply render hollow, however. So the programmer or artist would need to place skeleton and flesh models inside of the holes that were created, in order to create a convincing effect.

Todo: Add some images from the game or the presentation here.
Todo: How does this shader relate to $shinyblood of VertexLitGeneric?
Note.pngNote:This shader only works on models.


How the Shader Constructs the actual Base Texture

This shader is quite complex and no texture supplied to this shader is actually a base texture (despite the name of $basetexture).

Here is how the Actual Base Texture is constructed.

All of this layer's color is sampled from the $gradienttexture. Two samples are taken from the $gradienttexture:

PalletRowSelector_A = RandomInteger{from 0 to 7}
PalletRowSelector_B = RandomInteger{from 0 to 7}
Sample_A.color = $gradienttexture.sample($basetexture.alphaChannel.sample(u,v), 1/32 + PalletRowSelector_A*1/16) • Generally used for Clothing
Sample_B.color = $gradienttexture.sample($basetexture.alphaChannel.sample(u,v), 1/32 + PalletRowSelector_B*1/16 + 0.5) • Generally used for Skin

Notice Sample_B is always sampled 0.5 above Sample_A. The blue channel from $basetexture is then used to linearly interpolate between these 2 colors.

  • A blue value of 0.00 will return the color of Sample_A.
  • A blue value of 1.00 will return the color of Sample_B.
  • A blue value of 0.35 will return 65% of Sample_A and 35% of Sample_B mixed together.

Each of these samples are always taken from the same row of pixels. From row 0 to 7 for Sample_A and from row 8 to 15 for Sample_B. This allows for variation of color between otherwise duplicate models.

Note.pngNote:Currently I did not figure out if the row could be set. Right now it seems that the game chooses during runtime.


Shader Parameters

Note.pngNote:Due to the fact that the source code is inaccessible, many shader parameters and features are not further explained and require further testing.

Check out left4dead2/materials/models/infected/common/l4d2/ci_body_include.vmt for a sample material that uses the shader.


General

$basetexture <texture>
Unlike other shaders, this parameter serves a completely different role. The $basetexture acts as an assembly texture, where each channel supplies the information needed to construct one complex texture. It is quite complicated.
Each channel's purpose is as follows:
Red: 2 Functions
◘ ??? [000-127] Does something but I can't quite figure it out.
◘ Detail blend mask. [128-255] Less then 128 does nothing. 255 fully blends detail.
Green: 2 Functions
◘ Eye Glow. [000-127] Glows on contact with flashlight. 0 is full glowyness. Greater than 127 does nothing.
◘ Blood. [128-255] Less than 128 does nothing. 255 fully blends blood using the color $bloodcolor. It seems to blend similar to the "color burn" method.
Blue: Blends between 2 samples from the $gradienttexture. If your textures are well planned, you might find it useful to blend with this channel. Otherwise it is really only useful to use values 0 and 255 (nothing between). If you really know how to use an image editor like a magician, you could technically have 256 gradients to work with.
Alpha: U-Coordinate of Gradient Texture Sampler. Think of this as an indexed image. This channel provides values from 0-255. These values point to the gradient texture which always has a width of 256 (0-255 inclusive).
◘ Functions marked with a '◘' are split up into quadrants similar to a sprite sheet. This allows for more variations without using animation frames. A random quadrant per model will be chosen during runtime (when the models spawns in game).
When creating this $basetexture be VERY sure that you "save color values for transparent pixels". If you don't do this, you will get seemingly random artifacts. With out this setting: All pixels with alpha=0 will also have red, green, and blue values set to 0.
$nocull <bool>
Determines whether the backsides of faces can be seen. If a model is sliced open, you will see the insides of the model as well.
$translucent <bool>
Disables dynamic shadows on the model.
$cheapdiffuse <bool>
According to a vmt comment: "test stuff, doesn’t do anything right now".
$rttshadowbuild <bool>
Unknown. May be related to RTT shadow rendering. Default value is 0, setting to 1 changes shader combination and renders the model as white.
$translucent_material <string>
Unknown. Possibly allows for a translucent render pass.
$gradienttexture <texture>
Less of a gradient, more of a color palette. This is the texture that will be sampled to give the "true basetexture" it's color.
IF you are creating your own $gradienttexture textures THEN I highly recommend choosing lossless vtf formats such as <"Normal Format = "BGR888" OR "BGRA8888"> and <"Alpha Format" = "BGRA8888">. Also; For $basetexture and $gradienttexture, you may want to set the VTF's flag "Point Sample" to true. This does turn your texture pixelated but any other interpolation methods may create rainbows. Rainbows occur when the $gradienttexture is not very gradienty. For example: In your $basetexture: If you have two pixels the are next to each other and one has value 15 and the other has a value of 100... well, linear interpolation will grab all the colors of the $gradienttexture that are from 15 to 100. Most likey you don't want that.}}
$sheetindex <int>
$colortintgradient <int>
Unknown. Possibly controlled during runtime. (It would make sense to me if this set PalletRowSelector_A as I stated above. Allowing 8 different variations of color.)
$ambientocclusion <bool>
Enables ambient occlusion on the model (for example in Source Filmmaker).
Note.pngNote:While this shader supports many of VertexLitGeneric's shader parameters, it does not support $bumpmap due to the complexity of the shader. From Shading a Bigger, Better Sequel - Techniques in Left 4 Dead 2

Wounds

The most interesting part of this shader is the wound calculation. Set $wounded 1 to enable it.

To debug the wounds, you can use following shader parameters:

$debugellipsoids <bool>
Enables debugging ellipsoids. These can be positioned using other shader parameters to mess with the effect.
There are two debugging ellipsoids with different functionality. Only the second one is currently known to work: it bursts a hole into the model where it intersects.
Leaving out the 2 from the next four shader parameters controls the ellipsoid 1, but no effects could be seen.
$ellipsoidcenter2 <vector>
Center of the ellipsoid 2, in local space (0 0 0 should be the entity's origin).
$ellipsoidup2 <vector>
The up axis of ellipsoid 2. Z-up (0 0 1) by default.
$ellipsoidlookat2 <vector>
The direction that the ellipsoid 2 faces.
$ellipsoidscale2 <vector>
The size of the ellipsoid 2, in local coordinates.
$ellipsoid2culltype <int>
Unknown. 0 seems to be regular culling of ellipsoid 2, other values disable the ellipsoid. (This parameter does not exist for ellipsoid 1.)


The following shader parameters are unknown in function, but seem to be related to the wound rendering. They do not seem to influence the debugging ellipsoid culling at all.

$woundcutouttexture <texture>
Texture containing masks for blood and shapes of the wounds.
$cutouttexturebias <float>
When the cutout texture should mask the wound. Values below 0.5 hide the entire model when using debugging ellipsoids.
$cutoutdecalfalloff <float>
Unknown.
$cutoutdecalmappingscale <float>
Unknown.

Phong

$phong <bool>
$phongboost <float>
$phongtint <vector>
$phongfresnelranges <vector>
$halflambert <bool>
See the relevant articles for these commands. They function identically to VertexLitGeneric's counterparts. However, $phongexponent related options do not work. See below for replacement options.
$defaultphongexponent <float>
Equivalent to $phongexponent, except for any unmasked parts of the texture.

Detail

The detail pass is controlled by the top half of the red channel of the $basetexture. Unlike other textures in Source, this texture is actually transformed differently.

In order to convert a regular $basetexture to the equivalent $detail texture (with same UV mapping), you will need to:

  1. Mirror it horizontally
  2. Rotate it by 90° counter-clockwise
  3. Use a $detailscale of 1

Besides these parameters, $detailframe and $detailblendfactor are also supported.

Skin

There is not really a skin pass as it is part of the Actual Base Texture. Although the original images from the game L4D2 seem to use it on the skin, eyes, and teeth; I don't believe this is entirely necessary. The only time it would be a problem to not follow this rule is: if you really need different phongexponents for skin and non-skin. I'm sure it was so they could use a palette for the clothes and a separate palette for the skin. (see "How the Shader Constructs the actual Base Texture" above)

$skintintgradient <int>
Unknown. Possibly controlled during runtime. (It would make sense to me if this set PalletRowSelector_B as I stated above. Allowing 8 different variations of color.)
$skinphongexponent <float>
Equivalent to $phongexponent, except for skin parts of the texture (blue channel).

Blood

The blood pass is controlled by the green channel of the $basetexture.

$bloodcolor <color>
Color of the blood pass.
$bloodphongexponent <float>
Equivalent to $phongexponent, except for blood parts of the texture (green channel).
$bloodspecboost <float>
Equivalent to $phongboost, except for blood parts of the texture (green channel).
$bloodmaskrange <vector2>
Unknown. Seems to modify the phong shape on blood.
Todo: Something seems wrong! A fully red basetexture is affected by the values of $bloodspecboost and $bloodphongexponent, and $phongboost and $phongexponent do nothing! ???

Burning

$burning <bool>
Controls whether the material should appear burning. Does not seem to do anything.
$burnstrength <float>
Opacity of the burning effect. Defaults to 0.
$burndetailtexture <texture>
Unknown. Most likely the texture to use for the burning effect, implemented similarly to how Team Fortress 2 handles burning models (overlaying a $detail texture).

Eyeglow

The eyeglow pass is controlled by the bottom half of the red channel of the $basetexture.

$eyeglow <bool>
Set to 1 to enable the effect. Causes an additive color overlay, as well as different phong settings.
$eyeglowcolor <color>
Color of the effect.
$eyeglowflashlightboost <float>
Phong boost for the effect, in case the model is hit by projected texture lighting such as the flashlight.

SFM integration

Since Source Filmmaker version 0.9.8.9 (released 27 Aug, 2014), the Infected shader is supported and integrated into the program.

Creating an animation set with the shader will automatically generate the following attributes as part of the DmeGameModel:

infectedSkinTint <int>
infectedClothesTint <int>
Unknown. May be related to the gradient texture controls.
infectedTextureIndex <int>
Controls which of the four quarters of the $basetexture get selected for display (see above for detailed information).
0 is top left, 1 is top right, 2 is bottom left, 3 is bottom right.

See also

Rendering Wounds in Left 4 Dead 2