Skip to content

Commit

Permalink
Merge pull request #10228 from CesiumGS/custom-shaders-property-attri…
Browse files Browse the repository at this point in the history
…butes

Property Attributes in Custom Shaders
  • Loading branch information
j9liu authored Mar 28, 2022
2 parents 1f45b42 + f2c08b5 commit 07ac082
Show file tree
Hide file tree
Showing 39 changed files with 2,020 additions and 57 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- Added image-based lighting to `ModelExperimental`. [#10234](https://github.com/CesiumGS/cesium/pull/10234)
- Added a 'renderable' property to 'Fog' to disable its visual rendering while preserving tiles culling at a distance
- Refactored metadata API so `tileset.metadata` and `content.group.metadata` are more symmetric with `content.metadata` and `tile.metadata`. [#10224](https://github.com/CesiumGS/cesium/pull/10224)
- Added support for `EXT_structural_metadata` property attributes in `CustomShader` [#10228](https://github.com/CesiumGS/cesium/pull/10228)

##### Fixes :wrench:

Expand Down
123 changes: 121 additions & 2 deletions Documentation/CustomShaderGuide/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ struct VertexInput {
Attributes attributes;
// Feature IDs/Batch IDs. See the FeatureIds Struct section below.
FeatureIds featureIds;
// In the future, metadata will be added here.
// Metadata properties. See the Metadata Struct section below.
Metadata metadata;
};
```

Expand All @@ -216,7 +217,8 @@ struct FragmentInput {
Attributes attributes;
// Feature IDs/Batch IDs. See the FeatureIds Struct section below.
FeatureIds featureIds;
// In the future, metadata will be added here.
// Metadata properties. See the Metadata Struct section below.
Metadata metadata;
};
```

Expand Down Expand Up @@ -554,6 +556,123 @@ to the `EXT_feature_metadata` extension:
]
```

## `Metadata` struct

This struct contains the relevant metadata properties accessible to the
model from the
[`EXT_structural_metadata`](https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata)
glTF extension (or the older
[`EXT_feature_metadata`](https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata) extension).

The following types of metadata are currently supported. We plan to add more
in the near future:

- property attributes from the `EXT_structural_metadata` glTF extension.

Regardless of the source of metadata, the properties are collected into a single
struct by property ID. For example, if the metadata class looked like this:

```jsonc
"schema": {
"classes": {
"wall": {
"properties": {
"temperature": {
"name": "Surface Temperature",
"type": "SCALAR",
"componentType": "FLOAT32"
}
}
}
}
}
```

This will show up in the shader as the struct field as follows:

```
struct Metadata {
float temperature;
}
```

Now the temperature can be accessed as `vsInput.metadata.temperature` or
`fsInput.metadata.temperature`.

### Normalized values

If the class property specifies `normalized: true`, the property will appear
in the shader as the appropriate floating point type (e.g. `float` or `vec3`).
All components will be between the range of `[0, 1]` (unsigned) or `[-1, 1]`
(signed).

For example,

```jsonc
"schema": {
"classes": {
"wall": {
"properties": {
// damage normalized between 0.0 and 1.0 though stored as a UINT8 in
// the glTF
"damageAmount": {
"name": "Wall damage (normalized)",
"type": "SCALAR",
"componentType": "UINT32",
"normalized": true
}
}
}
}
}
```

This will appear as a `float` value from 0.0 to 1.0, accessible via
`(vsInput|fsInput).metadata.damageAmount`

### Offset and scale

If the property provides an `offset` or `scale`, this is automatically applied
after normalization (when applicable). This is useful to pre-scale values into
a convenient range.

For example, consider taking a normalized temperature value and automatically
converting this to Celsius or Fahrenheit:

```jsonc
"schema": {
"classes": {
"wall": {
"properties": {
// scaled to the range [0, 100] in °C
"temperatureCelsius": {
"name": "Temperature (°C)",
"type": "SCALAR",
"componentType": "UINT32",
"normalized": true,
// offset defaults to 0, scale defaults to 1
"scale": 100
},
// scaled/shifted to the range [32, 212] in °F
"temperatureFahrenheit": {
"name": "Temperature (°C)",
"type": "SCALAR",
"componentType": "UINT32",
"normalized": true,
"offset": 32,
"scale": 180
}
}
}
}
}
```

In the shader, `(vsInput|fsInput).metadata.temperatureCelsius` will be a `float`
with a value between 0.0 and 100.0, while
`(vsInput|fsInput).metadata.temperatureFahrenheit` will be a `float` with a
range of `[32.0, 212.0]`.

## `czm_modelVertexOutput` struct

This struct is built-in, see the [documentation comment](../../../Shaders/Builtin/Structs/modelVertexOutput.glsl).
Expand Down
18 changes: 17 additions & 1 deletion Source/Renderer/ShaderBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,10 @@ ShaderBuilder.prototype.addAttribute = function (type, identifier) {

const location = this._nextAttributeLocation;
this._attributeLocations[identifier] = location;
this._nextAttributeLocation++;

// Most attributes only require a single attribute location, but matrices
// require more.
this._nextAttributeLocation += getAttributeLocationCount(type);
return location;
};

Expand Down Expand Up @@ -519,6 +522,19 @@ function generateStructLines(shaderBuilder) {
};
}

function getAttributeLocationCount(glslType) {
switch (glslType) {
case "mat2":
return 2;
case "mat3":
return 3;
case "mat4":
return 4;
default:
return 1;
}
}

function generateFunctionLines(shaderBuilder) {
const vertexLines = [];
const fragmentLines = [];
Expand Down
29 changes: 29 additions & 0 deletions Source/Scene/AttributeType.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,35 @@ AttributeType.getNumberOfComponents = function (attributeType) {
}
};

/**
* Get the number of attribute locations needed to fit this attribute. Most
* types require one, but matrices require multiple attribute locations.
*
* @param {AttributeType} attributeType The attribute type.
* @returns {Number} The number of attribute locations needed in the shader
*
* @private
*/
AttributeType.getAttributeLocationCount = function (attributeType) {
switch (attributeType) {
case AttributeType.SCALAR:
case AttributeType.VEC2:
case AttributeType.VEC3:
case AttributeType.VEC4:
return 1;
case AttributeType.MAT2:
return 2;
case AttributeType.MAT3:
return 3;
case AttributeType.MAT4:
return 4;
//>>includeStart('debug', pragmas.debug);
default:
throw new DeveloperError("attributeType is not a valid value.");
//>>includeEnd('debug');
}
};

/**
* Gets the GLSL type for the attribute type.
*
Expand Down
11 changes: 11 additions & 0 deletions Source/Scene/ModelExperimental/CustomShader.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import TextureManager from "./TextureManager.js";
* @typedef {Object} VertexVariableSets
* @property {VariableSet} attributeSet A set of all unique attributes used in the vertex shader via the <code>vsInput.attributes</code> struct.
* @property {VariableSet} featureIdSet A set of all unique feature ID sets used in the vertex shader via the <code>vsInput.featureIds</code> struct.
* @property {VariableSet} metadataSet A set of all unique metadata properties used in the vertex shader via the <code>vsInput.metadata</code> struct.
* @private
*/

Expand All @@ -45,6 +46,7 @@ import TextureManager from "./TextureManager.js";
* @typedef {Object} FragmentVariableSets
* @property {VariableSet} attributeSet A set of all unique attributes used in the fragment shader via the <code>fsInput.attributes</code> struct
* @property {VariableSet} featureIdSet A set of all unique feature ID sets used in the fragment shader via the <code>fsInput.featureIds</code> struct.
* @property {VariableSet} metadataSet A set of all unique metadata properties used in the fragment shader via the <code>fsInput.metadata</code> struct.
* @property {VariableSet} materialSet A set of all material variables such as diffuse, specular or alpha that are used in the fragment shader via the <code>material</code> struct.
* @private
*/
Expand Down Expand Up @@ -212,6 +214,7 @@ export default function CustomShader(options) {
this.usedVariablesVertex = {
attributeSet: {},
featureIdSet: {},
metadataSet: {},
};
/**
* A collection of variables used in <code>fragmentShaderText</code>. This
Expand All @@ -222,6 +225,7 @@ export default function CustomShader(options) {
this.usedVariablesFragment = {
attributeSet: {},
featureIdSet: {},
metadataSet: {},
materialSet: {},
};

Expand Down Expand Up @@ -291,6 +295,7 @@ function getVariables(shaderText, regex, outputSet) {
function findUsedVariables(customShader) {
const attributeRegex = /[vf]sInput\.attributes\.(\w+)/g;
const featureIdRegex = /[vf]sInput\.featureIds\.(\w+)/g;
const metadataRegex = /[vf]sInput\.metadata.(\w+)/g;
let attributeSet;

const vertexShaderText = customShader.vertexShaderText;
Expand All @@ -300,6 +305,9 @@ function findUsedVariables(customShader) {

attributeSet = customShader.usedVariablesVertex.featureIdSet;
getVariables(vertexShaderText, featureIdRegex, attributeSet);

attributeSet = customShader.usedVariablesVertex.metadataSet;
getVariables(vertexShaderText, metadataRegex, attributeSet);
}

const fragmentShaderText = customShader.fragmentShaderText;
Expand All @@ -310,6 +318,9 @@ function findUsedVariables(customShader) {
attributeSet = customShader.usedVariablesFragment.featureIdSet;
getVariables(fragmentShaderText, featureIdRegex, attributeSet);

attributeSet = customShader.usedVariablesFragment.metadataSet;
getVariables(fragmentShaderText, metadataRegex, attributeSet);

const materialRegex = /material\.(\w+)/g;
const materialSet = customShader.usedVariablesFragment.materialSet;
getVariables(fragmentShaderText, materialRegex, materialSet);
Expand Down
13 changes: 13 additions & 0 deletions Source/Scene/ModelExperimental/CustomShaderPipelineStage.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import CustomShaderStageFS from "../../Shaders/ModelExperimental/CustomShaderSta
import AlphaMode from "../AlphaMode.js";
import CustomShaderMode from "./CustomShaderMode.js";
import FeatureIdPipelineStage from "./FeatureIdPipelineStage.js";
import MetadataPipelineStage from "./MetadataPipelineStage.js";
import ModelExperimentalUtility from "./ModelExperimentalUtility.js";

/**
Expand Down Expand Up @@ -512,6 +513,12 @@ function addVertexLinesToShader(shaderBuilder, vertexLines) {
FeatureIdPipelineStage.STRUCT_NAME_FEATURE_IDS,
"featureIds"
);
// Add Metadata struct from the metadata stage
shaderBuilder.addStructField(
structId,
MetadataPipelineStage.STRUCT_NAME_METADATA,
"metadata"
);

const functionId =
CustomShaderPipelineStage.FUNCTION_ID_INITIALIZE_INPUT_STRUCT_VS;
Expand Down Expand Up @@ -562,6 +569,12 @@ function addFragmentLinesToShader(shaderBuilder, fragmentLines) {
FeatureIdPipelineStage.STRUCT_NAME_FEATURE_IDS,
"featureIds"
);
// Add Metadata struct from the metadata stage
shaderBuilder.addStructField(
structId,
MetadataPipelineStage.STRUCT_NAME_METADATA,
"metadata"
);

const functionId =
CustomShaderPipelineStage.FUNCTION_ID_INITIALIZE_INPUT_STRUCT_FS;
Expand Down
Loading

0 comments on commit 07ac082

Please sign in to comment.