feat(dashboard): the game's own shaders in the world and property 3D views

game-shaders.js ports the client's techniques to one ShaderMaterial
program per family and variant: LEGOPPLighting (hemisphere lit sun,
ambient, fresnel rim, N.H^320 specular, the default reflection cube;
decal and non-decal textures, emissive, super emissive, glow, grayscale,
no ambient, AnimUV), BasicShaders and AlphaAsAlpha (lit or unlit, both
sides, alpha blend, alpha test, additive, animated alpha, two layers),
Metallic.fx polished metal and brushed steel with the client's metal
cubes, clear plastic, the Distortion (Ocean) layers, Flat Surf,
BrickWater, darklings, terrain meshes, flairs and the sky. The maths runs
on sRGB values as Direct3D 9 did; lights, specular, hemisphere and fog
blend between scenes; textures move by the .nif's texture transforms.
Programs are shared by every material with the same defines.

A Fog switch (off by default) adds the zone's fog to scenery and
terrain. Shaders the game doesn't draw in the world (footprints, drop
shadows, post-processing) are left out. Docs list the families, their
gameValues and what's approximated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-28 13:38:47 -05:00
parent e1d36f7dbe
commit d817697ab2
7 changed files with 649 additions and 124 deletions

View File

@@ -305,9 +305,11 @@ export const TERRAIN_LOOKS = { textured: 0, colorMap: 1, blendMap: 2, scenes: 3
const TERRAIN_VERTEX = `
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vWorld;
void main() {
vUv = uv;
vNormal = normal;
vWorld = (modelMatrix * vec4(position, 1.0)).xyz;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}`;
const TERRAIN_FRAGMENT = `
@@ -316,6 +318,10 @@ uniform vec3 lightDirection;
// The zone's lights as the game's TerrainDiffuse.fx gets them (scenery.js gameLights), when gameLightOn
uniform vec3 gameLightColor, gameAmbient, gameLightVec;
uniform float gameLightOn;
// ... and its fog, when the view turns it on (game-shaders.js)
uniform vec3 gameFogColor;
uniform float gameFogNear, gameFogFar, gameFogOn;
varying vec3 vWorld;
uniform int look;
uniform float sceneSize;
varying vec2 vUv;
@@ -342,7 +348,9 @@ void main() {
if (gameLightOn > 0.5) {
// TiledDetailDiffuse_4_PS: textures * diffuse map * 2 * (sun * N.L + ambient, clamped as a vertex color), * blend alpha
vec3 lit = clamp(gameLightColor * max(0.0, dot(normalize(vNormal), gameLightVec)) + gameAmbient, 0.0, 1.0);
gl_FragColor = vec4(min(color.rgb * tint * lit * blend.a, 1.0), 1.0);
vec3 terrain = min(color.rgb * tint * lit * blend.a, 1.0);
float fog = gameFogOn * clamp((length(vWorld - cameraPosition) - gameFogNear) / max(1.0, gameFogFar - gameFogNear), 0.0, 1.0);
gl_FragColor = vec4(mix(terrain, gameFogColor, fog), 1.0);
return;
}
gl_FragColor = vec4(min(color.rgb * tint, 1.0) * light * blend.a, 1.0);
@@ -403,7 +411,10 @@ export function buildTerrainChunks(data, loadTexture, lightDirection, gameLights
gameLightColor: gameLights ? gameLights.gameLightColor : { value: new THREE.Vector3() },
gameAmbient: gameLights ? gameLights.gameAmbient : { value: new THREE.Vector3() },
gameLightVec: gameLights ? gameLights.gameLightVec : { value: new THREE.Vector3(0, 1, 0) },
gameLightOn: gameLights ? gameLights.gameLightOn : { value: 0 }
gameLightOn: gameLights ? gameLights.gameLightOn : { value: 0 },
gameFogColor: gameLights ? gameLights.gameFogColor : { value: new THREE.Vector3(1, 1, 1) },
gameFogNear: gameLights ? gameLights.gameFogNear : { value: 0 }, gameFogFar: gameLights ? gameLights.gameFogFar : { value: 0 },
gameFogOn: gameLights ? gameLights.gameFogOn : { value: 0 }
}
});
const mesh = new THREE.Mesh(geometry, material);