mcpField
C
cesiumjs-custom-shader

cesiumjs-custom-shader

github.com/cesiumgs开发193209 次安装6 周前更新
代码仓库

关于 cesiumjs-custom-shader

面向 CesiumJS 开发者的 CustomShader GLSL 技能,可用于 Model、Cesium3DTileset 和 VoxelPrimitive 渲染,支持通过着色器控制几何体、要素 ID 与元数据。

主要功能

  1. 01顶点与片元着色器入口
  2. 02Uniform 与 varying
  3. 03材质和光照模式
  4. 04glTF 属性与要素 ID
  5. 05结构化元数据访问
  6. 06用于模型和瓦片集的自定义着色器

适用场景

  1. 01用自定义 GLSL 为模型表面着色
  2. 02按要素 ID 为瓦片要素着色
  3. 03在 3D 瓦片上可视化元数据值
  4. 04为体素数据应用程序化着色

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/cesiumgs/cesiumjs-skills --skill cesiumjs-custom-shader

安装后在 Claude Code 中输入/cesiumjs-custom-shader确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

REFERENCE.md
examples/
_sandcastle-template.html
01-diffuse-tint.js
02-texture-swap.js
03-feature-id-tileset.js
04-translucent-override.js
05-vertex-displacement.js
06-metadata-ramp.js
07-voxel-shader.js
README.md

详细介绍

CesiumJS CustomShader

版本基线:CesiumJS 1.144。所有导入均采用 ES 模块风格。

CustomShader 会将用户编写的 GLSL 注入 Model / Cesium3DTileset / VoxelPrimitive 渲染管线。它会向逐顶点和逐片元代码公开 glTF 属性、要素 ID 和 EXT_structural_metadata,并通过内置的 czm_modelVertexOutput 和 czm_modelMaterial 结构体返回值。

使用此技能编写着色器主体。以下情况请使用:

  • cesiumjs-materials-shaders — 用于 Fabric Material、ImageBasedLighting、PostProcessStage(泛光、SSAO、FXAA、色调映射)。
  • cesiumjs-3d-tiles — 用于通过 Cesium3DTileStyle 声明式地为各要素着色,以及设置/配置 VoxelPrimitive。
  • cesiumjs-models-particles — 用于 Model.fromGltfAsync、动画、ModelFeature.getProperty()。

不适用范围

  • 用于实体折线/多边形/墙体的 Fabric Material — 请参阅 cesiumjs-materials-shaders。
  • PostProcessStage 屏幕空间效果 — 请参阅 cesiumjs-materials-shaders。
  • ImageBasedLighting — 请参阅 cesiumjs-materials-shaders。
  • Cesium3DTileStyle 声明式 JSON 样式 — 请参阅 cesiumjs-3d-tiles。不要在同一个 tileset 上同时使用它和 CustomShader。
  • 在 glTF 中编写 EXT_structural_metadata / EXT_mesh_features — 这属于工具链工作,而非运行时工作。

最小示例

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);

**注意:**写入 material.alpha 需要设置 translucencyMode: CustomShaderTranslucencyMode.TRANSLUCENT — 请参阅下文的“半透明”。对于使用默认 INHERIT 模式的不透明模型,alpha 写入会被静默忽略。

**视觉评估取景:**添加带着色器的 Model 后,请等待模型达到可渲染状态,再显式对准已知目标点。不要依赖加载后的第一帧,也不要使用默认比例很小的模型。对于公开模型评估,请设置 minimumPixelSize(通常为 256-400),使用适中的 scale,并在着色器效果是评判重点时添加轮廓或明显的颜色色调。

应用 CustomShader

Model — 构造函数选项或可变属性:

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

Cesium3DTileset — 构造函数选项或可变属性。仅影响由 Model 支持的瓦片内容(不影响原生 I3S 或其他格式):

const tileset = await Cesium3DTileset.fromUrl(url, { customShader });
tileset.customShader = newShader;

根据 Cesium3DTileset.customShader 的 JSDoc:“将自定义着色器与 Cesium3DTileStyle 一起使用可能会导致未定义行为。” 该属性还标记为 @experimental — 它使用了尚未定稿的 3D Tiles 规范部分,且可能会变更,不受 Cesium 标准弃用政策的约束。

VoxelPrimitive — 仅支持片元着色器的子集(见下文“VoxelPrimitive 着色器子集”):

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

引擎会在每一帧自动调用 customShader.update(frameState)。不再使用 CustomShader 时,请调用 customShader.destroy(),以释放其 TextureManager 所拥有的 GPU 纹理资源。

构造函数参考

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
});

通常需要设置 vertexShaderText 或 fragmentShaderText。完整枚举值请参阅 REFERENCE.md。

着色器函数签名

运行时会从生成的管线阶段调用这些函数。参数名称属于接口约定 — 重命名会导致着色器失效。

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

Uniform

使用 { type, value } 声明 uniform。类型是一个 UniformType 值;JS 值类型必须与之匹配(例如,VEC3 → Cartesian3)。在 GLSL 中,可通过声明的名称访问 uniform。

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 接受 url(字符串或 Resource),或 typedArray + width + height — 且只能二选一(否则构造函数会抛出异常)。其他选项:repeat(默认 true)、pixelFormat、pixelDatatype、minificationFilter、magnificationFilter、maximumAnisotropy。

UniformType 中声明了 SAMPLER_CUBE,但构造时会拒绝它 — 会抛出 DeveloperError("CustomShader does not support samplerCube uniforms")。仅支持 SAMPLER_2D。

变化量

声明的变化量在顶点着色器中输出为 out <type> <name>,在片元着色器中输入为 in <type> <name>。在顶点着色器中写入,在片元着色器中读取。

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 支持 FLOAT、VEC2–VEC4、MAT2–MAT4。不支持 INT/BOOL/SAMPLER 变体。

模式与光照

CustomShaderMode:

  • MODIFY_MATERIAL(默认)——在材质阶段之后、光照之前运行。czm_modelMaterial 中已填充 PBR/纹理结果;着色器会进一步细化这些结果。
  • REPLACE_MATERIAL ——完全跳过材质阶段。着色器以程序化方式设置所有字段。无需使用源材质时,这种方式开销更低。

LightingModel:

  • UNLIT ——跳过光照;material.diffuse 成为最终颜色(仍会应用 alpha)。采用平面着色。
  • PBR ——基于物理的渲染,在可用时使用 IBL。
  • 省略 ——保留模型的默认光照。除非有意覆盖,否则请省略。

将 REPLACE_MATERIAL 与 UNLIT 搭配使用,可实现纯程序化的平面着色(不采样材质,也不应用光照)。

透明度

CustomShaderTranslucencyMode 控制 alpha 写入与渲染通道的交互方式:

  • INHERIT(默认)——仅当源材质为半透明时,才会采用 alpha 值。
  • OPAQUE ——强制使用不透明通道。
  • TRANSLUCENT ——强制使用半透明通道。

**注意:**在 INHERIT 模式下,对不透明模型写入 material.alpha 不会生效,且不会报错。设置 translucencyMode: CustomShaderTranslucencyMode.TRANSLUCENT,使 alpha 写入生效。

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
    }
  `,
});

参见 examples/04-translucent-override.js。

属性

vsInput.attributes 和 fsInput.attributes 会公开 glTF 顶点属性。名称区分大小写,且必须带有坐标空间后缀——构造函数会拒绝不带后缀的 position/normal/tangent/bitangent。

常见字段(完整表格见 REFERENCE.md):

  • positionMC ——模型坐标,在 VS 和 FS 中均有效
  • positionWC ——世界(ECEF)坐标,仅限片元着色器,精度较低
  • positionEC ——眼睛坐标,仅限片元着色器
  • normalMC / normalEC ——顶点 / 片元
  • tangentMC / tangentEC、bitangentMC / bitangentEC
  • texCoord_N、color_N、joints_N、weights_N

**坐标空间验证。**构造函数会扫描着色器文本;如果组合无效,会抛出 DeveloperError("<name> is not available in the <stage> shader. Did you mean <alt> instead?")。例如:在顶点着色器中使用 positionEC,或在片元着色器中使用 normalMC。

自定义、以下划线开头的 glTF 属性(_FEATURE_ID_0、_SURFACE_TEMP)会转换为小写并移除前缀:fsInput.attributes.surface_temp。

FeatureIds

vsInput.featureIds / fsInput.featureIds 会将三个 glTF 来源统一到一个结构体中:

  • featureId_N ——来自 EXT_mesh_features 的要素 ID 属性和隐式属性(N 是图元 featureIds 数组中的索引)。也包括要素 ID 纹理,但仅限片元着色器使用。
  • instanceFeatureId_N ——来自 EXT_instance_features + EXT_mesh_gpu_instancing 的逐实例要素 ID。
  • 命名别名 ——如果 glTF 指定 "label": "perVertex",则也可以使用 featureIds.perVertex。
  • 旧版 3D Tiles 1.0 的 BATCH_ID / _BATCHID → 会透明地重命名为 featureId_0。

GLSL 类型始终为 int。WebGL 1 中,数值超过 2^24 时会失去精度。

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
}

参见 examples/03-feature-id-tileset.js。

元数据

EXT_structural_metadata 提供三种源类型(自 1.139 起,均可从着色器访问):

  • 属性 — 每个顶点一份。顶点着色器和片段着色器。
  • 属性纹理 — 每个纹素一份。仅限片段着色器。
  • 属性表 — 每个要素一份,以要素 ID 为键。在 1.139 中新增(#13124)。
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);
}

属性 ID 规范化(GLSL 标识符规则):

  • 非字母数字字符序列 → 单个 _(temperature ℃ → temperature_)
  • 去掉前缀 gl_(gl_custom → custom)
  • 以数字开头时添加前缀 _(12345 → _12345)
  • 规范化后发生冲突 → 行为未定义

同级结构体: metadataClass.<prop>.noData | defaultValue | minValue | maxValue(类模式边界值)和 metadataStatistics.<prop>.minValue | maxValue | mean | median | standardDeviation | variance | sum(仅当 tileset.json 声明了 statistics 时才会填充)。

1.139 破坏性变更(#13135): 无符号整数元数据不再转换为有符号 int。将 UINT 属性赋值给 GLSL int(int x = fsInput.metadata.myUint;)将无法编译。请使用匹配的无符号类型。

在 .glb 上没有 EXT_structural_metadata 的公开资源十分稀少——大多数真实场景中的元数据都位于 3D Tiles 中。参见 examples/06-metadata-ramp.js(目标为 Cesium3DTileset)。

czm_modelVertexOutput 和 czm_modelMaterial

czm_modelVertexOutput(顶点着色器的 inout vsOutput):

struct czm_modelVertexOutput {
  vec3 positionMC;    // initialized to vsInput.attributes.positionMC
  float pointSize;    // overrides gl_PointSize and Cesium3DTileStyle point sizing
};

注意事项: 修改 positionMC 会使顶点发生位移,但不会更新图元的包围球。大幅位移的顶点可能会被视锥剔除。

czm_modelMaterial(片段着色器的 inout material)。所有颜色均为线性 RGB。条件字段 specularWeight / anisotropic* / clearcoatFactor……仅当图元启用了 KHR_materials_specular / _anisotropy / _clearcoat 时才会出现(完整的 #ifdef 保护结构体请参见 REFERENCE.md):

struct czm_modelMaterial {
  vec4 baseColor; vec3 diffuse; float alpha;
  vec3 specular;  float roughness;
  vec3 normalEC;  float occlusion;  vec3 emissive;
  // + conditional PBR extension fields
};

内置 czm_* 自动 uniform

无需声明即可使用。对 CustomShader 最有用的包括:czm_frameNumber、czm_pi、czm_model、czm_view、czm_projection、czm_modelView、czm_normal、czm_lightDirectionEC、czm_sunDirectionWC、czm_eyeHeight、czm_sceneMode、czm_viewerPositionWC、czm_splitPosition。完整目录见 REFERENCE.md。

VoxelPrimitive 着色器子集

仅限片段着色器。在视线方向的每个光线步进处执行;最终像素会合成所有步进结果。提供 vertexShaderText 会被静默忽略。

可用结构体有所精简:

  • fsInput.attributes — 仅包含 positionEC 和 normalEC。
  • fsInput.featureIds — 不存在。
  • fsInput.metadata — 完全支持。
  • fsInput.metadataClass — 不存在。
  • fsInput.metadataStatistics — 仅包含 minValue 和 maxValue。

将 customShader = undefined 赋值后,会回退到 VoxelPrimitive.DefaultCustomShader。参见 examples/07-voxel-shader.js。关于 VoxelPrimitive 的设置(provider、shape、modelMatrix、nearestSampling),请参见 cesiumjs-3d-tiles。

1.130 破坏性变更(#12636): 已移除 fsInput.voxel.positionUv | positionShapeUv | positionLocal。请改用 fsInput.attributes.positionEC。fsInput.voxel.surfaceNormal → fsInput.attributes.normalEC。 1.142 修复(#13517): 内置默认体素着色器能更稳健地处理常见元数据类型。仅当需要显式分类、着色、过滤或光线步进逻辑时,才保留自定义着色器。

常见模式

文件目标演示内容
examples/01-diffuse-tint.jsModel时间统一变量驱动 material.diffuse
examples/02-texture-swap.jsModelTextureUniform、SAMPLER_2D、setUniform
examples/03-feature-id-tileset.jsCesium3DTilesetfsInput.featureIds.featureId_0 分类着色
examples/04-translucent-override.jsModel(不透明源)CustomShaderTranslucencyMode.TRANSLUCENT
examples/05-vertex-displacement.jsModelvsOutput.positionMC + 法线偏移
examples/06-metadata-ramp.jsCesium3DTilesetfsInput.metadata.<prop> + metadataStatistics 归一化
examples/07-voxel-shader.jsVoxelPrimitive仅 FS 子集、每个体素的元数据

CesiumJS 1.139-1.142 版本说明

逐字引用上游 CHANGES.md:

破坏性变更(1.139,#13135):

依赖 EXT_structural_metadata 扩展派生元数据的 Custom Shader 不再将无符号整数元数据类型转换为有符号整数。任何将 UINT 类型元数据赋给本地整数的现有 Custom Shader(例如 int myMetadata = vsInput.metadata.myUintMetadata)都将无法编译。

破坏性变更(1.139,#13170): 修复了在自定义片段着色器中访问点云属性时的精度问题。

新增(1.139,#13124): 支持访问属性表中的元数据(此前仅支持属性/纹理)。

新增(1.139,#13135): 通过属性纹理支持更多元数据类型。

修复(1.139,#13231): 元数据变量正则表达式扩展至 metadataClass 和 metadataStatistics。

修复(1.139.1,#13247): 修复 NGA-GPM 本地扩展与自定义着色器回归问题。

修复(1.140,#13258): 当类定义中存在元数据时,不再因图元缺少元数据而禁用自定义着色器。

新增(1.140,#13323): 通过降精度转换,有限支持双精度元数据。

修复(1.142,#13517): 改进常见元数据类型的默认体素着色器。

破坏性变更(1.130,#12636): 体素 FragmentInput 结构已调整(参见 VoxelPrimitive 部分)。

注意事项与陷阱

  1. 必须带有坐标空间后缀。 在顶点着色器中使用 positionEC、在片段着色器中使用 normalMC,或使用任何不带后缀的 position/normal/tangent/bitangent,都会在构造时抛出 DeveloperError,并提供建议替代项。
  2. positionMC 在片段着色器中有效——尽管带有 MC 后缀。只有 normalMC/tangentMC/bitangentMC 会在 FS 中被拒绝。
  3. 顶点位移不会更新包围球。 位移后的顶点可能会被视锥体意外剔除。
  4. Cesium3DTileStyle + CustomShader = 未定义行为。 上游 JSDoc 如此说明。每个 tileset 只能选用其中一种。
  5. 构造时会拒绝 SAMPLER_CUBE。 仅使用 SAMPLER_2D。
  6. 参数名称约定。 vsInput、vsOutput、fsInput、material 会通过正则表达式扫描——更改名称会导致代码生成失败。
  7. TextureUniform 的 URL 与 typedArray 只能二选一。 同时提供或都不提供都会抛出异常。typedArray 需要 width + height。
  8. 在 INHERIT 下,不透明模型上的 Alpha 写入会被静默忽略。 设置 translucencyMode: CustomShaderTranslucencyMode.TRANSLUCENT——不要只写入 material.alpha 并期待它生效。
  9. 必须调用 customShader.destroy()。 释放持有纹理统一变量的着色器时请调用,否则其 TextureManager 会泄漏 GPU 资源。
  10. vsOutput.pointSize 会覆盖 Cesium3DTileStyle 的点大小设置。 除非这是预期行为,否则不要设置它。
  11. 元数据属性 ID 会经过清理。 非字母数字字符会替换为 _;开头的 gl_ 会被移除;发生冲突时行为未定义。
  12. UINT 元数据现在会保留有符号性(1.139+,#13135)。 使用 uint x = fsInput.metadata.prop;,不要使用 int x = ...。
  13. Cesium3DTileset 上的 customShader 属于 @experimental。 可能会在不遵循 Cesium 标准弃用政策的情况下发生变化。
  14. Tileset:只有由 Model 支持的瓦片内容才会使用 CustomShader。 原生 I3S 和其他格式不受影响。

性能提示

  • REPLACE_MATERIAL 会跳过材质阶段(不采样纹理和 PBR 输入)。
  • LightingModel.UNLIT 会跳过光照计算——与 REPLACE_MATERIAL 结合使用可实现平面程序化着色。
  • 在顶点着色器中写入 varying,而不是在每个片元中重复计算。
  • 避免每帧调用 setUniform 设置 SAMPLER_2D——这会触发异步纹理重新加载。使用一次 url 并保留该引用。
  • 拆除时调用 customShader.destroy(),以释放 GPU 纹理资源。

另请参阅

  • REFERENCE.md — 完整的结构体表格(Attributes、FeatureIds、Metadata/Class/Statistics)、枚举值表格、内置 czm_* uniform 目录、坐标空间验证错误参考。
  • examples/ — 七个通过编译测试的代码片段。examples/_sandcastle-template.html 是内部脚手架;examples/README.md 介绍了目录布局。
  • cesiumjs-materials-shaders — 用于 Fabric Material、ImageBasedLighting、PostProcessStage。
  • cesiumjs-3d-tiles — Cesium3DTileStyle、Cesium3DTileset 设置、VoxelPrimitive 实例化。
  • cesiumjs-models-particles — Model.fromGltfAsync、ModelFeature.getProperty()、动画。
  • cesiumjs-primitives — 在经典 Primitive 几何体的外观中使用 Fabric。
  • CesiumJS Custom Shader Guide — CesiumGS/cesium main 分支中的 Documentation/CustomShaderGuide/README.md。

评论

支持 Markdown