cesiumjs-custom-shader について
Model、Cesium3DTileset、VoxelPrimitiveの描画に使うCustomShaderのGLSLを記述するCesiumJS向けスキルです。ジオメトリ、フィーチャーID、メタデータをシェーダーで制御したい開発者向けです。
主な機能
- 01頂点・フラグメントシェーダーの入口
- 02UniformとVarying
- 03マテリアルとライティングのモード
- 04glTF属性とフィーチャーID
- 05構造化メタデータへのアクセス
- 06モデルとタイルセット用のカスタムシェーダー
活用シーン
- 01独自GLSLによるモデル表面の着色
- 02フィーチャーIDに基づくタイル要素の着色
- 033Dタイル上でのメタデータ値の可視化
- 04ボクセルデータへのプロシージャルシェーディング
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ cesiumgs/ cesiumjs-skills --skill cesiumjs-custom-shaderインストール後、Claude Code で/cesiumjs-custom-shaderを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
REFERENCE.mdexamples/_sandcastle-template.html01-diffuse-tint.js02-texture-swap.js03-feature-id-tileset.js04-translucent-override.js05-vertex-displacement.js06-metadata-ramp.js07-voxel-shader.jsREADME.md詳細
CesiumJS CustomShader
バージョンの基準:CesiumJS 1.144。すべてのインポートで ES モジュール形式を使用します。
CustomShader は、ユーザーの GLSL を Model / Cesium3DTileset / VoxelPrimitive のレンダリングパイプラインに挿入します。glTF 属性、フィーチャー ID、EXT_structural_metadata を頂点単位およびフラグメント単位のコードに公開し、組み込みの czm_modelVertexOutput および czm_modelMaterial 構造体を通じて値を返します。
このスキルはシェーダー本体の作成に使用してください。次の用途には、以下を使用してください。
cesiumjs-materials-shaders— FabricMaterial、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を参照してください。同じタイルセットで 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モードを使用する不透明なモデルでは、アルファ値の書き込みは通知なく無視されます。
ビジュアル評価時のフレーミング: シェーダーを適用した
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 の値で、JavaScript の値の型は一致している必要があります(例:VEC3 → Cartesian3)。Uniform は、宣言した名前で GLSL からアクセスできます。
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。
SAMPLER_CUBE は UniformType で宣言されていますが、コンストラクターでは拒否されます — DeveloperError("CustomShader does not support samplerCube uniforms") がスローされます。サポートされているのは SAMPLER_2D のみです。
変数
宣言した varying は、頂点シェーダーでは 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が最終色になります(アルファは引き続き適用されます)。フラットシェーディングです。PBR— IBL が利用可能な場合は、物理ベースのライティングを行います。- 省略 — モデルのデフォルトのライティングを維持します。意図的に上書きする場合を除き、省略してください。
プロシージャルなフラットシェーディングのみを行う場合(マテリアルのサンプリングもライティングもなし)は、REPLACE_MATERIAL と UNLIT を組み合わせます。
透明度
CustomShaderTranslucencyMode は、アルファの書き込みがレンダーパスにどう反映されるかを制御します。
INHERIT(デフォルト)— 元のマテリアルが半透明の場合にのみ、アルファが反映されます。OPAQUE— 不透明パスを強制します。TRANSLUCENT— 半透明パスを強制します。
注意点: INHERIT の不透明なモデルで material.alpha を書き込んでも、何も起こりません。アルファの書き込みを有効にするには、translucencyMode: CustomShaderTranslucencyMode.TRANSLUCENT を設定してください。
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/bitangentECtexCoord_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 の 3 つのソースを 1 つの構造体に統合します。
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 は、シェーダーからアクセス可能な 3 種類のソースを公開します(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): 符号なし整数のメタデータは、符号付き整数にキャストされなくなりました。
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_* 自動ユニフォーム
宣言なしで使用できます。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 のセットアップ(プロバイダー、形状、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.js | Model | material.diffuse を駆動する時間ユニフォーム |
examples/02-texture-swap.js | Model | TextureUniform、SAMPLER_2D、setUniform |
examples/03-feature-id-tileset.js | Cesium3DTileset | fsInput.featureIds.featureId_0 による分類色付け |
examples/04-translucent-override.js | Model(不透明なソース) | CustomShaderTranslucencyMode.TRANSLUCENT |
examples/05-vertex-displacement.js | Model | vsOutput.positionMC + 法線方向へのオフセット |
examples/06-metadata-ramp.js | Cesium3DTileset | fsInput.metadata.<prop> + metadataStatistics による正規化 |
examples/07-voxel-shader.js | VoxelPrimitive | FS のみのサブセット、ボクセルごとのメタデータ |
CesiumJS 1.139-1.142 のバージョンに関する注意事項
上流の CHANGES.md よりそのまま引用:
破壊的変更(1.139、#13135):
EXT_structural_metadata拡張から派生したメタデータに依存するカスタムシェーダーでは、符号なし整数型のメタデータが符号付き整数型にキャストされなくなりました。UINT 型のメタデータをローカル整数に代入する既存のカスタムシェーダー(例: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」セクションを参照)。
注意点と落とし穴
- 座標空間の接尾辞が必要です。 頂点シェーダー内の
positionEC、フラグメントシェーダー内のnormalMC、または接尾辞のないposition/normal/tangent/bitangentを使用すると、代替候補とともに構築時にDeveloperErrorがスローされます。 positionMCはフラグメントシェーダーで有効です —MCの接尾辞が付いていても使用できます。FS で拒否されるのはnormalMC/tangentMC/bitangentMCのみです。- 頂点の変位はバウンディングスフィアを更新しません。 変位した頂点が予期せず視錐台カリングされることがあります。
Cesium3DTileStyleと CustomShader の併用は未定義の動作です。 上流の JSDoc による説明です。タイルセットごとにどちらか一方を選択してください。SAMPLER_CUBEは構築時に拒否されます。SAMPLER_2Dのみを使用してください。- パラメーター名に関する契約があります。
vsInput、vsOutput、fsInput、materialは正規表現でスキャンされるため、名前を変更するとコード生成が壊れます。 TextureUniformでは URL と typedArray のどちらか一方を指定します。 両方、またはいずれも指定しない場合は例外がスローされます。typedArrayにはwidthとheightが必要です。- 不透明なモデルでのアルファ値の書き込みは、
INHERITの場合に暗黙に無視されます。translucencyMode: CustomShaderTranslucencyMode.TRANSLUCENTを設定してください。material.alphaに値を書き込むだけで動作するとは限りません。 customShader.destroy()が必要です。 テクスチャユニフォームを保持するシェーダーを破棄するときに呼び出してください。呼び出さないと、そのTextureManagerが GPU リソースをリークします。vsOutput.pointSizeはCesium3DTileStyleのポイントサイズ指定を上書きします。 意図している場合を除き、設定しないでください。- メタデータのプロパティ ID はサニタイズされます。 英数字以外は
_に置き換えられ、先頭のgl_は削除されます。ID が衝突した場合の動作は未定義です。 - UINT メタデータは符号を保持するようになりました(1.139 以降、#13135)。
int x = ...ではなく、uint x = fsInput.metadata.prop;を使用してください。 Cesium3DTilesetのcustomShaderは@experimentalです。 Cesium の標準的な非推奨化ポリシーに従わず変更される可能性があります。- タイルセットでは、
Modelベースのタイルコンテンツのみが CustomShader を使用します。 ネイティブ I3S やその他の形式には影響しません。
パフォーマンスのヒント
REPLACE_MATERIALはマテリアルの段階をスキップします(テクスチャやPBR入力はサンプリングされません)。LightingModel.UNLITはライティング計算をスキップします。フラットなプロシージャルシェーディングを行うには、REPLACE_MATERIALと組み合わせてください。- フラグメントごとに再計算するのではなく、頂点シェーダーでバリアブルに書き込んでください。
SAMPLER_2Dに対するフレームごとのsetUniformは避けてください。非同期のテクスチャ再読み込みが発生します。urlは一度だけ使用し、参照を保持してください。- GPUテクスチャリソースを解放するため、破棄時に
customShader.destroy()を呼び出してください。
関連項目
REFERENCE.md— 構造体の全テーブル(Attributes、FeatureIds、Metadata/Class/Statistics)、列挙値のテーブル、組み込みczm_*ユニフォームの一覧、座標空間の検証エラーのリファレンスです。examples/— コンパイルテスト済みの7つのスニペットです。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 ジオメトリ向けの Appearance 上の Fabric です。- CesiumJS Custom Shader Guide —
CesiumGS/cesiumのmainにあるDocumentation/CustomShaderGuide/README.mdです。