Skip to content

The rendered frame

FromSoftware engineLighting and skyApplies toSekiroElden Ring

Both games draw a frame the same way: shadow maps, a G-buffer of the scene’s opaque surfaces, screen-space passes over it (ambient occlusion, the sun’s shadow mask, reflections), deferred light into two light targets (diffuse and specular), a composite with the albedo, volumetric fog, the sky and forward surfaces, effects, motion vectors and temporal anti-aliasing, glow, exposure, bloom and a tone-mapping look-up into a 10-bit picture, then the interface. The renderer’s own passes are the programs of the gxrenderershader and gxposteffect bundles; the scene’s draws are the materials’ programs. Observed

The order and targets below come from one frame of each game recorded in GPU execution order (every draw, dispatch, clear and copy of the frame with its programs and bindings): Sekiro 1.06 at 1920x1080 at the Ashina Castle gate, Elden Ring 1.17.2 at 2560x1440 at night at the Stormveil gate. Programs are named by matching their bytecode with the bundles’. Observed

# Pass (programs) Sekiro Elden Ring Confidence
1 Shadow casters, GXRend_CopyShadowMapDepth between maps R16 4096x2048 and four R16 4096x4096 D16 atlases: 6144x4096, 8192x4096, 4096x4096 Observed
2 G-buffer: the scene’s opaque materials targets as on G-buffer: RGB10A2, RGBA8, RGBA8, RGBA8, RGBA16F; depth D32S8 RGB10A2, RGBA8, RGBA8 sRGB, RG8, RGBA16F; depth D32S8 Observed
3 Depth reductions: DepthScale2x2OutputDepth one level two levels, then DownSampleDepth16CS (compute) to a 160x90 RG32F tile map and a 1x1 Observed
4 Ambient occlusion: ScreenSpaceAO2_WithNormal, _SeparatedReProj (temporal), _BilateralUpScale half resolution, up to full the same, to R8 Observed
5 Sun shadow mask: GXScreenSpaceCascadedShadow_PCF9, GXRend_CompositeShadowMask splits 10 / 50 / 200 m, fades 2 / 10 / 40 m; plus a view-space shadow volume (GXViewSpaceShadowVolume_*) and two blurs neither the volume nor the blurs among the named programs Observed
6 Screen-space reflections: GXSsrConvertDepth, GXSsrNew, then GXSsrTexDownScale / GXSsrTexBlur2 RGBA16F, half resolution, 6 mips a prepass of programs not named in the recording, then the down-scale and blur chain, five levels Observed
7 Environment-map regions: one box draw per region, GXEnvMapRegion[Box][DiffSpc] and variants 61 regions in the frame 64 Observed
8 Sun: GXLightAcc_DDirLightShadowed1 after GXLightDeferredAOWithSsr after GXLightDeferredAOWithSsr Observed
9 Point and spot lights: GXLightOrganize<N> (compute, tile lists) then a tiled light pass GXLightOrganize16, GXRend_TileBasedLighting GXLightOrganize8 and GXRend_TileBasedLighting, and GXLightAcc_DPointLightShadowed1 for a shadowed point light Observed
10 GXLightCmbAlbedo, GXSssBlurX / Y, GXLightComposite into R11G11B10F then OutputColor Observed
11 Volumetric fog: GXVolumetricFog_DirLightShadowReproj, _WriteInScatteringPreShadowed, a dispatch per local light, _SolveVolumetricFog, _SceneComposite froxels 120x72x16 144x88x36, plus _DirLightShadowPostFilter; then GXFogVolume_* fog volumes at half resolution Observed
12 Sky and forward surfaces the sky’s materials (below), depth-only and blended material draws before pass 11: an OutputColor fill and the sky shell at depth 0 (below), among forward draws Observed
13 Effects, distortion, half-resolution particles with SampleBilateralDepthBlurTwo Observed
14 Motion vectors MotionBlurWriteCMBVelocity, then StencilAndMotionVectorPreprocess, TempAA[StencilMask], SharpenFilter Observed
15 Glow: EmissiveToGlowPS, Sample13Weight down, GlowCompositeSinglePS up 6 levels 7 levels Observed
16 Exposure: ToneMap_ColorToLuminancePS (64x64), ToneMap_ScaleLuminancePS (16, 4, 1), ToneMap_AdaptedLuminancePS (1x1 with the previous frame’s) RG32F the same, after ToneMap_DownSample2x2PS Observed
17 Tone curve and grading: ToneMap_WriteReinhardExToneMappingTablePS (1024x1 R16F), a 16³ look-up ToneMap_Blend2Dto3DLUTCS, RGBA32F ToneMap_Copy2Dto3DLUTCS, RGBA16F Observed
18 Bloom: ToneMap_BrightPassTemporalPS, ToneMap_BloomGaussBlurPS chain, …CompositeUpScalePS, …CompositeSinglePS Observed
19 ToneMap_TabledToneMapLUTPS into RGB10A2 then ToneMap_PostOETFPS Observed
20 Interface, GXSimple_BlendUI_SDR2SDR_F to the back buffer Scaleform draws Observed

Over more recorded frames (Sekiro: 9 frames of 3 sessions at 1920x1080; Elden Ring: 7 frames of 2 sessions at 2560x1440): Observed

  • Every full-screen draw of Sekiro binds one 3-vertex buffer, a triangle over the screen: positions (−1, 1, 1, 1), (3, 1, 1, 1), (−1, −3, 1, 1) with texture coordinates (0, 0), (2, 0), (0, 2). Observed · Sekiro
  • Targets sized from the view round up: 1080 / 16 gives 68 rows in Sekiro, and Elden Ring’s bloom chain is 80x45, 40x23, 20x12, 10x6. Observed
  • The tiled light lists: Elden Ring’s GXLightOrganize8 writes 1,382,400 bytes at u0 (345,600 words) and 43,264 at u1 (3,600 records of 12 bytes); Sekiro’s GXLightOrganize16 writes 522,240 bytes at u0. Observed
  • The last tone-map step’s target is RGB10A2 in Sekiro’s nine frames and in five Elden Ring frames of one session, and RGBA8 in two frames of another Elden Ring session; the scene targets of both are 2560x1440, and the RGBA8 session ran on a display that reported 8 bits per colour channel with advanced colour (HDR) supported and off. What chooses the format is not shown. Observed · Elden Ring · 7 Elden Ring frames of 2 sessions Unknown
  • Elden Ring draws the shadows of local lights into a 4096x4096 R16 atlas in 64x64 regions (26 in one frame), which GXLightAcc_DPointLightShadowed1 reads at t7. It is there in the 2 of 7 frames that have shadowed point lights. Observed · Elden Ring
  • Elden Ring runs the screen-space reflection chain and DeferredAOWithSsr only in the frames that draw M[Water] (5 of 7). Observed · Elden Ring
  • Elden Ring’s GXEnvMapRegion_CopyCubeToCubeLerp writes an 8-mip 128x128 R11G11B10F cube through u0 to u7, and the region variant [LerpDiff] reads it at t25. Observed · Elden Ring Its program writes uk only inside size >> k, from the source cubes at level k, so uk is mip k. Inferred · Elden Ring · from the program
  • Elden Ring’s ToneMap_AdaptedLuminancePS writes one RG32F 1x1 target and reads another at t4 that no recorded frame writes. Observed · Elden Ring
  • The order of fog and sky differs: Sekiro composites the volumetric fog (GXVolumetricFog_SceneComposite) before its sky draws, while Elden Ring draws its sky’s cloud shell (M[Clouds9] with an _FwdSdw program) before its fog composite. Observed

The forward or G-buffer rule decides which sky materials are drawn forward. In both games the sky’s colour comes from forward programs: Elden Ring’s G-buffer variants of its skysphere and cloud materials (M[Sky9]_Gbuf, M[DistantClouds]_001_Gbuf, M[Clouds5]_Gbuf, M[Atmosphere][Clouds]_001_Gbuf) write no colour, only a normal (normal * 0.5 + 0.5 in target 0) and alpha words, every colour lane being the constant 0 (M[Sky9] samples no texture). Observed · Elden Ring · the programs translated and read, for the tile m60_42_36_00

The scene constants cbSceneParam carry SC_PreExposure and SC_InvPreExposure. Both are 1 in every program of the recorded Sekiro frame, and in all 50 distinct cbSceneParam contents the Elden Ring frame binds (and its loading frame). Observed So in these frames neither game scales its light targets by a pre-exposure factor through this constant.

Elden Ring’s cbSceneParam also carries SC_SceneExposure and SC_InvSceneExposure (offsets 1152 and 1156, reciprocal): 37.696 at night at the Stormveil gate and 19.886 in a loading frame. Observed · Elden Ring None of the 96 programs that declare cbSceneParam in the gxrenderershader, gxposteffect, gxffxshader, gxshader, gxdecal and grass bundles reads them: the 72 that read that row read only SC_PreExposure, by their DXIL. Observed · Elden Ring The scene exposure is therefore a value of the CPU side, which a light packer can apply to the records it writes. Inferred · Elden Ring

The sky is not a pass of its own. It is drawn as material draws (programs of the materials’ shader bundles), in one command list right after the volumetric fog’s composite, into both light targets with depth testing: boxes (GXSimple_Transform_V) that write neither colour nor depth, the dome (M[Sky]_SkyDome_Ver2, 1,824 indices), the moon (M[Moon], a quad of 6 indices), the far mountains (M[LandScape]_FarMountain) and the cloud layer (M[LandScape]_CloudFog, 60,324 indices), in two recorded frames of the sunset sky. Observed · Sekiro The dome, the moon, the far mountains and the cloud layer draw with the same states: blended by the source alpha (SRC_ALPHA, INV_SRC_ALPHA), the depth tested with GREATER_EQUAL (the depth is reversed) and written, back faces culled. Observed · Sekiro The dome’s pixel program reads the sun’s direction from cbLight, FC_GlobalTime, the camera, the volumetric fog and 37 material values of its own, per its resource definitions; no separate sun-disc draw is in the frame. Observed · Sekiro These draws are in pass 0 (Fwd), not the shadowed forward pass, by a breakpoint on the running game’s pass id (see bundles). Observed · Sekiro None of the map pieces the layouts of the nine maps place draws a material whose name or definition names the sky or the moon (every map piece model of each layout read, a positive control finding 1,590 pieces with AMSN materials). Observed · Sekiro The sky is a map of its own, m89_00_00_99, whose layout places at the origin one dome per sky state (map pieces m900000 to m900095, materials M[Sky]_SkyDome_<state>: morning, afternoon, evening, night, past night, dawn, dark clouds, storm, fog, cloud sea, fire, and two night domes with a cloud layer), one ring of far mountains per state (m900100 to m900200, M[LandScape]_FarMtn_<state>) and the moon, the object o005900. Observed · Sekiro Each dome is 12.6 km across (±6,293.6 m) and spans −1,191.2 m to 3,000 m in height, 1,824 indices. Observed · Sekiro m10_00_00_00, m11_01_00_00 and m17_00_00_00 keep domes of their own (±6,000 m, 0 to 3,000 m) that their layouts do not place. Observed · Sekiro In the recorded frame the dome draw’s index and vertex data are byte for byte those of the m89_00_00_99 morning, evening and night domes, which share their geometry. Observed · Sekiro The draw drew the evening dome: its material constants (cbMtdParam) equal M[Sky]_SkyDome_Evening’s parameters on all 37 fields, a colour being its stored value raised to the power 2.2 and multiplied by its -Intensity parameter, in nine recorded frames of one save at the 18:00 lighting; the next closest state (dawn) matches 28 fields. Observed · Sekiro Each of those frames draws one dome and no blend of two. Observed · Sekiro

The game’s executable loads the sky map’s 27 models and the moon’s object from a fixed list of paths whatever the area. Observed · Sekiro Its run-time code moves the sky map’s parts every frame: each part whose enabled flag is set gets the world matrix “translation (camera x, 0, camera z) times the part’s own transform”, and the moon is then placed at a distance the in-game step stores as 25.0 when it is built (a debug value, Moon Distance, can change it), read from the decrypted code of the running game. Inferred · Sekiro The part’s enabled flag is set only by the engine’s enable-entity call, by entity id, in the same code. Inferred · Sekiro The layout puts every dome, ring and the moon in draw group 0, so draw groups do not pick the state. Observed · Sekiro

In the running game the sky map’s 27 parts are one block of map parts, and one dome and one ring are enabled among them. At an Ashina Castle scene, read twice 40 s apart, two of the 27 have bit 0x200 set in the 32-bit word at part +0x14C (0x04043E00 against 0x04043C00 on the other 25): the dome m900020_0000 (entity 8904020) and the ring m900120_0000 (entity 8904021), the ring’s number being the dome’s plus 100. Observed · Sekiro · one scene, two readings So the game shows a sky by enabling one dome and its ring, not by choosing among drawn domes when it renders. Inferred · Sekiro m900020 is the evening dome. Observed · Sekiro

Event 100 of common.emevd (initialised by instruction 6 of common event 50) chooses the sky and the environment volumes from four story flags, 9800 to 9803. After branches for particular maps and flags (one for map 10 selects environment 0), it takes the first of these that holds: Observed · Sekiro · the installed script, read instruction by instruction

Flags 9800 to 9803 Part group enabled (2012:1) 2003:80 environment
all off 8907000 1
9800 on, the rest off 8907010 2
9800 and 9801 on, 9802 and 9803 off 8907020 3
9800 to 9802 on, 9803 off 8907030 4
all on 8907050 5

The third branch also clears flag 9810 and sets animation 4 on the sky object 8901000. 2012:1 takes the group, not a part: the one call for group 8907020 enables the evening dome (entity 8904020) and its ring (8904021); in a run of the game from a cold start, all 29 calls the script made reached those parts. Observed · Sekiro · 29 of 29 calls, one run The sky therefore follows the story’s world state, not the clock hour. Inferred · Sekiro The environment number of 2003:80 is the package suffix of the environment volumes the frame draws: the evening branch’s 3 goes with _03 below. Inferred · Sekiro

The walls’ diffuse ambient comes from volumes, not from the light set. Each of m11_01’s six packages m11_01_00_00_envmap_00 to _05.tpf.dcx holds 580 textures: 464 BC7 (DXGI 98, package code 102) single-mip volume textures and 116 cube maps, the volumes being 116 complete groups GIIV<nnnn>_<ss>_W, _X, _Y and _Z, and every one of the 107 EnvironmentMapEffectBox regions of the layout has a group in every package (9 groups more are not placed). Observed · Sekiro · all six packages, read with an independent reader The four volumes of a group usually share one grid, but not always: six groups of m11_00 (packages 01 and 05: groups 0250, 0300, 0307 and 0323) mix sizes, 0250_05 having X, Z and W of 40 × 20 × 36 and Y of 64 × 36 × 60. Every group of m11_01, and every group of the _03 packages, shares one grid. Observed · Sekiro · the DDS headers of the m11_00, m11_01 and m11_02 packages

In a frame at the evening lighting, every one of the 1,524 volume textures the game created matched, byte for byte, a GIIV texture of the packages, and each of the 61 GXEnvMapRegion draws bound the four volumes X, Y, Z and W of one group of package _03, in that order, including regions of the neighbouring maps m11_00 and m11_02. Observed[one frame, the uploads hooked]{games=“sekiro”} The neighbouring maps’ regions draw in an m11_01 frame: the region draws of one frame included m11_00’s box 000 (centre (102.4, 583, 8.69), 13,520 × 1,600 × 13,439 m) and m11_02’s (centre (−8.5, −67.3, −62.5), 10,000 × 1,250 × 10,000 m) beside m11_01’s own; a drawn centre is the layout’s position plus half the box’s height. Observed · Sekiro · one frame

The diffuse region program decodes each coefficient texel as (rgb − 0.5) × 2 × 2^((a − 0.5) × 20), takes per colour channel the dot product of a coefficient row with (n, 1), clamps it at 0 and multiplies by the pre-exposure; it scales by its diffuse colour scale (1) and does not read g_envMapColorScale, and it samples the volumes trilinearly at level 0, clamped, at a position offset along the normal. Observed · Sekiro · the program’s disassembly

Each EnvironmentMapEffectBox region of a layout is one box draw of a GXEnvMapRegion program, which adds the region’s diffuse and specular light. In one recorded frame of m11_01 at 18:00 (61 region draws, each matched to its box by g_envMapDrawAreaExtents and the translation of g_envMapWorld, the box’s position plus half its height) the programs were [Box][DiffSpc] 42 times, [Box][DiffSpcPcc] 9, [Stencil] 5, [Box][Diff] 3 and [Box][SpcPcc] 2. Observed · Sekiro · one frame

The box’s type data chooses the program and supplies its constants. The names are SoulsFormats’ for the type data’s words. Observed · Sekiro · 61 region draws of one frame

Type data Use Confidence
UnkT08 1: parallax-corrected specular (the Pcc programs); 1 on all 11 Pcc draws, 0 on the other 50 Observed · Sekiro
UnkT09 90 on the reflection boxes below; the region’s priority Inferred · Sekiro
UnkT24 (i32 at 0x24) 0 diffuse and specular ([DiffSpc], [DiffSpcPcc], [Stencil]), 1 diffuse only ([Diff]), 2 specular only ([SpcPcc]): 56, 3 and 2 draws Observed · Sekiro
UnkT28 (f32 at 0x28) g_envMapDiffuseColorScale, bit for bit on 61 of 61 draws Observed · Sekiro
UnkT2C (f32 at 0x2C) g_envMapColorScale, the specular cube’s scale, bit for bit on the 58 draws whose program reads it Observed · Sekiro

The three diffuse-only draws are each map’s whole-map box Env_Box000, and the two specular-only ones m11_01’s and m11_00’s Env_Box900. m11_01’s boxes 900 to 917, named 映りこみ用 (“for reflections”), have priority 90 and UnkT08 1, and m11_00 and m11_02 have one each. Observed · Sekiro Of m11_01’s 107 boxes, 87 store 0 in UnkT24, 2 store 1, 17 store 2, and Env_Box902 stores 258 (0x102), whose 0x100 bit has no known meaning. Observed · Sekiro Lighting four recorded wall points from the irradiance volumes of the 89 boxes that store 0 or 1, leaving out the specular-only boxes, gives the frame’s four packed R11G11B10 irradiance words bit for bit. Observed · Sekiro · four points of one frame For a parallax-corrected region, g_envMapMatPccObb is the box itself (g_envMapWorld) on all 11 draws, and g_envMapRegionPccOrigin (cb3 byte 288) is the position of the environment point of the same number (Env_Point<id>) bit for bit on all 7 of m11_01’s. Observed · Sekiro · one frame g_envMapInterpRatio is 0 on every draw, and g_envMapFadeRate 1.0 on 59 (0.468 and 0.324 on the others). Observed · Sekiro · one frame

The region’s specular cube (t25) is <map>_GILM<id>_<package> in the same environment package as its irradiance volumes: a BC6H_UF16 (DXGI 95) cube of 128 × 128 with 7 levels, one per coefficient group. Package _03 of m11_01 holds 116, their ids those of its 116 GIIV groups one for one; every package of m11_00 (1 to 5) holds 55 and every one of m11_02 20. Observed · Sekiro · the packages listed with an independent reader All 61 draws bind such a cube at t25. Observed · Sekiro · one frame

The program, by its disassembly: Observed · Sekiro · the programs’ disassembly

  • It writes specular to o0 and diffuse to o1, both with one alpha. With local = g_envMapInvWorld × P and edge = min((1 − |local|) × g_envMapDrawAreaExtents), a pixel with edge < 0 writes 0 with alpha 0; otherwise alpha = smoothstep(saturate(edge × g_envMapInvFadeDistance)) × g_envMapFadeRate.
  • The reflection is r = 2(n·v)n − v, v the unit vector to the eye and n the G-buffer normal (× 2 − 1, normalised).
  • A Pcc program moves the ray into the unit box (q = g_envMapMatInvPccObb × P, d its rotation of r), takes per axis |(1 − q) / d| where d > 0, |(1 + q) / d| where d < 0 and 5,000 otherwise, the least of these t, and the exit point q + t d. If any coordinate of the exit lies outside ±(1 + 1e-4 t) the specular is 0 (the alpha stays); otherwise the cube is sampled along g_envMapMatPccObb × exit − g_envMapRegionPccOrigin. The other programs sample along r.
  • The cube is sampled (SS_WrapLinear) at level min(6.5 − 6.5 × gloss, 6), gloss being g_GBuffer1.x (t3). o0 is the texel times g_envMapColorScale times the pre-exposure (cb8[62].z); o1 is the volumes’ irradiance times g_envMapDiffuseColorScale times the pre-exposure.

GXLightDeferredAOWithSsr then applies ambient occlusion and the ambient BRDF: Observed · Sekiro · the program’s disassembly

  • AO = t32.x × t32.y (g_ShadowTexture), and the diffuse out is AO times the diffuse sum.
  • (A, B) is g_AmbientBRDFTexture (t39) .rg, sampled SS_ClampLinear at (min(|n·v|, 1), gloss) with n in view space, and envBRDF = GBuffer3.rgb² × A + B (t5).
  • The specular sum (t7) times envBRDF is blended with the screen-space reflections (t44, sampled at level 5 × saturate((1 − gloss) × cb0[20].w + cb0[21].x)) by k = min(saturate(max(f0) × 10 − cb0[20].z), depth fade): AO × (spec × (1 − k × ssr.a) + envBRDF × ssr.rgb × k).

GXLightComposite adds that to the diffuse light times the albedo. Observed · Sekiro · the program’s disassembly

The ambient BRDF table is data of the executable, not of a texture file. The renderer’s setting AmbientBRDF LUT (LUT Type) names five tables, R8G8_OLD_256, R8G8_UE4_128, R8G8_UE4_256, R8G8_UE4_512 and R16G16_UE4_256, and each has a loader that copies its table out of .rdata into a new texture. R16G16_UE4_256 is 0x40000 bytes at VA 0x143114D30 of the 1.06 executable (SHA-256 637aca52…8b856; file offset 0x3114130, the section being stored unencrypted), a 256 × 256 R16G16_UNORM texture with a row pitch of 1,024; the table’s SHA-256 is aebecda11cf0543a67b87c1585dff816226e33e46653983cbb46cc945ef532fb. The renderer’s set-up creates all five and makes R16G16_UE4_256 the current one whenever it exists, and the setting is bound to that same pointer, so only the parameter editor changes it. Observed · Sekiro · read from the decrypted code and the executable file All three recorded frames bind a format-35 (R16G16_UNORM) texture at t39, the only R16G16 type. Observed · Sekiro · three frames That the bound texels are that table’s bytes is not yet read back from the running game. Inferred · Sekiro

The tone curve, colour matrix, vignette and grading look-up, with Sekiro’s recorded constants, are on exposure, tone curve and colour grading.

GXVolumetricFog_SolveVolumetricFog runs as 15 × 9 × 1 groups of 8 × 8 × 1 threads. It integrates the 16 slices of 120 × 72 froxels from two R32_UINT targets that pack density and blue, and red and green, as half floats, and writes slices 0 to 15 in RGBA16F, the accumulated end in slice 16 and, with EnableAnalyticalFog 0, a neutral (0, 0, 0, 1) in slice 17: a 120 × 72 × 18 volume, which the sky draw reads. Before it run the sun’s shadow reprojection, the in-scattering and one dispatch per local point light (four in the frame). Observed · Sekiro · one frame, the program’s disassembly How its constants come from the draw parameters is on GPARAM.

The in-scattering dispatch (GXVolumetricFog program 2fce80b2c53e19ff) gives each froxel the density fixedDensity + heightDensity × H(h), with h the world height times the height factor and the height distribution, and H(h) = exp(−|h|) for h ≥ 0, 2 − exp(−|h|) below. Computed from the dispatch’s constant buffer and narrowed toward zero to f16, it gives the density half of all 138,240 froxel words of the 120 × 72 × 16 grid bit for bit. Observed · Sekiro · one frame In the solve, the recorded slice 16 repeats slice 15. An emulation of the solve on the recorded inputs, with fused arithmetic and narrowing toward zero, gives 622,048 of its 622,080 half-words; the other 32 are not explained. Observed · Sekiro · one frame

The moon o005900 is a 10 by 10 quad with the material M[Moon] (masked by the texture m99_Moon_3m), skinned whole to the second of its two bones, with a skeleton and animations of its own; the recorded moon draw’s vertex and index data are byte for byte its model’s. Observed · Sekiro

A map’s _farmodel param (FAR_MODEL_PARAM_ST) names placed parts by parts group, not the sky. Observed · Sekiro

The dome and the far mountains follow the camera across the ground and keep their height: their object matrices are the identity rotation at the camera’s horizontal position and height 0. Observed · Sekiro

The moon follows the light: in both frames its quad’s object matrix places it 25.0 units from the camera exactly along the light set’s direction towards its first directional light (0, 0.4226, −0.9063), with its rotation built from the same direction so that it faces the camera. Observed · Sekiro The quad is 10 by 10 units (corners at ±5 in its own x and y), its normal its own −z, and its vertices select the second of the draw’s two object matrices, whose z axis is that direction, whose y axis is the world’s up made perpendicular to it, and whose x axis completes a right-handed frame. Observed · Sekiro So the moon moves with the light set’s Angle0 over the day. Inferred · Sekiro

Which part groups hold which domes?

Group 8907020 holds the evening dome and its ring; which domes the other four groups enable, and what the map and flag branches before the table choose, were not read from the layouts.

The sky is a family of assets, AEG096 (82 asset binders): skyspheres 53 to 86 km across (aeg096_010 to _016, _101, _102), cloud layers (_400 and up), the aurora (_201), a rainbow (_200) and pieces of the Erdtree, drawn by the sky material families (M[Sky3] to M[Sky9], M[Clouds…], M[Atmosphere][Clouds]_…, M[DistantClouds]_…) whose materials are named Field_Skysphere_…. Observed · Elden Ring They are placed by the areas’ _99 maps: the open world’s m60_00_00_99 places 38 of them. Observed · Elden Ring

The sky’s forward draws do not share one render state. With a GreaterEqual depth test in each, the recorded draws of the Stormveil gate set:

Pixel program Colour blend Depth write Confidence
M[Sky9]_FwdSdw off on Observed · Elden Ring · three recorded draws
M[Clouds5]_FwdSdw source alpha, one minus source alpha; alpha One, Zero on Observed · Elden Ring · three recorded draws
M[DistantClouds]_001_FwdSdw the same off Observed · Elden Ring · three recorded draws
M[Clouds9]_FwdSdw the same as M[Clouds5] on, at depth 0 (below) Observed · Elden Ring · three draws of three other frames

The materials of Field_Skysphere_10, Clouds400, Clouds410 and Clouds411 state the matching blend mode (0 for M[Sky9], 2 for the two cloud families) and, for the distant clouds, a flag that turns depth writes off. Observed · Elden Ring · those four materials

The sky is drawn where nothing else is: into the pixels whose depth is still 0 after the G-buffer, the far end of the reversed depth. In three recorded frames at the Stormveil gate at about 8 h (the camera at three places up to 9.3 m apart, 2560x1440), the sky is two draws between GXLightComposite and the volumetric fog’s composite: Observed · Elden Ring · three frames

  1. OutputColor (vertex program ScreenSpaceQuadShaderZ), one full-screen triangle at the depth cbVtxParam.g_vtxParam.x, 0, with the depth test GreaterEqual, no depth write and no blending, right after the composite: it writes cbColor.g_fillColor, (0.013840831, 0.013840831, 0.013840831, 0.5019608) in all three frames, into the scene target wherever the depth is 0. Observed · Elden Ring · three frames
  2. Among the forward draws, one draw of the cloud shell M[Clouds9] (6,336 indices over 1,220 vertices; the same vertex and index bytes in all three frames), a dome ±43,104 m across and −8,000 to 18,076 m high in its own frame. Observed · Elden Ring · three frames

The shell lands exactly on the sky’s pixels: its draw changes every one of the 197,950 pixels at depth 0 and no other pixel of the scene target, and leaves the depth target as it was. Observed · Elden Ring · one frame, the targets read back before and after the draw It does so through its own copy of cbSceneParam, whose depth rows (register 18, FC_MatrixProj’s, and register 26, VC_MatrixViewProj’s) are 0; every other register of its 2,016 bytes equals the next forward draw’s. Its vertex program writes SV_Position = VC_MatrixViewProj × (world − SC_CameraPos), so every vertex has depth 0 and passes the GreaterEqual test only where nothing was drawn. Among the 641 to 870 draws of each frame that bind cbSceneParam at b8, it is the only one with those rows 0. Observed · Elden Ring · three frames, and the vertex program’s DXIL The shell therefore needs no real distance and never hides scenery, whatever its size. Inferred · Elden Ring

None of these frames draws an M[Sky9] skysphere or another cloud layer, in the G-buffer or forward: the fill colour and the shell are the whole sky. Observed · Elden Ring · three frames After them the volumetric fog’s composite and the half-resolution fog volumes change almost every sky pixel (197,701 and 192,634 of the 197,950), then the half-resolution effects’ composite (SampleBilateralDepthBlurTwo3) and the temporal anti-aliasing, whose sharpened result (SharpenFilter) is written back; no other material (M[…]) draw writes them. Observed · Elden Ring · one frame, the scene target read back after each pass

The shell follows the camera across the ground and keeps its height. Its object matrix (cbObjMatrix[0], repeated as cbInstanceData[0].mWorld) is the identity rotation at (camera x, −16, camera z): its x and z equal SC_CameraPos‘s bit for bit in each frame, while the camera’s height (5.74, 6.61 and 6.99) leaves its y at −16. Observed · Elden Ring · three frames This is Sekiro’s sky-map placement (translation (camera x, 0, camera z) times the part’s own transform) with the part’s own height. Inferred · Elden Ring The shell is AEG096_603: the draw’s vertex range (53,680 bytes at offset 22,816 of a 76,544-byte buffer) and its index binding (offset 0, 76,496 bytes) are byte for byte that model’s first vertex buffer and its whole data block, which no other of the 82 sky binders holds; its 6,336 indices are its first face set, which two other sky models share. Observed[one frame, the same vertex and index bytes as the three above; the bound ranges’ keys against every sky FLVER’s]{games=“elden-ring”}

Which show depends on the weather: WeatherAssetReplaceParam (1,007 rows) lists, for a _99 map and a weather id, the assets drawn; every one of m60_00_00_99’s 38 sky assets is listed for some weather, and its 18 weathers list 1 to 27 of them (weather 0: the clear skysphere aeg096_010 with its clouds and the Erdtree; weather 99: aeg096_102 alone). Observed · Elden Ring A row (64 bytes, data version 2) is keyed by the sky map, the weather (a WeatherParam id), isFireAsh and four CreateAssetLimitIds, and lists up to 8 asset ids, AEGnnn_mmm being the number nnn * 1000 + mmm; no key repeats, and every asset a row names is placed by its sky map’s layout, over all 18 sky maps of the base game. Observed · Elden Ring · every row, and the 935 layouts of the base game isFireAsh is 0 or 1 in the base game’s rows and 2 only on the expansion’s maps (m20, m21, m28, m61). Observed · Elden Ring The code that applies the rows takes 2 as either: it skips a row whose isFireAsh (byte 8) is 0 or 1 and does not equal the game’s current ash state, a byte of a world-state object (+0xBCD; 0 when the object is absent). Observed · Elden Ring · read from the decrypted code of 2.7.2.0 A row with limits applies only on maps whose MapDefaultInfoParam row (its id the map’s 8-digit number) has a WeatherCreateAssetLimitId among them: the open world’s tiles hold 0 to 4, 21, 31, 41 and 42 in the base game and 50 to 75 in the expansion, the legacy dungeons −1; every row of m60 has limits and no row of a legacy map has. Observed · Elden Ring

The sky maps place more than the rows name: far-view land pieces (m90XXZZ), AEG004 and AEG299 props and the Erdtree AEG094, 594 parts in m60_00_00_99. Observed · Elden Ring m60_00_00_99’s origin is the centre of the level-2 tile cell (10, 8): a piece m90XXZZ stands at ((XX − 10) × 1024, 0, (ZZ − 8) × 1024) and its model is centred on its cell (±512), as the terrain of m60_10_08_02 is centred on its own; a level-0 tile (x, z) has its centre at ((x − 42) × 256 + 128, (z − 34) × 256 + 128) in that frame. Observed · Elden Ring A legacy dungeon’s sky map mAA_BB_00_99 shares its dungeon’s frame. Observed · Elden Ring WeatherAssetCreateParam (8 rows) spawns the rainbow between 7 and 16 h and the aurora between 21 and 3 h, by weather. Observed · Elden Ring Its rows: 0 and 1 the rainbow AEG096_200 (condition 0, on the change from weather 21 to 0 or to 1, 7 to 16 h), 1000 and 1001 the aurora AEG096_201 (condition 1, after an elapsed time, 21 to 3 h), and 7000 to 7003 AEG050_448 and _449 (condition 0, −2 to 82, a life time of 99,999). Rows of condition 0 set a transition and rows of condition 1 an elapsed time, so 0 is the transition condition and 1 the elapsed-time one. Inferred · Elden Ring Every row sets exactly one creation point and one limit (2, 4 or 70). Proven · Elden Ring · the 8 rows

WeatherAssetCreateParam row (WEATHER_ASSET_CREATE_PARAM_ST)64 bytesElden Ring20% known

Data version 1. The names are the community param definition's; the bytes it leaves as padding are zero in every row.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32AssetIdProven
0x44u32SlotNoUnknown
0x81u8CreateConditionTypeInferred0 on a weather transition, 1 after an elapsed time
0x93u8[3] = 0paddingProven
0xC2s16TransitionSrcWeatherSourced
0xE2s16TransitionDstWeatherSourced
0x102s16ElapsedTimeCheckWeatherSourced
0x122u8[2] = 0paddingProven
0x144f32ElapsedTimeSourced
0x184f32CreateDelayTimeSourced
0x1C4u32CreateProbabilitySourced
0x204f32LifeTimeSourced
0x244f32FadeTimeSourced
0x284f32EnableCreateTimeMinSourcedhours: 7 for the rainbow, 21 for the aurora
0x2C4f32EnableCreateTimeMaxSourcedhours: 16, 3
0x308s16[4]CreatePoint0..3Sourcedone set per row
0x384s8[4]CreateAssetLimitId0..3Sourcedone set per row: 2, 4 or 70
0x3C4u8[4] = 0paddingProven

The layout reads the base game’s 8 rows with every padding byte zero. Proven · Elden Ring · the 8 rows of the base game’s regulation AssetGeometryParam’s isSkydomeFlag (“treated as a celestial sphere: follows the player”, by its definition) is set on one asset only, AEG620_002, and no layout part names one of the 17 PartsDrawParam rows that set isSkydomeDrawPhase (935 layouts read). Observed · Elden Ring

In the scene constants cbSceneParam (2,016 bytes, b8 of the sky and light programs) FC_ShadowColor is at byte 544 and FC_ShadowDir at 560, a unit vector towards the sun in the drawing frame: it agrees within 0.01 with the negated forward axis of the frame’s orthographic shadow camera. Observed · Elden Ring · one frame at the Stormveil gate cbPerFrame (b9, 448 bytes, FC_GlobalTime at 432 and FC_TimeOfDay at 436) is bound from a buffer in GPU memory in every draw, not from an upload heap. Observed · Elden Ring

The deferred sun program’s own buffer repeats two of the scene’s matrices: cbLightAccParam’s g_InvProjMatrix (bytes 0 to 64) is byte for byte cbSceneParam’s FC_MatrixInvProj, and its g_CameraMatrix (bytes 128 to 192) is FC_MatrixInvView, so these are the inverse projection and the camera’s inverse view, not object matrices. Observed · Elden Ring · one frame at the Stormveil gate

The name g_ShadowTexture at t32 does not name one texture. GXLightAcc_DDirLightShadowed1 reads it with an ordinary sample through s8 at the pixel’s screen position: it is the screen-space shadow mask of pass 5. GXScreenSpaceCascadedShadow_PCF9 reads its t32 with a depth comparison (SampleCmpLevelZero) through s12 at shadow-atlas coordinates: there it is the cascade atlas. Observed · Elden Ring · the two installed programs’ DXIL, joined by their bytecode to three draws of the Stormveil frame

The sun program’s cbLight holds one constant, FC_DirLight: an array of two light sets, of which each pixel’s G-buffer selects one. A set’s members, in order, are the directions Vec_0 to Vec_3, the colours Col_0 to Col_3 and the specular colours Spc_0 to Spc_3 (four floats each), then bLocalLight (four unsigned integers) and FC_SsaoWeight: 224 bytes a set in Elden Ring. Sekiro’s sets put AmbientSH (48 bytes) and AmbientReflection between the specular colours and bLocalLight: 288 bytes a set. Observed · Elden Ring, Sekiro · the reflection of the installed GXLightAcc_DDirLightShadowed1 pixel program each recorded graph binds

A direction’s w is not part of the direction. In the frames recorded from the running game (nine frames of three sessions at the Ashina Castle gate), 243 of the 248 draws whose pixel program reads one light set at b5 bind a set whose Vec_0.w is 1, with Vec_1.w to Vec_3.w at 0.12 or 0.15 or 1. The other 5 bind a set of four equal lights of colour 1.5 whose every w is 50. Observed · Sekiro · nine recorded frames Elden Ring’s forward lake program (M[PlacidLake]_FwdSdw) takes the sun’s highlight with a smoothness of sqrt(smoothness * (1 - 0.075 / Vec_0.w)), so a larger w gives a sharper highlight and a w of 0 none that is a number. Observed · Elden Ring · the installed program’s DXIL Elden Ring’s Vec_0.w is the light set’s Sharpness0 field, and Vec_1.w its Sharpness1: in frames at the Stormveil gate the sun program’s cbLight (a 50,432-byte table of 225 sets) holds them equal to the stored values at the clock hour (light sets of a frame). Observed · Elden Ring · captures m14 to m17

In the recorded graphs GXLightDeferredAOWithSsr draws the two light targets first, and the sun program blends onto them; it reads the environment-map regions’ targets, the reflections and the shadow mask. The sun program reads the shadow mask that GXRend_CompositeShadowMask writes. Elden Ring’s GXRend_TileBasedLighting reads that mask too, after the mask step has read the ambient occlusion; Sekiro’s tiled program reads the ambient occlusion straight from ScreenSpaceAO2_BilateralUpScale. GXSssBlurX and Y redraw GXLightCmbAlbedo’s target before GXLightComposite reads it. Observed · Elden Ring, Sekiro · the recorded graphs of m11_01 (nine frames) and of Elden Ring (five frames)

The tiled point and spot lights are drawn by GXRend_TileBasedLighting in Elden Ring too. Each tile’s 12-byte record from GXLightOrganize8 holds its light counts in the third word, point lights in the low 16 bits and spot lights in the high 16: the program adds the two and discards the pixel when the sum is 0, before it walks the tile’s lights. Observed[the installed program’s DXIL]{games=“elden-ring”} Sekiro’s tiled program reads that word only in a debug branch. Observed · Sekiro · the program’s code

Stencil bit 0x10 marks the pixels the scene’s geometry covers. In the recorded Stormveil frame all 479 G-buffer draws (92 pipeline states, recognised by their four targets’ formats) set a stencil reference with bit 0x10: 16, 18, 50, 80, 88, 144, 210 or 242; 473 of them replace the stencil under write mask 255 and 6 keep it. GXRend_TileBasedLighting and GXLightComposite test the stencil with mask 0x10 against reference 16, and GXLightCmbAlbedo draws with reference 255. These three draws have independent blending off, so target 0’s blend state applies to every target they write. Observed · Elden Ring · one frame In Elden Ring and Sekiro the reference follows the kind of object: 88 on skin (the draws the subsurface blur selects, by the bit 0x8), 80 on the player’s other parts, and in the Stormveil frame 144 on the other character and 16 to 242 on the map (see the light-set lane). Observed · Elden Ring, Sekiro · one frame of each game Which material or draw data chooses a draw’s reference is not established. Unknown · Elden Ring

Stormveil’s environment packages are m10_00_00_00_envmap_<state>_<tier>_00.tpfbnd.dcx, seven states (00 to 06) in three tiers (high, middle, low). Each of the 21 holds 640 TPF members of one texture each: 512 GIIV coefficient volumes and 128 GILM reflection cubes, 13,440 textures in all. The volumes are BC7_UNORM true 3-D textures of one level, their size depending on the group (group 0000: 60 × 44 × 60 in high, 28 × 20 × 28 in middle, 12 × 8 × 12 in low); the cubes are BC6H_UF16 with six faces and five levels, 128 × 128 in high and 64 × 64 in middle and low. Within a tier the seven states agree in their members’ names, kinds, sizes, levels and formats. Observed · Elden Ring · the 21 Stormveil packages, read from the installed archives

Elden Ring blends two states in its region draws, where Sekiro’s draw one package’s. Observed · Elden Ring · one frame In a second recorded Stormveil frame, not the one of the pass table:

Program Draws Reads Confidence
GXEnvMapRegion[Box][DiffSpc][Lerp] 47 the cubes g_IBLTexture (t25) and g_IBLTexture2 (t26), and two X, Y, Z, W volume groups at t27 to t34 Observed · Elden Ring
GXEnvMapRegion[Box][DiffSpc][LerpDiff] 9 the same eight volumes, and one working cube at t25 Observed · Elden Ring
GXEnvMapRegion_CopyCubeToCubeLerp (compute) 9 dispatches two source cubes at t0 and t1, at eight levels, writing u0 to u7 Observed · Elden Ring

That the programs read these slots is traced in their DXIL, from each resource handle to its sample, not taken from the reflection. Observed · Elden Ring · the installed programs’ DXIL The frame has 56 such sampled region draws and 6 stencil draws. The source cubes are BC6H_UF16, 128 × 128, six faces, five levels; the working cube R11G11B10_FLOAT, 128 × 128, six faces, eight levels; the coefficient volumes BC7_UNORM 3-D with one level. Observed · Elden Ring · one frame Each [LerpDiff] draw reads the working cube the dispatch before it has just rewritten. Observed · Elden Ring · one frame

The two region programs shade alike apart from the cube. With [Lerp]’s two cube samples and their blend replaced by one sample, and [LerpDiff]‘s working-cube sample by the same, the programs keep the same resource registers, constants, dependencies, branches and output stores, the only other difference two operands of one multiplication in swapped order. Observed · Elden Ring · the two installed programs’ DXIL, compared in SSA form [LerpDiff] is therefore [Lerp] with the blend of the two states’ cubes done ahead of it by the compute dispatch. Inferred

Both read the cubes’ highest level from g_envMapMipmapLevelNum0 and 1 (b3 bytes 496 and 500), and all 56 draws hold (4, 4) there: the source cubes’ five levels counted from 0, not their number. Observed · Elden Ring · one frame The [LerpDiff] draws hold 4 too, so they read only the first five of the working cube’s eight levels. Observed · Elden Ring · one frame What a [LerpDiff] region adds then differs from sampling and blending the two source cubes directly only by the working cube’s R11G11B10 storage and its levels. Inferred

[Lerp] also scales the cube’s contribution by g_ivShLuminance0 and 1; its diffuse output from the coefficient volumes does not depend on them. Observed · Elden Ring · the program’s DXIL Four of the frame’s values of these constants are non-zero, and none of them is stored, byte for byte, in any of the 129 environment boxes of Stormveil’s layout or in 194 param tables read from the install, nor equals a decoded volume’s average. Observed[one frame, the installed layout and params]{games=“elden-ring”} Where the game takes them from is not established. Unknown · Elden Ring

The two states are 05 and 06 of the high tier: by the fingerprints of the uploads, 55 of the 56 draws read those packages’ textures, and 548 of the 550 uploaded inputs are identified uniquely; the other two are GILM0800 cubes whose bytes are the same in states 00, 03, 04, 05 and 06, so a cube alone does not tell the states apart, while the volumes do. All 56 draws’ b3 g_envMapInterpRatio and all 9 dispatches’ blend rate are the same f32, 0x3EB4E81C (0.35333335). One draw and its 10 inputs match none of the 21 packages. Observed · Elden Ring · one frame; the uploads fingerprinted, not compared byte for byte

The same frame’s region matrices (b3) are 55 boxes of Stormveil’s layout, each within 0.000122 in every one of the 16 floats. The box’s basis is Ry × Rz × Rx of its rotation, scaled by half its width, height and depth; its centre is its position plus the box’s local half-height offset turned by that rotation, then the map’s translation into the drawing frame, (88, −16, −16). The pitched Env_Box253 misses by about 3.165 if the offset is not turned. Observed · Elden Ring · 55 boxes of one frame

The box’s type data at +0x34 is not its probe group: Env_Box002 stores 0 there but binds GIIV and GILM group 0002, and Env_Box902 binds group 0901, so the box’s name alone is not the group either. Observed · Elden Ring · 55 boxes of one frame The signed 16-bit word at +0x0A of the type data overrides the group: where it is -1 the box binds the group of its own number, the digits of its name Env_Box<n>, and otherwise the group the word gives (Env_Box902 stores 901). The rule names the group of all 55 captured boxes, and over all 129 boxes of Stormveil’s layout it names 128 groups, every one present in the area’s packages. Observed · Elden Ring · 55 boxes of one frame; the 129 installed boxes No numeric field of the box’s record, its shape, type data or entity data holds the number of the name on every box (the best on 6 of 129), so the default comes from the name itself. Observed · Elden Ring · the 129 installed boxes That the game resolves the group this way on every map is not traced in its code. Inferred · Elden Ring

Byte +0x1A of MapDefaultInfoParam row 10000000 (Stormveil) is 1, and of row 10010000 also 1. Observed · Elden Ring The community param definition names it IsEnableBlendTimezoneEnvmap, blending environment maps by time of day on the legacy maps. Sourced · Elden Ring · Paramdex It enables blending; it does not give the schedule from hour to state. The nine m10_00 GPARAM files (0000 to 0600, 0800, 0900) have time axes offset to 6.5 h with period 24, an empty EnvironmentConfig group and no Env Map:Editor group, so they hold no state schedule either. Observed · Elden Ring · the nine files, read from the archives

The ratio is a function of the hour. In the four frames whose cbPerFrame was recorded (FC_TimeOfDay 8.0744, 8.1172, 8.1267 and 8.1294), the g_envMapInterpRatio of every region draw and the lerpRate of every [Lerp] dispatch is (FC_TimeOfDay − 6.5) / 4 (0.39361, 0.40424, 0.40660 and 0.40736), to within 7e-5. Observed · Elden Ring · 4 frames It is the same in a frame’s draws and dispatches except that one region draw or dispatch of three of the nine frames reads a value 7e-5 lower, so the game writes it again during the frame. Observed · Elden Ring · 9 frames In the five frames without cbPerFrame the same formula gives hours of 7.92 to 8.03 from their ratios (0.3553 to 0.3829). Inferred · Elden Ring The 6.5 is the offset of the file axis of 98 of the 102 draw-parameter files drawn at the gate (looking up an hour), so the blend runs from 0 to 1 over the 4 hours after that axis’s start; whether the pair of states changes at its end is not in any frame. Inferred · Elden Ring

The pair stays the same while the ratio moves. The textures bound to the region programs’ environment slots (t25 to t34) are the same contents in the three frames with ratios 0.3553 to 0.3829 (542 or 543 of 543) and in the three with 0.4042 to 0.4074 (518 of 519), and 480 of them are in both groups; a ratio rising by 0.05 across them changes no cube or volume. Observed · Elden Ring · 7 frames with first-use contents, the cubes and volumes’ content keys The two states are therefore held while the ratio rises over this hour. Inferred · Elden Ring

What chooses Elden Ring’s environment states and the blend?

The ratio at the gate follows (hour − 6.5) / 4 (above). The code that picks the two states for an hour (which pair outside 6.5 to 10.5 h, and where the ratio starts again), the rule that picks a tier on PC and where the formula’s 6.5 and 4 come from are not established: all ten frames are within 7.9 to 8.2 h in one area. The probe-group rule above fits Stormveil but has not been followed in the executable or checked on another map.

The overworld far tiles (m60_<x>_<z>_02, and the _12 alternates) have environment packages whose probe groups no box of the tile’s layout names: groups 0900 to 0959, each GIIV volume 84 × 84 × 84 in high, with a GILM cube beside it. 48 far tiles have such a package, all of them m60: 46 _02 tiles, of which 32 hold one group and 14 hold two (the second numbered 0946 to 0959), and m60_11_09_12 (one group, 0920) and m60_11_13_12 (two, 0924 and 0953, as m60_11_13_02). Observed[every m60 and m61 far tile name, _02 and _12, state 05 high, read from the installed archives]{games=“elden-ring”} The Shadow of the Erdtree far tiles (m61) have none in high: m61_10_10_02’s packages, for one, come in the middle tier only (states 00 to 06). Read from the best tier each far tile installs, 67 far tiles have packages, 47 with one group, 19 with two and one with four, and 10 have none. The 19 more than at high include m61_10_10_02, m61_10_11_02, m61_10_12_02 and m61_12_10_02 with two groups (0900 to 0905 under 0916 to 0922) and m61_13_10_02 with four (0910, 0918, 0921 and 0922). Observed · Elden Ring · every m60 and m61 far tile name, at the best tier each installs No recorded frame draws one of them. Observed[the recorded Elden Ring frames]{games=“elden-ring”}

Their volumes line up with the ground. Decoded, a W coefficient is dark in texels inside the terrain and bright in open air. Against the highest collision surface of the far tile’s 16 small tiles and the ring around them, the volume separates buried from open texels (those more than 1.5 rows from the surface) best when its columns run along +X, its slices along +Z and its rows up +Y, centred on the far tile’s origin and spanning the tile’s 1,024 m (12.19 m texels). Spans from 1,021 to 1,033 m score within 0.0001 of the best, so the volume spans the tile edge to edge. Observed · Elden Ring · four far tiles with relief: m60_08_10, 08_11, 09_10 and 11_09

The vertical placement is known less well. With one bottom and row height shared by every tile, the second group’s rows stacked on the first’s, the mean area under the ROC curve over twelve tiles is 0.971 at rows of 13.25 m from 105 m below the origin. Bottoms from -120 to -80 m with rows of 12.75 to 13.625 m score within 0.001 of that, while flipping X or Z, spans 2.5 % narrower or wider, a shift of two texels, the bottom 40 m higher or lower, rows 10 % shorter or taller, or cubic texels all score worse. Observed[twelve far tiles, eight with two groups: m60_08_10, 08_11, 09_10, 11_09, 10_12, 10_13, 11_12, 11_13, 12_12, 12_13, 13_13 and 13_14]{games=“elden-ring”} The tiles disagree within that range: the six one-group tiles checked, taken alone, separate best with rows of 14 m from -75 m (pooled, 0.996), and the lower group of a two-group tile separates worst (0.81 to 0.99 per tile). Observed[the same twelve tiles, and m60_10_14 and 10_15]{games=“elden-ring”} The lower groups sit under high ground, where cliffs and overhangs make a highest-surface height field a poor label of buried and open, which would explain their scores without a different placement. Inferred · Elden Ring

Where a tile has two groups, the second continues the first directly above it. Stacked with the lower-numbered group at the bottom, the two volumes separate buried from open texels at 0.902 to 0.997, and swapped at 0.079 to 0.699, on each of the 14 _02 tiles, whose terrain reaches 22 to 1,969 m. Letting the upper group take a bottom and row height of its own gains 0.0008 and moves its bottom 38 m down, within the uncertainty of the stack. Observed · Elden Ring · all 14 _02 far tiles with two groups, at the shared placement above The m61 tiles stack the same way. On the four with two groups, the lower-numbered group at the bottom separates at 0.73 to 0.995 and swapped at 0.36 to 0.46; on m61_13_10_02, 0910 under 0918 separates at 0.984 and swapped at 0.779, while swapping 0918 with 0921, or 0921 with 0922, leaves 0.984 unchanged. Observed · Elden Ring · the five m61 far tiles with more than one group, at the shared placement above The upper three of that tile lie wholly above its ground, where the measure has no buried texels to rank, so it cannot tell their order. Inferred · Elden Ring

None of the ten recorded frames holds a far tile’s volume: of the BC7 3-D textures of each frame’s resource list, none is 84 on all three sides, and the volumes the region draws bind are boxes of the area, at most 132 × 112 × 64. Observed · Elden Ring · the resources of 10 frames at the Stormveil gate A frame at the gate does not draw them, so the captures cannot show how a far tile’s groups are placed or drawn. Observed · Elden Ring

How does the game place a far tile’s environment groups?

The boxes above are fitted to the volumes’ contents; the code that places these groups, whether the height range is one constant or depends on the tile, and whether they are drawn with the region programs, a fade, colour scales or a priority, is not traced. Which state blend and tier a far tile uses is the open question above. A frame that draws a far tile (at a place on the open world’s tiles, not the Stormveil gate) is missing.

The glow pass (15 above) spreads the effects’ second target. Every effect draw whose node blooms writes a 2560 × 1440 R11G11B10_FLOAT target beside the colour (effect shader programs), and after the effects four programs turn it into light the tone map adds: Observed[18 frames of 4 sessions at 2560 × 1440, the programs translated and their constants read]{games=“elden-ring”}

Step Program What it does Constants
1 EmissiveToGlowPS to 1280 × 720: one bilinear tap at each texel’s centre (the mean of the four under it), colour times alpha (1, the target having none); a texel with a NaN channel is 0 none
2 Sample13Weight, 7 passes halves each level, the size rounded up, down to 10 × 6 (640 × 360, 320 × 180, 160 × 90, 80 × 45, 40 × 23, 20 × 12, 10 × 6): 13 bilinear taps, each offset in texels of the level written, times its weight g_avSampleOffsets below, the same in every pass
3 GlowCompositeSinglePS, 6 passes from 10 × 6 up to 640 × 360, each level adds its bilinear upsample of the level below times g_GlowScaleOffset.x (blend one + one) 1.2
4 GlowCompositeSinglePS, last pass the 1280 × 720 level is replaced (no blend) by the 640 × 360 level’s upsample times g_GlowScaleOffset.x 0.38711962
5 ToneMap_BloomCompositeSinglePS adds the 1280 × 720 level to the glare buffer times g_vBloomFinalColor, which is the scene exposure g_ToneMapSceneLumScale in this draw (9.49 to 17.93 in these frames, equal in each) and 0.05 in the draw that adds the bright-pass bloom

Each tap is at uv + offset × g_vScreenSpaceTexCoordScale, the reciprocal of the written level’s size, clamped half a texel of the level read inside its edge. Observed · Elden Ring · the program’s code The 13 taps, their weights summing to 1: Observed · Elden Ring · cbSampleOffset of every Sample13Weight draw in 18 frames

Taps Offset (texels of the level written) Weight
1 (0, 0) 0.09818182
2 (±0.5, 0.707106) 0.06747935
2 (±1, 0) 0.059550285
2 (±0.5, −0.7071006) 0.06747961
4 (±0.5, ±0.23570226) 0.08427146
2 (0, ±0.47140452) 0.08785695

The constants 1.2 and 0.38711962 are the same in all 18. Observed · Elden Ring Every level is R11G11B10_FLOAT like the glow target, each store rounding toward zero. Run as described on the glow target read back from a captured frame, with the levels in that format, steps 1 and 2 give the game’s half-size level and all seven halvings bit for bit; each climbing step, run on the game’s own levels, is exact in the median texel and off by at most one R11G11B10 step; and the whole chain gives the game’s 1280 × 720 result exact in the median texel, within 2.5 % at the 95th percentile and 99.8 % of its total. Observed[one frame’s targets, reproduced pass by pass by an independent implementation]{games=“elden-ring”} The same chain with half-float levels comes out 6 % brighter in red and green and 12 % in blue: the format’s rounding is part of the result. Observed · Elden Ring · the same frame

0.38711962 is 5 / (1 + 1.2 + … + 1.2⁶), so a glow target of one even value v comes out as 5 v, the largest levels weighing most (the 10 × 6 level 1.156 of it, the 640 × 360 level 0.387). Inferred · Elden Ring · the arithmetic fits to 8 digits ToneMap_TabledToneMapLUTPS takes the scene times the scene exposure plus the glare buffer times g_GlareLuminance.x (1), so the glow lands in the scene’s own light units, added after exposure is metered: the meter (ToneMap_DownSample2x2PS and the luminance passes) and the bright pass read the scene target without it. Observed[the programs’ code and bindings in one frame; the factor equal to the scene exposure in all 18]{games=“elden-ring”}

Elden Ring’s exposure meter and the scale the CPU derives from it are on exposure, tone curve and colour grading.

In nine more recorded frames at the gate (the camera at five places, its height 5.74 to 8.19 m, looking from 20° below the horizon to 40° above it, at about 8 h) the shell’s object matrix is again the identity rotation with x and z equal to SC_CameraPos’s bit for bit and y −16. Observed · Elden Ring · 9 frames; the tenth holds no constants Of the 1,872 to 2,768 draws of each frame that bind both cbObjMatrix and cbSceneParam, the shell is the only one whose matrix sits at the camera’s x and z, and in every frame exactly one of the scene-constant contents bound has registers 18 and 26 zero. Observed · Elden Ring · 9 frames None of the ten frames, the three that look up at the sky included, draws an M[Sky9] skysphere or another cloud layer: the fill and the shell are the whole sky at this hour and place. Observed[10 frames, the programs of every draw named]{games=“elden-ring”}

Do Elden Ring’s other sky assets follow the camera?

The cloud shell M[Clouds9] follows the camera at a fixed height (above), in every frame that holds constants. Whether the skysphere M[Sky9] and the cloud layers drawn in night frames are placed the same way and take the same depth-0 scene constants is not shown: no recorded frame draws one (all ten are at the Stormveil gate at about 8 h). How a weather’s list combines with the _99 map’s own placements is not established.

Sources

  • Paramdex — the field names and types of WEATHER_ASSET_CREATE_PARAM_ST, and the name of MapDefaultInfoParam's IsEnableBlendTimezoneEnvmap
  • SoulsFormats — the names of the environment-map box's type-data words (UnkT08 and on)