three.js does not auto-declare uniforms added via onBeforeCompile, so the injected uMapRepeat/uMapOffset were undeclared and the card shader failed to compile, rendering cards transparent. Prepend the declarations to the vertex shader.
51 lines
1.9 KiB
TypeScript
51 lines
1.9 KiB
TypeScript
import * as THREE from 'three';
|
|
|
|
/**
|
|
* Per-card UV transform injected into a `MeshStandardMaterial` shader.
|
|
*
|
|
* Cards share one texture (the deck's sprite sheet, cached by drei) and one
|
|
* geometry, but each card samples a different sprite cell. Rather than cloning
|
|
* the texture per card (which re-uploads the sheet on every GPU bind), the
|
|
* repeat/offset is pushed into the material as a uniform. The shader source is
|
|
* identical across cards, so three.js still compiles a single shared program.
|
|
*
|
|
* We inject our own uniform instead of setting `texture.repeat`/`offset`
|
|
* because three r185 derives the map UVs from a `mapTransform` matrix that is
|
|
* refreshed from `map.matrix` every frame, overwriting any per-material
|
|
* transform we set on the shared texture.
|
|
*/
|
|
// Uniforms added via `onBeforeCompile` are not auto-declared by three.js, so
|
|
// they must be declared in the GLSL explicitly (the built-in `mapTransform` is
|
|
// declared in `uv_pars_vertex.glsl.js`).
|
|
const UNIFORM_DECLS = /* glsl */ `
|
|
uniform vec2 uMapRepeat;
|
|
uniform vec2 uMapOffset;
|
|
`;
|
|
|
|
const VERTEX_INJECT = /* glsl */ `
|
|
#include <uv_vertex>
|
|
vMapUv = uv * uMapRepeat + uMapOffset;
|
|
`;
|
|
|
|
/**
|
|
* Apply a repeat/offset to a card material's map sampling. Call once per
|
|
* material (the transform is baked into the shader). `repeat`/`offset` are
|
|
* copied, so the caller may reuse the vectors.
|
|
*/
|
|
export function applyMapTransform(
|
|
material: THREE.MeshStandardMaterial,
|
|
repeat: THREE.Vector2,
|
|
offset: THREE.Vector2,
|
|
): void {
|
|
// Clone eagerly so later mutation of the caller's vectors can't leak into
|
|
// the uniform once the material is compiled.
|
|
const r = repeat.clone();
|
|
const o = offset.clone();
|
|
material.onBeforeCompile = (shader) => {
|
|
shader.uniforms.uMapRepeat = { value: r };
|
|
shader.uniforms.uMapOffset = { value: o };
|
|
shader.vertexShader =
|
|
UNIFORM_DECLS +
|
|
shader.vertexShader.replace('#include <uv_vertex>', VERTEX_INJECT);
|
|
};
|
|
} |