cesiumgs/cesiumjs-skills/cesiumjs-custom-shader

Claude Code and agent skill by cesiumgs/cesiumjs-skills

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

CustomShader authoring — vertexShaderText and fragmentShaderText against VertexInput, FragmentInput, FeatureIds, Metadata, czm_modelMaterial. Use when reading EXT_mesh_features or EXT_structural_metadata property textures/tables, vertex displacement, or shading VoxelPrimitive.

Install

npx vskill@latest install cesiumgs/cesiumjs-skills/cesiumjs-custom-shader

SKILL.md

CesiumJS CustomShader

Version baseline: CesiumJS 1.144. All imports use ES module style.

CustomShader injects user GLSL into the Model / Cesium3DTileset / VoxelPrimitive rendering pipeline. It exposes glTF attributes, feature IDs, and EXT_structural_metadata to per-vertex and per-fragment code, and returns values through the built-in czm_modelVertexOutput and czm_modelMaterial structs.

Use this skill for writing the shader body. Use:

  • cesiumjs-materials-shaders — for Fabric Material, ImageBasedLighting, PostProcessStage (bloom, SSAO, FXAA, tonemapping).
  • cesiumjs-3d-tiles — for declarative per-feature coloring via Cesium3DTileStyle, and for VoxelPrimitive setup/configuration.
  • cesiumjs-models-particles — for Model.fromGltfAsync, animations, ModelFeature.getProperty().

Out of scope

  • Fabric `Material` for entity polylines/polygons/walls — see cesiumjs-materials-shaders.
  • `PostProcessStage` screen-space effects — see cesiumjs-materials-shaders.
  • `ImageBasedLighting` — see cesiumjs-materials-shaders.
  • `Cesium3DTileStyle` declarative JSON styling — see cesiumjs-3d-tiles. Do not combine with CustomShader on the same tileset.
  • Authoring `EXT_structural_metadata` / `EXT_mesh_features` in glTF — tooling concern, not runtime.

Minimal example

js
import { CustomShader, Model } from "cesium";
const shader = new CustomShader({
fragmentShaderText: `
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
material.diffuse = vec3(1.0, 0.0, 0.0);
}
`,
});
const model = await Model.fromGltfAsync({ url: "./aircraft.glb", customShader: shader });
viewer.scene.primitives.add(model);
Note: Writing material.alpha requires translucencyMode: CustomShaderTranslucencyMode.TRANSLUCENT — see "Translucency" below. On opaque models with the default INHERIT mode, alpha writes are silently ignored.
Visual eval framing: after adding a shadered Model, wait until the model
is ready enough to render, then frame a known target point explicitly. Do not
rely on the first post-load frame or on a tiny default-scale model. For public
model evals, set minimumPixelSize (usually 256-400), use a moderate scale,
and add a silhouette or strong color tint when the shader effect is the thing
being judged.

Applying a CustomShader

Model — constructor option or mutable property:

js
const model = await Model.fromGltfAsync({ url, customShader });
model.customShader = newShader; // hot-swap
model.customShader = undefined; // clear

Cesium3DTileset — constructor option or mutable property. Only Model-backed tile content is affected (not native I3S or other formats):

js
const tileset = await Cesium3DTileset.fromUrl(url, { customShader });
tileset.customShader = newShader;
Per the Cesium3DTileset.customShader JSDoc: "Using custom shaders with a `Cesium3DTileStyle` may lead to undefined behavior." The property is also marked @experimental — it uses 3D Tiles spec surface that is not final and may change without Cesium's standard deprecation policy.

VoxelPrimitive — fragment-only subset (see "VoxelPrimitive shader subset" below):

js
const voxelPrimitive = new VoxelPrimitive({ provider, customShader });

The engine calls customShader.update(frameState) automatically each frame. When finished with a CustomShader, call customShader.destroy() to release GPU texture resources owned by its TextureManager.

Constructor reference

js
new CustomShader({
mode, // CustomShaderMode — default MODIFY_MATERIAL
lightingModel, // LightingModel — if omitted, model's default is preserved
translucencyMode, // CustomShaderTranslucencyMode — default INHERIT
uniforms, // { [name]: { type: UniformType, value } } — default {}
varyings, // { [name]: VaryingType } — default {}
vertexShaderText, // string or undefined
fragmentShaderText, // string or undefined
});

Either vertexShaderText or fragmentShaderText is typically required. See REFERENCE.md for exhaustive enum values.

Shader function signatures

The runtime calls these from generated pipeline stages. Parameter names are part of the contract — renaming them breaks the shader.

glsl
void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput) { ... }
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) { ... }

Uniforms

Declare uniforms with { type, value }. The type is a UniformType value; the JS value type must match (e.g. VEC3 → Cartesian3). Uniforms are accessible in GLSL by their declared name.

js
import { CustomShader, UniformType, TextureUniform, Cartesian3 } from "cesium";
const shader = new CustomShader({
uniforms: {
u_tint: { type: UniformType.VEC3, value: new Cartesian3(1.0, 0.5, 0.2) },
u_time: { type: UniformType.FLOAT, value: 0.0 },
u_detail: { type: UniformType.SAMPLER_2D, value: new TextureUniform({ url: "./detail.png", repeat: true }) },
},
fragmentShaderText: `
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
vec3 d = texture(u_detail, fsInput.attributes.texCoord_0).rgb;
material.diffuse = mix(material.diffuse, u_tint, d.r + 0.1 * sin(u_time));
}
`,
});
// Update at runtime. For Cartesian/Matrix values, setUniform clones into existing storage.
shader.setUniform("u_time", performance.now() / 1000);

TextureUniform accepts either url (string or Resource) or typedArray + width + height — exactly one (constructor throws otherwise). Other options: repeat (default true), pixelFormat, pixelDatatype, minificationFilter, magnificationFilter, maximumAnisotropy.

`SAMPLER_CUBE` is declared on `UniformType` but rejected at construction — throws DeveloperError("CustomShader does not support samplerCube uniforms"). Only SAMPLER_2D is supported.

Varyings

Declared varyings are emitted as out <type> <name> in the vertex shader and in <type> <name> in the fragment shader. Write in vertex, read in fragment.

js
import { CustomShader, VaryingType } from "cesium";
const shader = new CustomShader({
varyings: { v_worldHeight: VaryingType.FLOAT },
vertexShaderText: `
void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput) {
v_worldHeight = vsInput.attributes.positionMC.z;
}
`,
fragmentShaderText: `
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
float t = clamp(v_worldHeight / 100.0, 0.0, 1.0);
material.diffuse = mix(vec3(0.2,0.4,0.8), vec3(1.0,0.8,0.2), t);
}
`,
});

VaryingType supports FLOAT, VEC2–VEC4, MAT2–MAT4. No INT/BOOL/SAMPLER variants.

Modes & lighting

`CustomShaderMode`:

  • MODIFY_MATERIAL (default) — runs after the material stage, before lighting. czm_modelMaterial is populated with PBR/texture results; the shader refines them.
  • REPLACE_MATERIAL — skips the material stage entirely. Shader sets every field procedurally. Cheaper when the source material is not needed.

`LightingModel`:

  • UNLIT — skip lighting; material.diffuse becomes the final color (alpha still applied). Flat-shaded.
  • PBR — physically-based with IBL when available.
  • Omitted — preserves the model's default lighting. Omit unless overriding intentionally.

Pair REPLACE_MATERIAL + UNLIT for pure procedural flat shading (no material sampling, no lighting).

Translucency

CustomShaderTranslucencyMode governs how alpha writes interact with the render pass:

  • INHERIT (default) — alpha is only honored if the source material is translucent.
  • OPAQUE — force opaque pass.
  • TRANSLUCENT — force translucent pass.

Pitfall: writing material.alpha on an opaque model with INHERIT silently does nothing. Set translucencyMode: CustomShaderTranslucencyMode.TRANSLUCENT to make alpha writes effective.

js
import { CustomShader, CustomShaderTranslucencyMode } from "cesium";
const shader = new CustomShader({
translucencyMode: CustomShaderTranslucencyMode.TRANSLUCENT,
fragmentShaderText: `
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
material.diffuse = vec3(0.2, 0.6, 1.0);
material.alpha = 0.5; // honored because translucencyMode is TRANSLUCENT
}
`,
});

See examples/04-translucent-override.js.

Attributes

vsInput.attributes and fsInput.attributes expose glTF vertex attributes. Names are case-sensitive and coordinate-space suffixes are required — the constructor rejects bare position/normal/tangent/bitangent.

Common fields (full table in REFERENCE.md):

  • positionMC — model coords, valid in VS and FS
  • positionWC — world (ECEF) coords, fragment only, low-precision
  • positionEC — eye coords, fragment only
  • normalMC / normalEC — vertex / fragment
  • tangentMC / tangentEC, bitangentMC / bitangentEC
  • texCoord_N, color_N, joints_N, weights_N
Coordinate-space validation. The constructor scans shader text and throws DeveloperError("<name> is not available in the <stage> shader. Did you mean <alt> instead?") for invalid combinations. Examples: positionEC in vertex, normalMC in fragment.

Custom underscore-prefixed glTF attributes (_FEATURE_ID_0, _SURFACE_TEMP) are lowercased and un-prefixed: fsInput.attributes.surface_temp.

FeatureIds

vsInput.featureIds / fsInput.featureIds unify three glTF sources into one struct:

  • featureId_N — feature ID attributes and implicit attributes from EXT_mesh_features (N is the array index in the primitive's featureIds array). Also covers feature ID textures, which are fragment-shader-only.
  • instanceFeatureId_N — per-instance feature IDs from EXT_instance_features + EXT_mesh_gpu_instancing.
  • Named aliases — if glTF specifies "label": "perVertex", then featureIds.perVertex is also available.
  • Legacy 3D Tiles 1.0 BATCH_ID / _BATCHID → transparently renamed to featureId_0.

GLSL type is always int. WebGL 1 loses precision above 2^24.

glsl
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
int id = fsInput.featureIds.featureId_0;
if (id == 0) material.diffuse = vec3(1.0, 0.2, 0.2); // roof
else if (id == 1) material.diffuse = vec3(0.2, 0.8, 0.2); // wall
}

See examples/03-feature-id-tileset.js.

Metadata

EXT_structural_metadata surfaces three source types (all addressable from shaders as of 1.139):

  • Property attributes — per-vertex. Vertex and fragment shaders.
  • Property textures — per-texel. Fragment only.
  • Property tables — per-feature, keyed by feature ID. Added in 1.139 (#13124).
glsl
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
float t = fsInput.metadata.temperature;
float tMin = fsInput.metadataStatistics.temperature.minValue;
float tMax = fsInput.metadataStatistics.temperature.maxValue;
float v = (t - tMin) / (tMax - tMin);
material.diffuse = vec3(v, 0.0, 1.0 - v);
}

Property ID sanitization (GLSL identifier rules):

  • Non-alphanumeric runs → single _ (temperature ℃ → temperature_)
  • Leading gl_ stripped (gl_custom → custom)
  • Leading digit prefixed with _ (12345 → _12345)
  • Post-sanitization collisions → undefined behavior

Sibling structs: metadataClass.<prop>.noData | defaultValue | minValue | maxValue (class-schema bounds) and metadataStatistics.<prop>.minValue | maxValue | mean | median | standardDeviation | variance | sum (populated only when tileset.json declares statistics).

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
44
Repository forks
7
7-day trend score
19

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 20 May 2026, 5 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/cesiumgs/cesiumjs-skills/cesiumjs-custom-shader/badge)](https://verified-skill.com/skills/cesiumgs/cesiumjs-skills/cesiumjs-custom-shader)

Something wrong with this skill or its page? Tell us.