Shader Builders (.shader_builder)
Many shaders in Spore are not coded in a single .hlsl file directly. Instead, they are generated by combining different pieces of code, known as Shader Fragments. These fragments are then combined under certain conditions in the Shader Builder files.
A shader builder contains two lists of references to fragments, with certain conditions that the game must meet for that fragment to be selected. When the game needs a shader, all these conditions are checked, the correct fragments are selected and merged, and a shader is generated.
There are two types of list, both must be included in the same file:
vertexShader: Can only use those fragments that end with the
pixelShader: Can only use those fragments that end with the
For example, a builder list for a vertex shader might look something like this:
vertexShader add copyVertexColor -elements color add getAngle add toClipSpace end
In this example, there are fragments that are always selected (
toClipSpace) and other that depend, in this case, on whether the model has vertex colors.
Shader builders are stored in the
material_shaders~ (0x40212004) folder, in a
.smt file; when unpacked, they finish with the extension
.shader_builder. You will notice that unpacked shader builder have special names, something like
0x70000034(TerrainDebugShader).shader_builder. The real ID that must be used to reference this shader on a material is the one outside the parenthesis, that is,
0x70000034. When you create your custom shader you don't need to do that: to reference
MyShader.shader_builder just use
Coding custom shader builder
It is possible to create your custom shader builders packs. Like Standard Shaders, you can have your own
MyShaders.smt.unpacked folder (where MyShaders is whatever name you want, unique to your mod) containing standard shaders and shader builders. In order to pack it, you also need to have a shader fragments pack in your project.
By default, no shader builders are compiled. This means that if you want to use them in the game, you will need to enable the
FragmentCompilation property in the
DataEP1\Config folder. This way, Spore will compile them on runtime:
This is okay for developing, but when you release your mod you don't want the users to enable that. So instead, you can pre-compile the shaders you use. To do so, you must add two files to your shader pack:
precompiled_vsh.txt (for vertex shaders) and
precompiled_psh.txt (for vertex shaders).
Each file will be a pre-compiled shader. Just add the names of the shader fragments you want to compile, in order. For example:
unpackNormalTangent applyMaterialColor toClipSpace unpackNormalTangent whiteVertexColor applyMaterialColor toClipSpace
Apart from using
add to add fragments, it's also possible to choose one from a group of possible selections. For example:
group 0x1 select applyShadowMapTerrainNest -compareData shadowMapInfo 3 select applyShadowMapTerrain -compareData shadowMapInfo 2 select applyShadowMapNull end
This will select, at most, only one of those fragments; if
applyShadowMapTerrainNest cannot be used, it will try with
applyShadowMapTerrain, and so on. It is possible that no fragment in the group is selected at all. Notice that inside a group, you use
select instead of
When defining a group you must also give it a name.
select have the same attributes and options:
fragmentName is the name of the fragment to be used; if this is inside a vertex shader only vertex fragments can be used, and the same goes for pixel shaders.
Add and select instructions can have certain conditions that define when these will be used. If no conditions are specified, then the fragments are always used.
Only uses the fragments if the input model has data for the specified vertex elements. The available elements are those accepted as inputs of vertex fragments. For example,
add myFragment -elements position color.
Only uses the fragments if all of the selection groups specified have been used as well. For example, if we have a group caled
VertexColoring, we can do
add myFragment -require VertexColoring.
Only uses the fragments if none of the selection groups specified have been used. For example, if we have a group caled
VertexColoring, we can do
add myFragment -exclude VertexColoring.
These conditions are mutually exclusive, meaning that only one of them can be used:
Uses the fragment if the game has defined data for the specified Shader Data names; you can use from 1 to 3 names. For example,
add myFragment -hasData skinWeights cellStage
-compareData dataName value
Uses the fragment if the game has defined data for the specified Shader Data, and that has the same value as specified. For example,
select myFragment -compareData shadowMapInfo 2
Uses the fragment if the game has defined data for the specified Shader Data. Furthermore, it will use that data value as an array index, so if the fragment is at index 123 and the shader data has value 5, it will use the fragment 123+5 = 128. For example,
select uvTweak_projectXY -array uvTweak: all fragments after
uvTweak_projectXY are also related, the one that is sued is determined by the value of
Uses the fragment if the game has defined
objectTypeColor data and it has the same value as specified. For example,
select myFragment -objectTypeColor 2
Uses the fragment if the game has defined a texture sampler for the given inde. For example,
select myFragment -hasSampler 0
-unk12 value1 value2
Example of a working shader builder file (
Spore has a special feature called render types: a single shader can have multiple versions of the code depending on the current render type. For example, there's one shader for rendering creatures (
generic_skinning), but it has different code for the hologram render type (
15). The only known render types are:
0: the base and default render type
In your custom sahders you can provide the different versions on the code by adding their number at the extension. For example, to make the code for
MyShader at render type
15 you must use
MyShader.15.vshader.hlsl (and same for
.pshader). When no number is specified, the sahder will be packed for the default render type.