Skip to content

Commit

Permalink
Merge pull request CesiumGS#11766 from CesiumGS/feature-id-texture-tr…
Browse files Browse the repository at this point in the history
…ansform-fix

Apply texture transforms to feature ID textures
  • Loading branch information
ptrgags authored Jan 16, 2024
2 parents f0a2309 + 1c7f460 commit 9cf4839
Show file tree
Hide file tree
Showing 8 changed files with 362 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- Fixed improper scaling of ellipsoid inner radii in 3D mode. [#11656](https://github.com/CesiumGS/cesium/issues/11656) and [#10245](https://github.com/CesiumGS/cesium/issues/10245)
- Fixed `Entity` documentation for `orientation` property. [#11762](https://github.com/CesiumGS/cesium/pull/11762)
- Fixed an issue where `DataSource` objects incorrectly shared a single `PolylineCollection` in the `PolylineGeometryUpdater`. Updated `PolylineGeometryUpdater` to create a distinct `PolylineCollection` instance per `DataSource`. This resolves the crashes reported under [#7758](https://github.com/CesiumGS/cesium/issues/7758) and [#9154](https://github.com/CesiumGS/cesium/issues/9154).
- Fixed a bug where transforms that had been defined with the `KHR_texture_transform` extension had not been applied to Feature ID Textures in `EXT_mesh_features`. [#11731](https://github.com/CesiumGS/cesium/issues/11731)

#### @cesium/widgets

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
{
"asset": {
"generator": "glTF-Transform v3.6.0",
"version": "2.0"
},
"accessors": [
{
"type": "SCALAR",
"componentType": 5123,
"count": 6,
"bufferView": 0,
"byteOffset": 0
},
{
"type": "VEC3",
"componentType": 5126,
"count": 4,
"max": [
1,
1,
0
],
"min": [
0,
0,
0
],
"bufferView": 1,
"byteOffset": 0
},
{
"type": "VEC3",
"componentType": 5126,
"count": 4,
"bufferView": 1,
"byteOffset": 12
},
{
"type": "VEC2",
"componentType": 5126,
"count": 4,
"bufferView": 1,
"byteOffset": 24
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 12,
"target": 34963
},
{
"buffer": 0,
"byteOffset": 12,
"byteLength": 128,
"byteStride": 32,
"target": 34962
}
],
"samplers": [
{
"magFilter": 9728,
"minFilter": 9728,
"wrapS": 33071,
"wrapT": 33071
}
],
"textures": [
{
"source": 0,
"sampler": 0
}
],
"images": [
{
"uri": "FeatureIdTextureWithTextureTransform_img0.png"
}
],
"buffers": [
{
"uri": "FeatureIdTextureWithTextureTransform_data.bin",
"byteLength": 140
}
],
"materials": [
{
"doubleSided": true,
"pbrMetallicRoughness": {
"metallicFactor": 0,
"baseColorTexture": {
"index": 0,
"extensions": {
"KHR_texture_transform": {
"offset": [
0.25,
0.25
],
"scale": [
0.5,
0.5
]
}
}
}
}
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"POSITION": 1,
"NORMAL": 2,
"TEXCOORD_0": 3
},
"mode": 4,
"material": 0,
"indices": 0,
"extensions": {
"EXT_mesh_features": {
"featureIds": [
{
"texture": {
"channels": [
0
],
"index": 0,
"extensions": {
"KHR_texture_transform": {
"offset": [
0.25,
0.25
],
"scale": [
0.5,
0.5
]
}
}
}
}
]
}
}
}
]
}
],
"nodes": [
{
"mesh": 0
}
],
"scenes": [
{
"nodes": [
0
]
}
],
"scene": 0,
"extensionsUsed": [
"KHR_texture_transform",
"EXT_mesh_features"
],
"extensionsRequired": [
"KHR_texture_transform"
]
}
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Feature ID Texture With Texture Transform

The test data for https://github.com/CesiumGS/cesium/issues/11731 :

It is a glTF asset that only contains a unit square.

It uses the same texture for the base color and for a Feature ID Texture:
The texture just contains 8x8 pixels with increasing 'red' component
values: The red components will be [0 ... 64)*3. (Meaning that the
lower right pixel will have a red value of 63*3=189).

So the base color will be (black ... red), and the feature ID values in
the property texture will be in [0...189].

It uses the same texture transform for both usages of the texture,
namely with an offset of [0.25, 0.25], and a scale of [0.5, 0.5].
27 changes: 25 additions & 2 deletions packages/engine/Source/Scene/Model/FeatureIdPipelineStage.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import FeatureIdStageVS from "../../Shaders/Model/FeatureIdStageVS.js";
import ModelComponents from "../ModelComponents.js";
import VertexAttributeSemantic from "../VertexAttributeSemantic.js";
import ModelUtility from "./ModelUtility.js";
import Matrix3 from "../../Core/Matrix3.js";

/**
* The feature ID pipeline stage is responsible for processing feature IDs
Expand Down Expand Up @@ -387,10 +388,32 @@ function processTexture(
ShaderDestination.FRAGMENT
);

// Get a GLSL expression for the texture coordinates
const texCoord = textureReader.texCoord;
const texCoordVariable = `v_texCoord_${texCoord}`;
let texCoordVariableExpression = texCoordVariable;

// Check if the texture defines a `transform` from a `KHR_texture_transform`
const transform = textureReader.transform;
if (defined(transform) && !Matrix3.equals(transform, Matrix3.IDENTITY)) {
// Add a uniform for the transformation matrix
const transformUniformName = `${uniformName}Transform`;
shaderBuilder.addUniform(
"mat3",
transformUniformName,
ShaderDestination.FRAGMENT
);
uniformMap[transformUniformName] = function () {
return transform;
};
// Update the expression for the texture coordinates
// with one that transforms the texture coordinates
// with the transform matrix first
texCoordVariableExpression = `vec2(${transformUniformName} * vec3(${texCoordVariable}, 1.0))`;
}
// Read one or more channels from the texture
// example: texture(u_featureIdTexture_0, v_texCoord_1).rg
const texCoord = `v_texCoord_${textureReader.texCoord}`;
const textureRead = `texture(${uniformName}, ${texCoord}).${channels}`;
const textureRead = `texture(${uniformName}, ${texCoordVariableExpression}).${channels}`;

// Finally, assign to the struct field. Example:
// featureIds.featureId_0 = unpacked;
Expand Down
80 changes: 80 additions & 0 deletions packages/engine/Specs/Scene/Model/FeatureIdPipelineStageSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ describe(
const weather = "./Data/Models/glTF-2.0/Weather/glTF/weather.gltf";
const largeFeatureIdTexture =
"./Data/Models/glTF-2.0/LargeFeatureIdTexture/glTF/LargeFeatureIdTexture.gltf";
const featureIdTextureWithTextureTransformUrl =
"./Data/Models/glTF-2.0/FeatureIdTextureWithTextureTransform/glTF/FeatureIdTextureWithTextureTransform.gltf";

let scene;
const gltfLoaders = [];
Expand Down Expand Up @@ -491,6 +493,84 @@ describe(
});
});

it("adds feature ID texture transforms to the shader", async function () {
const gltfLoader = await loadGltf(
featureIdTextureWithTextureTransformUrl
);
const components = gltfLoader.components;
const node = components.nodes[0];
const primitive = node.primitives[0];
const frameState = scene.frameState;
const renderResources = mockRenderResources(node);

FeatureIdPipelineStage.process(renderResources, primitive, frameState);

const shaderBuilder = renderResources.shaderBuilder;
ShaderBuilderTester.expectHasVertexStruct(
shaderBuilder,
FeatureIdPipelineStage.STRUCT_ID_FEATURE_IDS_VS,
FeatureIdPipelineStage.STRUCT_NAME_FEATURE_IDS,
[]
);
ShaderBuilderTester.expectHasFragmentStruct(
shaderBuilder,
FeatureIdPipelineStage.STRUCT_ID_FEATURE_IDS_FS,
FeatureIdPipelineStage.STRUCT_NAME_FEATURE_IDS,
[" int featureId_0;"]
);
ShaderBuilderTester.expectHasVertexFunctionUnordered(
shaderBuilder,
FeatureIdPipelineStage.FUNCTION_ID_INITIALIZE_FEATURE_IDS_VS,
FeatureIdPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_FEATURE_IDS,
[]
);
ShaderBuilderTester.expectHasFragmentFunctionUnordered(
shaderBuilder,
FeatureIdPipelineStage.FUNCTION_ID_INITIALIZE_FEATURE_IDS_FS,
FeatureIdPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_FEATURE_IDS,
[
" featureIds.featureId_0 = czm_unpackUint(texture(u_featureIdTexture_0, vec2(u_featureIdTexture_0Transform * vec3(v_texCoord_0, 1.0))).r);",
]
);
ShaderBuilderTester.expectHasVertexFunctionUnordered(
shaderBuilder,
FeatureIdPipelineStage.FUNCTION_ID_INITIALIZE_FEATURE_ID_ALIASES_VS,
FeatureIdPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_FEATURE_ID_ALIASES,
[]
);
ShaderBuilderTester.expectHasVertexFunctionUnordered(
shaderBuilder,
FeatureIdPipelineStage.FUNCTION_ID_SET_FEATURE_ID_VARYINGS,
FeatureIdPipelineStage.FUNCTION_SIGNATURE_SET_FEATURE_ID_VARYINGS,
[]
);
ShaderBuilderTester.expectHasVertexDefines(shaderBuilder, []);
ShaderBuilderTester.expectHasFragmentDefines(shaderBuilder, []);
ShaderBuilderTester.expectHasAttributes(shaderBuilder, undefined, []);
ShaderBuilderTester.expectHasVertexUniforms(shaderBuilder, []);
ShaderBuilderTester.expectHasFragmentUniforms(shaderBuilder, [
"uniform sampler2D u_featureIdTexture_0;",
"uniform mat3 u_featureIdTexture_0Transform;",
]);
ShaderBuilderTester.expectHasVaryings(shaderBuilder, []);
ShaderBuilderTester.expectVertexLinesEqual(shaderBuilder, [
_shadersFeatureIdStageVS,
]);
ShaderBuilderTester.expectFragmentLinesEqual(shaderBuilder, [
_shadersFeatureIdStageFS,
]);

expect(resources).toEqual([]);
expect(renderResources.attributes.length).toBe(1);

const uniformMap = renderResources.uniformMap;
expect(uniformMap.u_featureIdTexture_0).toBeDefined();
const featureIdTexture = primitive.featureIds[0];
expect(uniformMap.u_featureIdTexture_0()).toBe(
featureIdTexture.textureReader.texture
);
});

it("processes feature ID textures with multiple channels", function () {
return loadGltf(largeFeatureIdTexture).then(function (gltfLoader) {
const components = gltfLoader.components;
Expand Down
Loading

0 comments on commit 9cf4839

Please sign in to comment.