Layered surface shaders
Layered materials combine multiple sampled surfaces using vertex attributes, masks and material constants. A material’s apparent base colour is therefore not necessarily any one albedo texture. Observed
Sekiro
Section titled “Sekiro”Multiple and MultipleGround
Section titled “Multiple and MultipleGround”The shared vertex programs pass normalized-byte FLVER members to the pixel program as follows. The UV member numbers below identify stored attributes, not separate two-lane interpretations of their bytes. Observed · Sekiro
| Pixel input | Source lanes | Confidence |
|---|---|---|
TEXCOORD5 |
(UV1.x, UV2.y, UV4.x, UV5.y) for MultipleGround; Multiple uses UV2.z in the second lane |
Observed · Sekiro |
TEXCOORD7 |
(UV3.xyz, COLOR1.x) |
Observed · Sekiro |
TEXCOORD12.x |
UV6.z |
Observed · Sekiro |
TEXCOORD0.xy |
Texture coordinates before per-slot scaling | Observed · Sekiro |
These families, with M[Multiple]_Emissive, cover most of Ashina Castle’s drawn surface. Observed · Sekiro
The share is about 80%. Inferred · Sekiro In m11_01, 23 of 790 models store all-zero layer weights, among them m100090_1000 with
M[m11_00]_Ground1. Observed · Sekiro
For the inspected roof-tile permutation 18, the second-layer weight is
w = saturate((v - m*(1-v)) * (1 + float_5)), with
m = (dot(mask3, float3_0)*float_10 + 0.01)*float_4.
The colour path is lerp(lerp(base,layer2,w) * (1 + float_2*(UV3-1)), snowColour, snow).
The snow branch uses its own mask constants and a normal-facing factor. These equations describe that permutation,
not every member of the shader family. Observed · Sekiro
Permutation 27 (M[Multiple]_27_Gbuf.ppo) uses the tint as a mask as well as a colour. With t = UV3.xyz,
L = dot(1 - t, (0.29891, 0.58661, 0.11448)) and m a layer mask of the same form as above,
s = saturate((L - m*(1-L)) * (1 + cb[41].x)) and the albedo is base * (1 + s*(t*cb[28].xyz - 1)), where cb is
the material’s constant buffer. Observed · Sekiro · the program’s code A zero tint gives L = 1 and so s = 1
whatever the mask, wherever cb[41].x is at least 0, and the albedo is black. A white tint gives L = 0, so
s = saturate(-m * (1 + cb[41].x)): 0, leaving the base unchanged, only where m * (1 + cb[41].x) is at least 0
(for example m >= 0 with cb[41].x >= -1). Inferred · Sekiro Executing the program with
M[m11_02]_StoneWall1’s constants (cb[41].x = 3, cb[28] = (4.376, 3.577, 2.740)) gives a mean albedo of 0 at
tint 0, 88 at tint 1 and 106 at tint 0.498 (8-bit levels). Observed · Sekiro · one material
M[MultipleGround] blends the snow albedo m11_snow_01_a by dot(float3_5, normal) × UV5.y, with no time-of-day
or world-state input. Up-facing ground with a stored snow weight near 0.64 saturates to the snow colour (mean RGB
about 228, 233, 243), so near-white ground in Ashina is authored snow. Observed · Sekiro
Executing saved vertex and pixel programs under the WARP rasterizer checks interpolation, sRGB-before-filtering
sampling and the resulting channels against independent execution. The channel comparison permits one byte level
for encoded albedo and normal; float-channel tolerance is one level of 1/255 or one thousandth of the value.
This establishes execution agreement for the supplied states, not the correctness of every runtime constant.
Proven · Sekiro · executed saved states
Some layered programs read a second coordinate set in TEXCOORD0.zw. Stored UV1 differs from UV0 in sampled meshes
from both m10_00 and m11_01, and need not be an affine transform of UV0. Observed · Sekiro
Elden Ring
Section titled “Elden Ring”Layer weights of the Mb families
Section titled “Layer weights of the Mb families”The layered programs of the Mb2, Mb3 and Mb4 families take their layer weights from the second UV member of the
vertex layout, kind 0x13, four unsigned bytes read as byte / 255: 8,140 of 8,860 Mb2, 4,910 of 5,892 Mb3 and 1,360
of 3,512 Mb4 meshes have exactly that second member (a UV short pair first). Over the 28,597,065, 14,421,619 and
6,785,862 vertices of those meshes that have one, the bytes of lanes x, y and z cover 0 to 255 (most are 0, 3% to 18%
are 255) and lane w is 0 on every vertex. Observed · Elden Ring The vertex programs read it as a float input
TEXCOORD1 and pass lanes x, y and z on unchanged to a pixel input; there is no remapping from a signed range.
Observed · Elden Ring
Running the lifted vertex and pixel programs of M[AMSN][Mb2]_Gbuf with the weight swept from 0 to 1 gives the blend
weight w = saturate((v - p (1 - v) (m + 0.01)) (1 + q t)), where v is lane x, m the first lane of the mask
texture, p and q the material’s float_0 and float_1 and t the pixel input TEXCOORD0.w; the albedo is
lerp(AlbedoMap_0, AlbedoMap_1, w). M[AMSN][Mb3][Ov_AN]_Gbuf gives w_a = saturate(1 - 1.4 s1 (m + 1 - 2 a))
and w_b = saturate(1 - 1.4 s2 (m + 1 - 2 b)) with a and b lanes x and y, s1 and s2 its float_1 and
float_2; the albedo is AlbedoMap_1 blended to AlbedoMap_2 by w_a, and that to AlbedoMap_3 by w_b; lane z
changes nothing. Both agree with the formulas read from the disassembly (w = 0.44 at a = 0.4, s1 = 2, mask 0).
Observed · Elden Ring Sweeping each lane of TEXCOORD1 of the *_Gbuf programs of the Mb families (104
programs: 21 without a TEXCOORD1 input, 5 that the executor does not run, 30 whose albedo does not change under the
constants used) shows lane x alone changing the albedo in 36 (30 Mb2, 1 Mb3, 5 Mb5), lanes x and y in 9 (7 Mb3,
2 Mb2), lanes x, y and z in M[AMSN_V][Mb4] and lane z alone in 2 Mb4 programs: the plain families with n layers
use n - 1 lanes. Observed · Elden Ring
The albedo and the instance palette
Section titled “The albedo and the instance palette”Read as value graphs specialised with each material’s own constants, the G-buffer programs of the 62 Mb2, Mb3 and
Mb4 materials of one Stormveil region (11 of them ColorAdjust) all compute the albedo as saturate(Over) × F. Over is
a chain of blends lerp(below, L_i, w_i), one per layer, each weight w_i made of sums, products, saturates, minima and
maxima of vertex lanes (TEXCOORD4.xy and .w, TEXCOORD7.x, TEXCOORD0.w) and mask texels. Observed[62 materials of
one region of m10_00_00_00]{games=“elden-ring”} A layer’s value L_i takes one of three forms:
| Layer value | Where | Confidence |
|---|---|---|
texel × C, with C at most 1 |
the plain Mb2 and Mb3 layers |
Observed · Elden Ring |
lerp(texel, C, f) |
ColorAdjust; f between 0 and 1 (lerp(texel, white, 0.3)) in 4 of the 11 ColorAdjust materials |
Observed · Elden Ring |
texel × C, with C up to 1.47 |
ColorAdjust; the blended colour can then pass 1, so the outer saturate changes it |
Observed · Elden Ring |
TEXCOORD0.w reaches the albedo in the plain Mb2 programs alone: in all 17 materials of M[AMSN_V][Mb2]_Gbuf and
M[AMSN][Mb2]_Skin_Gbuf in the region, and in none of the other 45. There their vertex programs write it from the
vertex colour’s alpha, COLOR0.w, unchanged. Run on each of the 256 byte values the stream can store, the vertex
program gives that value back exactly, with no constant read on the way. It is the t of the Mb2 weight above.
Observed · Elden Ring · the 62 Mb materials of one region of m10_00_00_00, each vertex program run over every stored value
The material constant snp_0_float_6 means different things in different programs. Observed[62 Mb materials of one
region]{games=“elden-ring”}
| Program | snp_0_float_6 |
Read by the albedo | Confidence |
|---|---|---|---|
[Mb2][Ov_AN] (4 materials) |
2.5, and 0 in the _Edge program |
yes: a value computed from it multiplies the overlay’s centred colour (pow(o, 1/2.2) - 0.5) |
Observed · Elden Ring |
plain Mb3 (7) |
1, 1.5 or 2 | yes, with no overlay colour in the program | Observed · Elden Ring |
Mb2, Mb3 and Mb4 ColorAdjust (11) |
1 or 2 | yes, with no overlay colour in the program | Observed · Elden Ring |
[Mb3][Ov_N2_T] (1) |
0.8 | no | Observed · Elden Ring |
plain Mb2, [Ov_N], [Flag][Mb2], [Mb4][3Mask][Ov_AN] (39) |
not stated | no | Observed · Elden Ring |
So snp_0_float_6 is the overlay’s strength only where an Ov_AN overlay colours the albedo; in the plain Mb3
and ColorAdjust programs it feeds some other part of the albedo, not established which. Inferred · Elden Ring
F is a colour of the drawing instance’s palette: aColor[13 × r + 9 + FC_ColorSlotNo] of cbInstanceData (b4), the
four colours at byte 144 of the 208-byte record r, as the SpeedTree leaf
program reads them. The pixel program’s record r arrives as NORMAL0.w,
which is no vertex stream here: every Mb vertex program writes it from the instance table, at
VC_InstanceIdxOffset + SV_InstanceID, bounds-checked. Observed[the vertex programs of every Mb material of the same
region]{games=“elden-ring”}
In the recorded frames of two sessions at the Stormveil gate (2,076 draws of 26 Mb pixel programs, 1,476 of them
with b4 recorded), every record an instance can address (2,474 records, among them 1,299 drawn by Mb3_ColorAdjust,
26 by Mb2_ColorAdjust and 16 by Mb4_ColorAdjust) holds the same palette, four slots of (1, 1, 1, 1), so F is white
there. Observed · Elden Ring · two recorded sessions, one area The records past the instance count hold world-matrix
rows, zeros and non-numbers: stale buffer space, not palettes. Observed · Elden Ring That instance i reads
record i is inferred from the addressed records being well formed (a valid mWorld in rows 0 to 2); the instance
table itself is not recorded. Inferred · Elden Ring
The palette is not white everywhere. The vertex program reads the record from g_InstanceIndexBuffer (t25) at
VC_InstanceIdxOffset (c0.x of b10) plus the instance id, and that buffer is recorded, so the addressed record of each
draw can be read. Over nine Stormveil frames, of 20,164 distinct (draw, record) pairs, 20,041 select a white palette and 123
select a different one: the 14 instances a frame of the single S[AMSN]_Glow_Gbuf draw, whose four slots hold the same
bright orange, about (18 to 19, 5.6 to 6.3, 1.2, 1). Observed · Elden Ring · nine captured frames of one area The
unselected slots 1 and 2 of many M[AMSN] records hold (0.706, 0.706, 1, 1) and (0.706, 0.706, 0.392, 1), so the table holds
values other than white that no addressed record of these frames selects. Observed · Elden Ring · nine captured frames of one area
Outside the Stormveil gate, whether other areas draw coloured palettes is not established, nor is what fills the table or which object the glow draw is.
Masked blends and overlays
Section titled “Masked blends and overlays”The inspected M[AMSN][Mb2][Ov_AN]_Gbuf.ppo uses
w2 = saturate(1 - 1.4*s*(mask.r + 1 - 2*v)), where v = TEXCOORD1.x and s is snp_0_float_1.
A saved comparison against WARP covers 81,240 lanes. In the material read (m60_fort_216), s is 5.
Observed · Elden Ring
In the multi-layer families (Mb2, Mb4, GSBlendMap) the base layer is AlbedoMap_1 with NormalMap_1; layer 0
(Ov_AN) is an overlay blended last. Observed · Elden Ring
The same program’s overlay uses a colour-dependent weight per channel:
x = t*(pow(o,1/2.2)-0.5) and c = lerp(a,o,x*x), with t = snp_0_float_6.
The result is saturated and multiplied by the diffuse-map multiplier and instance colour.
This is not a single scalar mask shared by RGB. Observed · Elden Ring
For character DetailBlend shaders, Texture2D_7 carries the character’s base albedo: its own _a texture, of
which the game streams the _l variant. Shared aat100 detail albedos at slots 1, 10 and 4 multiply over it at their own tilings; masks come from normal texture Texture2D_0 alpha.
Program inspection and an in-world capture agree on those roles. Observed · Elden Ring
The inspected Fur family multiplies the strand-card texture at slot 1 by a colour texture at slot 8. Two-sided
cloth permutations with switch 18 select back-face colour from slot 8 using SV_IsFrontFace.
Observed · Elden Ring
The detail-blend equations describe the separately executed character family, including Sekiro’s different slot assignment and reflectance encoding.
Family and switch names do not prove an identical blend graph, and the disassembly of one frame says the same: its 12 Mb and ColorAdjust G-buffer
pixel programs have 12 distinct bodies, and among the 118 depth, motion-vector and G-buffer programs of that frame the only shared bodies are depth-only and motion-vector programs of the
same family’s switches, the cloth variants of C[DetailBlend] and C[Fur], and the SpeedTree wind variants: no G-buffer program of the Mb and ColorAdjust families matches
another. Observed · Elden Ring So each permutation’s weight graph has to be
read for itself; the formulas above stay scoped to the programs they were derived from, and snow, instance colour and secondary UV inputs still need their
own bindings. Settling it needs no new capture for the weight graphs (execute each program as for Mb2 and Mb3); runtime snow and instance colour need a frame in a
snowfield or area with a coloured palette, which the Stormveil frames lack. Unknown