From 9b7e46ed3c2d5fbd018b9e2d3aeacc8fd3c5f721 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Thu, 16 Dec 2021 17:03:10 -0500 Subject: [PATCH 01/21] Stub out attenuation pipeline stage --- .../PointCloudAttenuationPipelineStage.js | 66 +++++++++++++++++++ .../PointCloudAttenuationStageVS.glsl | 8 +++ 2 files changed, 74 insertions(+) create mode 100644 Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js create mode 100644 Source/Shaders/ModelExperimental/PointCloudAttenuationStageVS.glsl diff --git a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js new file mode 100644 index 000000000000..6fd91949267d --- /dev/null +++ b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js @@ -0,0 +1,66 @@ +import Cartesian3 from "../../Core/Cartesian3.js"; +import OrthographicFrustum from "../../Core/OrthographicFrustum.js"; +import ShaderDestination from "../../Renderer/ShaderDestination.js"; +import PointCloudAttenuationStageVS from "../../Shaders/ModelExperimental/PointCloudAttenuationStageVS.js"; +import SceneMode from "../SceneMode.js"; + +var PointCloudAttenuationPipelineStage = {}; +PointCloudAttenuationPipelineStage.name = "PointCloudAttenuationPipelineStage"; // Helps with debugging + +var scratchAttenuationUniform = new Cartesian3(); + +PointCloudAttenuationPipelineStage.process = function ( + renderResources, + primitive, + frameState +) { + var shaderBuilder = renderResources.shaderBuilder; + shaderBuilder.addVertexLines([PointCloudAttenuationStageVS]); + shaderBuilder.addDefine( + "USE_POINT_CLOUD_ATTENUATION", + undefined, + ShaderDestination.VERTEX + ); + + // TODO: what if it comes from the tileset? + var pointCloudShading = renderResources.model.pointCloudShading; + + shaderBuilder.addUniform( + "vec3", + "model_pointCloudAttenuation", + ShaderDestination.VERTEX + ); + renderResources.uniformMap.model_pointCloudAttenuation = function () { + var scratch = scratchAttenuationUniform; + + scratch.x = pointCloudShading.attenuation + ? pointCloudShading.maximumAttenuation + : 1.0; + scratch.x *= frameState.pixelRatio; + + if (pointCloudShading.attenuation) { + var context; + var frustum = frameState.camera.frustum; + var depthMultiplier; + // Attenuation is maximumAttenuation in 2D/ortho + if ( + frameState.mode === SceneMode.SCENE2D || + frustum instanceof OrthographicFrustum + ) { + depthMultiplier = Number.POSITIVE_INFINITY; + } else { + depthMultiplier = + context.drawingBufferHeight / + frameState.camera.frustum.sseDenominator; + } + + // TODO: where to get the gE from? especially when not a part of a + // tileset? + var geometricError = 0; + scratch.y = geometricError * pointCloudShading.geometricErrorScale; + scratch.z = depthMultiplier; + } + + return scratch; + }; +}; diff --git a/Source/Shaders/ModelExperimental/PointCloudAttenuationStageVS.glsl b/Source/Shaders/ModelExperimental/PointCloudAttenuationStageVS.glsl new file mode 100644 index 000000000000..6ed4c2b4daa0 --- /dev/null +++ b/Source/Shaders/ModelExperimental/PointCloudAttenuationStageVS.glsl @@ -0,0 +1,8 @@ +float pointCloudAttenuationStage(vec3 positionEC) { + // Variables are packed into a single vector to minimize gl.uniformXXX() calls + float pointSize = model_pointCloudAttenuation.x; + float geometricError = model_pointCloudAttenuation.y; + float depthMultiplier = model_pointCloudAttenuation.z; + float depth = -positionEC.z; + return min((geometricError / depth) * depthMultiplier, pointSize); +} From 4292d622cfbdef9e3880860b702a7437f7ad3399 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Fri, 17 Dec 2021 14:00:35 -0500 Subject: [PATCH 02/21] Grab pointCloudShading from model or tileset --- .../ModelExperimentalPrimitive.js | 7 +++++++ .../PointCloudAttenuationPipelineStage.js | 19 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js index 18f5624666a9..f7604af20d1e 100644 --- a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js +++ b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js @@ -12,6 +12,7 @@ import LightingPipelineStage from "./LightingPipelineStage.js"; import MaterialPipelineStage from "./MaterialPipelineStage.js"; import ModelExperimentalUtility from "./ModelExperimentalUtility.js"; import PickingPipelineStage from "./PickingPipelineStage.js"; +import PointCloudAttenuationPipelineStage from "./PointCloudAttenuationPipelineStage.js"; import VertexAttributeSemantic from "../VertexAttributeSemantic.js"; /** @@ -94,9 +95,15 @@ function initialize(runtimePrimitive) { var hasQuantization = ModelExperimentalUtility.hasQuantizedAttributes( primitive.attributes ); + var is3DTiles = defined(model.content); + var hasPointCloudShading = is3DTiles || defined(model.pointCloudShading); pipelineStages.push(GeometryPipelineStage); + if (hasPointCloudShading) { + pipelineStages.push(PointCloudAttenuationPipelineStage); + } + if (hasQuantization) { pipelineStages.push(DequantizationPipelineStage); } diff --git a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js index 6fd91949267d..9e03852cec67 100644 --- a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js +++ b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js @@ -1,5 +1,7 @@ import Cartesian3 from "../../Core/Cartesian3.js"; +import defined from "../../Core/defined.js"; import OrthographicFrustum from "../../Core/OrthographicFrustum.js"; +import PrimitiveType from "../../Core/PrimitiveType.js"; import ShaderDestination from "../../Renderer/ShaderDestination.js"; import PointCloudAttenuationStageVS from "../../Shaders/ModelExperimental/PointCloudAttenuationStageVS.js"; import SceneMode from "../SceneMode.js"; @@ -14,6 +16,11 @@ PointCloudAttenuationPipelineStage.process = function ( primitive, frameState ) { + // Attenuation only applies to point primitives + if (primitive.primitiveType !== PrimitiveType.POINTS) { + return; + } + var shaderBuilder = renderResources.shaderBuilder; shaderBuilder.addVertexLines([PointCloudAttenuationStageVS]); shaderBuilder.addDefine( @@ -22,8 +29,16 @@ PointCloudAttenuationPipelineStage.process = function ( ShaderDestination.VERTEX ); - // TODO: what if it comes from the tileset? - var pointCloudShading = renderResources.model.pointCloudShading; + var model = renderResources.model; + var pointCloudShading; + + // If this is 3D Tiles, use the point cloud shading object + if (defined(model.content)) { + var tileset = model.content.tileset; + pointCloudShading = tileset.pointCloudShading; + } else { + pointCloudShading = model.pointCloudShading; + } shaderBuilder.addUniform( "vec3", From dd6fb505a644b64aeda9015b141b12572e962db1 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Thu, 6 Jan 2022 16:35:27 -0500 Subject: [PATCH 03/21] export default for pipeline stage --- .../ModelExperimental/PointCloudAttenuationPipelineStage.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js index 9e03852cec67..554c6d36331f 100644 --- a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js +++ b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js @@ -79,3 +79,5 @@ PointCloudAttenuationPipelineStage.process = function ( return scratch; }; }; + +export default PointCloudAttenuationPipelineStage; From 2a002f2d7553d448bf04e89af59e8f69a41f1482 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Fri, 7 Jan 2022 16:04:58 -0500 Subject: [PATCH 04/21] add attenuation to the shader --- .../PointCloudAttenuationPipelineStage.js | 40 ++++++++++++++----- .../ModelExperimentalVS.glsl | 2 + 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js index 554c6d36331f..5b6202785368 100644 --- a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js +++ b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js @@ -1,10 +1,12 @@ import Cartesian3 from "../../Core/Cartesian3.js"; +import defaultValue from "../../Core/defaultValue.js"; import defined from "../../Core/defined.js"; import OrthographicFrustum from "../../Core/OrthographicFrustum.js"; import PrimitiveType from "../../Core/PrimitiveType.js"; import ShaderDestination from "../../Renderer/ShaderDestination.js"; import PointCloudAttenuationStageVS from "../../Shaders/ModelExperimental/PointCloudAttenuationStageVS.js"; import SceneMode from "../SceneMode.js"; +import ModelExperimentalType from "./ModelExperimentalType.js"; var PointCloudAttenuationPipelineStage = {}; PointCloudAttenuationPipelineStage.name = "PointCloudAttenuationPipelineStage"; // Helps with debugging @@ -31,11 +33,11 @@ PointCloudAttenuationPipelineStage.process = function ( var model = renderResources.model; var pointCloudShading; - - // If this is 3D Tiles, use the point cloud shading object - if (defined(model.content)) { - var tileset = model.content.tileset; - pointCloudShading = tileset.pointCloudShading; + var content; + var modelType = model.type; + if (ModelExperimentalType.is3DTiles(modelType)) { + content = model.content; + pointCloudShading = content.tileset.pointCloudShading; } else { pointCloudShading = model.pointCloudShading; } @@ -49,12 +51,12 @@ PointCloudAttenuationPipelineStage.process = function ( var scratch = scratchAttenuationUniform; scratch.x = pointCloudShading.attenuation - ? pointCloudShading.maximumAttenuation + ? defaultValue(pointCloudShading.maximumAttenuation, 1.0) : 1.0; scratch.x *= frameState.pixelRatio; if (pointCloudShading.attenuation) { - var context; + var context = frameState.context; var frustum = frameState.camera.frustum; var depthMultiplier; // Attenuation is maximumAttenuation in 2D/ortho @@ -69,9 +71,7 @@ PointCloudAttenuationPipelineStage.process = function ( frameState.camera.frustum.sseDenominator; } - // TODO: where to get the gE from? especially when not a part of a - // tileset? - var geometricError = 0; + var geometricError = getGeometricError(pointCloudShading, content); scratch.y = geometricError * pointCloudShading.geometricErrorScale; scratch.z = depthMultiplier; } @@ -80,4 +80,24 @@ PointCloudAttenuationPipelineStage.process = function ( }; }; +function getGeometricError(pointCloudShading, content) { + // 1. get tile's geometric error (if content is defined) + if (defined(content)) { + var geometricError = content._tile.geometricError; + + if (geometricError > 0) { + return geometricError; + } + } + + if (defined(pointCloudShading) && defined(pointCloudShading.baseResolution)) { + return pointCloudShading.baseResolution; + } + + // TODO: Waiting on another PR which has updates to the model matrix. + // estimate the geometric error. Originally it was done as + // cbrt(boundingVolume.volume() / pointsLength) + return 0.79; +} + export default PointCloudAttenuationPipelineStage; diff --git a/Source/Shaders/ModelExperimental/ModelExperimentalVS.glsl b/Source/Shaders/ModelExperimental/ModelExperimentalVS.glsl index 5c2e32a17126..41708ae3613e 100644 --- a/Source/Shaders/ModelExperimental/ModelExperimentalVS.glsl +++ b/Source/Shaders/ModelExperimental/ModelExperimentalVS.glsl @@ -47,6 +47,8 @@ void main() #ifdef PRIMITIVE_TYPE_POINTS #ifdef HAS_CUSTOM_VERTEX_SHADER gl_PointSize = vsOutput.pointSize; + #elif defined(USE_POINT_CLOUD_ATTENUATION) + gl_PointSize = pointCloudAttenuationStage(v_positionEC); #else gl_PointSize = 1.0; #endif From 717e778d71e4bad62fb6931090181e7e5de4c19c Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Mon, 10 Jan 2022 11:55:34 -0500 Subject: [PATCH 05/21] Add documentation comments --- .../PointCloudAttenuationPipelineStage.js | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js index 5b6202785368..d3800bdd5fa4 100644 --- a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js +++ b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js @@ -8,11 +8,30 @@ import PointCloudAttenuationStageVS from "../../Shaders/ModelExperimental/PointC import SceneMode from "../SceneMode.js"; import ModelExperimentalType from "./ModelExperimentalType.js"; +/** + * Stage to handle point cloud attenuation. + * + * @namespace PointCloudAttenuationPipelineStage + * + * @private + */ var PointCloudAttenuationPipelineStage = {}; PointCloudAttenuationPipelineStage.name = "PointCloudAttenuationPipelineStage"; // Helps with debugging var scratchAttenuationUniform = new Cartesian3(); +/** + * This stage does the following: + *
    + *
  • Add vertex shader code to compute attenuation and update gl_PointSize
  • + *
  • Updates the uniform map to pass in the point cloud attenuation parameters
  • + *
+ * @param {PrimitiveRenderResources} renderResources The render resources for this primitive + * @param {ModelComponents.primitive} primitive The primitive + * @param {FrameState} frameState The frame state + * + * @private + */ PointCloudAttenuationPipelineStage.process = function ( renderResources, primitive, @@ -81,7 +100,6 @@ PointCloudAttenuationPipelineStage.process = function ( }; function getGeometricError(pointCloudShading, content) { - // 1. get tile's geometric error (if content is defined) if (defined(content)) { var geometricError = content._tile.geometricError; From 91d84aa868dd90b2a82d6612a0fcf78a87dbdf26 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Mon, 10 Jan 2022 14:35:21 -0500 Subject: [PATCH 06/21] Start adding unit tests for attenuation --- .../ModelExperimentalPrimitive.js | 9 +- .../PointCloudAttenuationPipelineStage.js | 15 +- .../MaterialPipelineStageSpec.js | 2 +- .../ModelExperimentalPrimitiveSpec.js | 91 ++++++++++++- .../PointCloudAttenuationPipelineStageSpec.js | 128 ++++++++++++++++++ 5 files changed, 233 insertions(+), 12 deletions(-) create mode 100644 Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js diff --git a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js index 77a99a357b62..238cb1a41f10 100644 --- a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js +++ b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js @@ -4,12 +4,14 @@ import Check from "../../Core/Check.js"; import CustomShaderMode from "./CustomShaderMode.js"; import defaultValue from "../../Core/defaultValue.js"; import defined from "../../Core/defined.js"; +import PrimitiveType from "../../Core/PrimitiveType.js"; import FeatureIdPipelineStage from "./FeatureIdPipelineStage.js"; import CPUStylingPipelineStage from "./CPUStylingPipelineStage.js"; import DequantizationPipelineStage from "./DequantizationPipelineStage.js"; import GeometryPipelineStage from "./GeometryPipelineStage.js"; import LightingPipelineStage from "./LightingPipelineStage.js"; import MaterialPipelineStage from "./MaterialPipelineStage.js"; +import ModelExperimentalType from "./ModelExperimentalType.js"; import ModelExperimentalUtility from "./ModelExperimentalUtility.js"; import PickingPipelineStage from "./PickingPipelineStage.js"; import PointCloudAttenuationPipelineStage from "./PointCloudAttenuationPipelineStage.js"; @@ -118,12 +120,15 @@ function initialize(runtimePrimitive) { var hasQuantization = ModelExperimentalUtility.hasQuantizedAttributes( primitive.attributes ); - var is3DTiles = defined(model.content); + var is3DTiles = ModelExperimentalType.is3DTiles(model.type); var hasPointCloudShading = is3DTiles || defined(model.pointCloudShading); pipelineStages.push(GeometryPipelineStage); - if (hasPointCloudShading) { + if ( + hasPointCloudShading && + primitive.primitiveType === PrimitiveType.POINTS + ) { pipelineStages.push(PointCloudAttenuationPipelineStage); } diff --git a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js index d3800bdd5fa4..8aff97648e3e 100644 --- a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js +++ b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js @@ -2,7 +2,7 @@ import Cartesian3 from "../../Core/Cartesian3.js"; import defaultValue from "../../Core/defaultValue.js"; import defined from "../../Core/defined.js"; import OrthographicFrustum from "../../Core/OrthographicFrustum.js"; -import PrimitiveType from "../../Core/PrimitiveType.js"; + import ShaderDestination from "../../Renderer/ShaderDestination.js"; import PointCloudAttenuationStageVS from "../../Shaders/ModelExperimental/PointCloudAttenuationStageVS.js"; import SceneMode from "../SceneMode.js"; @@ -37,11 +37,6 @@ PointCloudAttenuationPipelineStage.process = function ( primitive, frameState ) { - // Attenuation only applies to point primitives - if (primitive.primitiveType !== PrimitiveType.POINTS) { - return; - } - var shaderBuilder = renderResources.shaderBuilder; shaderBuilder.addVertexLines([PointCloudAttenuationStageVS]); shaderBuilder.addDefine( @@ -53,8 +48,7 @@ PointCloudAttenuationPipelineStage.process = function ( var model = renderResources.model; var pointCloudShading; var content; - var modelType = model.type; - if (ModelExperimentalType.is3DTiles(modelType)) { + if (ModelExperimentalType.is3DTiles(model.type)) { content = model.content; pointCloudShading = content.tileset.pointCloudShading; } else { @@ -69,6 +63,7 @@ PointCloudAttenuationPipelineStage.process = function ( renderResources.uniformMap.model_pointCloudAttenuation = function () { var scratch = scratchAttenuationUniform; + // attenuation.x = pointSize scratch.x = pointCloudShading.attenuation ? defaultValue(pointCloudShading.maximumAttenuation, 1.0) : 1.0; @@ -78,6 +73,7 @@ PointCloudAttenuationPipelineStage.process = function ( var context = frameState.context; var frustum = frameState.camera.frustum; var depthMultiplier; + // Attenuation is maximumAttenuation in 2D/ortho if ( frameState.mode === SceneMode.SCENE2D || @@ -90,8 +86,11 @@ PointCloudAttenuationPipelineStage.process = function ( frameState.camera.frustum.sseDenominator; } + // attenuation.y = geometricError var geometricError = getGeometricError(pointCloudShading, content); scratch.y = geometricError * pointCloudShading.geometricErrorScale; + + // attenuation.z = depth multiplier scratch.z = depthMultiplier; } diff --git a/Specs/Scene/ModelExperimental/MaterialPipelineStageSpec.js b/Specs/Scene/ModelExperimental/MaterialPipelineStageSpec.js index 976ae1401699..f36f2116d370 100644 --- a/Specs/Scene/ModelExperimental/MaterialPipelineStageSpec.js +++ b/Specs/Scene/ModelExperimental/MaterialPipelineStageSpec.js @@ -655,5 +655,5 @@ describe( }); }); }, - "WEBGL" + "WebGL" ); diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js index 62312b42123d..503a7da4b0e6 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js @@ -8,7 +8,10 @@ import { GeometryPipelineStage, LightingPipelineStage, MaterialPipelineStage, + ModelExperimentalType, PickingPipelineStage, + PointCloudAttenuationPipelineStage, + PrimitiveType, VertexAttributeSemantic, BatchTexturePipelineStage, ModelExperimentalPrimitive, @@ -22,6 +25,7 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { }; var mockNode = {}; var mockModel = { + type: ModelExperimentalType.GLTF, allowPicking: true, featureIdAttributeIndex: 0, }; @@ -34,7 +38,7 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { function verifyExpectedStages(stages, expectedStages) { expect(stages.length, expectedStages.stages); for (var i = 0; i < stages.length; i++) { - expect(expectedStages[i].name).toEqual(stages[i].name); + expect(stages[i].name).toEqual(expectedStages[i].name); } } @@ -101,6 +105,7 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { primitive: mockPrimitive, node: mockNode, model: { + type: ModelExperimentalType.GLTF, allowPicking: false, content: {}, }, @@ -154,6 +159,7 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { }, node: {}, model: { + type: ModelExperimentalType.GLTF, allowPicking: true, featureIdAttributeIndex: 1, content: {}, @@ -181,6 +187,7 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { }, node: {}, model: { + type: ModelExperimentalType.GLTF, allowPicking: true, featureIdTextureIndex: 1, content: {}, @@ -224,6 +231,7 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { primitive: mockPrimitive, node: mockNode, model: { + type: ModelExperimentalType.GLTF, content: {}, customShader: new CustomShader({ vertexShaderText: emptyVertexShader, @@ -248,6 +256,7 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { primitive: mockPrimitive, node: mockNode, model: { + type: ModelExperimentalType.GLTF, content: {}, customShader: new CustomShader({ mode: CustomShaderMode.REPLACE_MATERIAL, @@ -272,6 +281,7 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { primitive: mockPrimitive, node: mockNode, model: { + type: ModelExperimentalType.GLTF, content: {}, customShader: new CustomShader({ mode: CustomShaderMode.REPLACE_MATERIAL, @@ -290,4 +300,83 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { verifyExpectedStages(primitive.pipelineStages, expectedStages); }); + + it("configures point cloud attenuation stage for 3D Tiles point clouds", function () { + var primitive = new ModelExperimentalPrimitive({ + primitive: { + featureIdAttributes: [], + featureIdTextures: [], + attributes: [], + primitiveType: PrimitiveType.POINTS, + }, + node: mockNode, + model: { + type: ModelExperimentalType.TILE_PNTS, + featureIdAttributeIndex: 0, + }, + }); + + var expectedStages = [ + GeometryPipelineStage, + PointCloudAttenuationPipelineStage, + MaterialPipelineStage, + LightingPipelineStage, + AlphaPipelineStage, + ]; + + verifyExpectedStages(primitive.pipelineStages, expectedStages); + }); + + it("configures point cloud attenuation stage for point clouds", function () { + var primitive = new ModelExperimentalPrimitive({ + primitive: { + featureIdAttributes: [], + featureIdTextures: [], + attributes: [], + primitiveType: PrimitiveType.POINTS, + }, + node: mockNode, + model: { + type: ModelExperimentalType.GLTF, + featureIdAttributeIndex: 0, + pointCloudShading: {}, + }, + }); + + var expectedStages = [ + GeometryPipelineStage, + PointCloudAttenuationPipelineStage, + MaterialPipelineStage, + LightingPipelineStage, + AlphaPipelineStage, + ]; + + verifyExpectedStages(primitive.pipelineStages, expectedStages); + }); + + it("skips point cloud attenuation if point cloud shading is not set", function () { + var primitive = new ModelExperimentalPrimitive({ + primitive: { + featureIdAttributes: [], + featureIdTextures: [], + attributes: [], + primitiveType: PrimitiveType.POINTS, + }, + node: mockNode, + model: { + type: ModelExperimentalType.GLTF, + featureIdAttributeIndex: 0, + pointCloudShading: undefined, + }, + }); + + var expectedStages = [ + GeometryPipelineStage, + MaterialPipelineStage, + LightingPipelineStage, + AlphaPipelineStage, + ]; + + verifyExpectedStages(primitive.pipelineStages, expectedStages); + }); }); diff --git a/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js b/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js new file mode 100644 index 000000000000..d32e3d6c20bc --- /dev/null +++ b/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js @@ -0,0 +1,128 @@ +import { + ModelExperimentalType, + PointCloudAttenuationPipelineStage, + PointCloudShading, + ShaderBuilder, +} from "../../../Source/Cesium.js"; +import createScene from "../../createScene.js"; +import ShaderBuilderTester from "../../ShaderBuilderTester.js"; + +describe( + "Scene/ModelExperimental/PointCloudAttenuationPipelineStage", + function () { + var scene; + var mockPrimitive = {}; + + beforeAll(function () { + scene = createScene(); + }); + + afterAll(function () { + scene.destroyForSpecs(); + }); + + it("adds uniform and define to the shader", function () { + var shaderBuilder = new ShaderBuilder(); + var uniformMap = {}; + var renderResources = { + shaderBuilder: shaderBuilder, + uniformMap: uniformMap, + model: { + type: ModelExperimentalType.GLTF, + }, + }; + + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + scene.frameState + ); + + ShaderBuilderTester.expectHasVertexDefines(shaderBuilder, [ + "USE_POINT_CLOUD_ATTENUATION", + ]); + ShaderBuilderTester.expectHasFragmentDefines(shaderBuilder, []); + ShaderBuilderTester.expectHasVertexUniforms(shaderBuilder, [ + "uniform vec3 model_pointCloudAttenuation;", + ]); + ShaderBuilderTester.expectHasFragmentUniforms(shaderBuilder, []); + expect(uniformMap.model_pointCloudAttenuation).toBeDefined(); + }); + + it("point size is set when attenuation is enabled", function () { + var uniformMap = {}; + var pointCloudShading = new PointCloudShading(); + pointCloudShading.attenuation = true; + pointCloudShading.maximumAttenuation = 4; + var renderResources = { + shaderBuilder: new ShaderBuilder(), + uniformMap: uniformMap, + model: { + type: ModelExperimentalType.GLTF, + pointCloudShading: pointCloudShading, + }, + }; + + var frameState = scene.frameState; + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + frameState + ); + + var attenuation = uniformMap.model_pointCloudAttenuation(); + expect(attenuation.x).toEqual(4 * frameState.pixelRatio); + }); + + it("point size is set to 1dp when attenuation is disabled", function () { + var uniformMap = {}; + var pointCloudShading = new PointCloudShading(); + pointCloudShading.attenuation = false; + pointCloudShading.maximumAttenuation = 4; + var renderResources = { + shaderBuilder: new ShaderBuilder(), + uniformMap: uniformMap, + model: { + type: ModelExperimentalType.GLTF, + pointCloudShading: pointCloudShading, + }, + }; + + var frameState = scene.frameState; + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + frameState + ); + + var attenuation = uniformMap.model_pointCloudAttenuation(); + expect(attenuation.x).toEqual(frameState.pixelRatio); + }); + + it("point size defaults to 1dp when maximumAttenuation is not defined", function () { + var uniformMap = {}; + var pointCloudShading = new PointCloudShading(); + pointCloudShading.attenuation = true; + pointCloudShading.maximumAttenuation = undefined; + var renderResources = { + shaderBuilder: new ShaderBuilder(), + uniformMap: uniformMap, + model: { + type: ModelExperimentalType.GLTF, + pointCloudShading: pointCloudShading, + }, + }; + + var frameState = scene.frameState; + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + frameState + ); + + var attenuation = uniformMap.model_pointCloudAttenuation(); + expect(attenuation.x).toEqual(frameState.pixelRatio); + }); + }, + "WebGL" +); From aeb1d9e43c3525872bc711281015f6aac0fc9091 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Mon, 10 Jan 2022 16:04:27 -0500 Subject: [PATCH 07/21] Refactor pipeline configuration --- .../ModelExperimentalNode.js | 23 ++++--- .../ModelExperimentalPrimitive.js | 33 +++++++--- .../ModelExperimentalSceneGraph.js | 61 ++++++++++++------- .../ModelExperimentalPrimitiveSpec.js | 4 ++ .../PrimitiveRenderResourcesSpec.js | 5 +- 5 files changed, 84 insertions(+), 42 deletions(-) diff --git a/Source/Scene/ModelExperimental/ModelExperimentalNode.js b/Source/Scene/ModelExperimental/ModelExperimentalNode.js index a4ff5c99fd13..33955b971fb2 100644 --- a/Source/Scene/ModelExperimental/ModelExperimentalNode.js +++ b/Source/Scene/ModelExperimental/ModelExperimentalNode.js @@ -72,7 +72,7 @@ export default function ModelExperimentalNode(options) { */ this.updateStages = []; - initialize(this); + this.configurePipeline(); } Object.defineProperties(ModelExperimentalNode.prototype, { @@ -191,19 +191,26 @@ ModelExperimentalNode.prototype.getChild = function (index) { return this.sceneGraph.runtimeNodes[this.children[index]]; }; -function initialize(runtimeNode) { - var node = runtimeNode.node; - var pipelineStages = runtimeNode.pipelineStages; - var updateStages = runtimeNode.updateStages; +/** + * Configure the node pipeline stages. If the pipeline needs to be re-run, call + * this method again to ensure the correct sequence of pipeline stages are + * used. + * + * @private + */ +ModelExperimentalNode.prototype.configurePipeline = function () { + var node = this.node; + var pipelineStages = this.pipelineStages; + pipelineStages.length = 0; + var updateStages = this.updateStages; + updateStages.length = 0; if (defined(node.instances)) { pipelineStages.push(InstancingPipelineStage); } updateStages.push(ModelMatrixUpdateStage); - - return; -} +}; /** * @private diff --git a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js index 238cb1a41f10..2a99579a9481 100644 --- a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js +++ b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js @@ -1,10 +1,11 @@ -import AlphaPipelineStage from "./AlphaPipelineStage.js"; -import BatchTexturePipelineStage from "./BatchTexturePipelineStage.js"; import Check from "../../Core/Check.js"; -import CustomShaderMode from "./CustomShaderMode.js"; import defaultValue from "../../Core/defaultValue.js"; import defined from "../../Core/defined.js"; import PrimitiveType from "../../Core/PrimitiveType.js"; +import AlphaPipelineStage from "./AlphaPipelineStage.js"; +import BatchTexturePipelineStage from "./BatchTexturePipelineStage.js"; +import CustomShaderMode from "./CustomShaderMode.js"; +import CustomShaderPipelineStage from "./CustomShaderPipelineStage.js"; import FeatureIdPipelineStage from "./FeatureIdPipelineStage.js"; import CPUStylingPipelineStage from "./CPUStylingPipelineStage.js"; import DequantizationPipelineStage from "./DequantizationPipelineStage.js"; @@ -100,15 +101,23 @@ export default function ModelExperimentalPrimitive(options) { */ this.updateStages = []; - initialize(this); + this.configurePipeline(); } -function initialize(runtimePrimitive) { - var pipelineStages = runtimePrimitive.pipelineStages; +/** + * Configure the primitive pipeline stages. If the pipeline needs to be re-run, call + * this method again to ensure the correct sequence of pipeline stages are + * used. + * + * @private + */ +ModelExperimentalPrimitive.prototype.configurePipeline = function () { + var pipelineStages = this.pipelineStages; + pipelineStages.length = 0; - var primitive = runtimePrimitive.primitive; - var node = runtimePrimitive.node; - var model = runtimePrimitive.model; + var primitive = this.primitive; + var node = this.node; + var model = this.model; var customShader = model.customShader; var hasCustomShader = defined(customShader); @@ -140,6 +149,10 @@ function initialize(runtimePrimitive) { pipelineStages.push(MaterialPipelineStage); } + if (defined(model.customShader)) { + pipelineStages.push(CustomShaderPipelineStage); + } + pipelineStages.push(LightingPipelineStage); // Add the FeatureIdPipelineStage and BatchTexturePipelineStage when the primitive has features, i.e. when at least one of the following conditions exists: @@ -186,4 +199,4 @@ function initialize(runtimePrimitive) { pipelineStages.push(AlphaPipelineStage); return; -} +}; diff --git a/Source/Scene/ModelExperimental/ModelExperimentalSceneGraph.js b/Source/Scene/ModelExperimental/ModelExperimentalSceneGraph.js index 9b9a6f60e4f7..45806760ca41 100644 --- a/Source/Scene/ModelExperimental/ModelExperimentalSceneGraph.js +++ b/Source/Scene/ModelExperimental/ModelExperimentalSceneGraph.js @@ -4,8 +4,6 @@ import Check from "../../Core/Check.js"; import defaultValue from "../../Core/defaultValue.js"; import defined from "../../Core/defined.js"; import Matrix4 from "../../Core/Matrix4.js"; -import CustomShaderPipelineStage from "./CustomShaderPipelineStage.js"; -import LightingPipelineStage from "./LightingPipelineStage.js"; import ModelColorPipelineStage from "./ModelColorPipelineStage.js"; import ModelExperimentalPrimitive from "./ModelExperimentalPrimitive.js"; import ModelExperimentalNode from "./ModelExperimentalNode.js"; @@ -109,6 +107,18 @@ export default function ModelExperimentalSceneGraph(options) { */ this._boundingSpheres = []; + /** + * Pipeline stages to apply to this model. This + * is an array of classes, each with a static method called + * process() + * + * @type {Object[]} + * @readonly + * + * @private + */ + this.modelPipelineStages = []; + this._boundingSphere = undefined; this._computedModelMatrix = Matrix4.clone(this._model.modelMatrix); @@ -250,12 +260,10 @@ ModelExperimentalSceneGraph.prototype.buildDrawCommands = function ( ) { var modelRenderResources = new ModelRenderResources(this._model); - var modelPipelineStages = []; - var model = this._model; - if (defined(model.color)) { - modelPipelineStages.push(ModelColorPipelineStage); - } + this.configurePipeline(); + var modelPipelineStages = this.modelPipelineStages; + var model = this.model; var i, j, k; for (i = 0; i < modelPipelineStages.length; i++) { var modelPipelineStage = modelPipelineStages[i]; @@ -264,14 +272,16 @@ ModelExperimentalSceneGraph.prototype.buildDrawCommands = function ( for (i = 0; i < this._runtimeNodes.length; i++) { var runtimeNode = this._runtimeNodes[i]; + runtimeNode.configurePipeline(); + var nodePipelineStages = runtimeNode.pipelineStages; var nodeRenderResources = new NodeRenderResources( modelRenderResources, runtimeNode ); - for (j = 0; j < runtimeNode.pipelineStages.length; j++) { - var nodePipelineStage = runtimeNode.pipelineStages[j]; + for (j = 0; j < nodePipelineStages.length; j++) { + var nodePipelineStage = nodePipelineStages[j]; nodePipelineStage.process( nodeRenderResources, @@ -283,20 +293,8 @@ ModelExperimentalSceneGraph.prototype.buildDrawCommands = function ( for (j = 0; j < runtimeNode.runtimePrimitives.length; j++) { var runtimePrimitive = runtimeNode.runtimePrimitives[j]; - // The pipeline stage array is copied because we don't want dynamic stages to be added to the primitive's default stages. - var primitivePipelineStages = runtimePrimitive.pipelineStages.slice(); - - if (defined(model.customShader)) { - // The custom shader stage needs to go before the lighting stage. - var lightingStageIndex = primitivePipelineStages.indexOf( - LightingPipelineStage - ); - primitivePipelineStages.splice( - lightingStageIndex, - 0, - CustomShaderPipelineStage - ); - } + runtimePrimitive.configurePipeline(); + var primitivePipelineStages = runtimePrimitive.pipelineStages; var primitiveRenderResources = new PrimitiveRenderResources( nodeRenderResources, @@ -331,6 +329,23 @@ ModelExperimentalSceneGraph.prototype.buildDrawCommands = function ( ); }; +/** + * Configure the model pipeline stages. If the pipeline needs to be re-run, call + * this method again to ensure the correct sequence of pipeline stages are + * used. + * + * @private + */ +ModelExperimentalSceneGraph.prototype.configurePipeline = function () { + var modelPipelineStages = this.modelPipelineStages; + modelPipelineStages.length = 0; + + var model = this._model; + if (defined(model.color)) { + modelPipelineStages.push(ModelColorPipelineStage); + } +}; + ModelExperimentalSceneGraph.prototype.update = function (frameState) { var i, j, k; diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js index 503a7da4b0e6..9fb5a0372afc 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js @@ -2,6 +2,7 @@ import { AlphaPipelineStage, CustomShader, CustomShaderMode, + CustomShaderPipelineStage, FeatureIdPipelineStage, CPUStylingPipelineStage, DequantizationPipelineStage, @@ -244,6 +245,7 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { var expectedStages = [ GeometryPipelineStage, MaterialPipelineStage, + CustomShaderPipelineStage, LightingPipelineStage, AlphaPipelineStage, ]; @@ -269,6 +271,7 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { var expectedStages = [ GeometryPipelineStage, + CustomShaderPipelineStage, LightingPipelineStage, AlphaPipelineStage, ]; @@ -294,6 +297,7 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { var expectedStages = [ GeometryPipelineStage, MaterialPipelineStage, + CustomShaderPipelineStage, LightingPipelineStage, AlphaPipelineStage, ]; diff --git a/Specs/Scene/ModelExperimental/PrimitiveRenderResourcesSpec.js b/Specs/Scene/ModelExperimental/PrimitiveRenderResourcesSpec.js index 06ed00a9b3e0..752b49e9736d 100644 --- a/Specs/Scene/ModelExperimental/PrimitiveRenderResourcesSpec.js +++ b/Specs/Scene/ModelExperimental/PrimitiveRenderResourcesSpec.js @@ -9,6 +9,7 @@ import { Matrix4, ModelExperimentalNode, ModelExperimentalPrimitive, + ModelExperimentalType, PrimitiveType, ModelRenderResources, NodeRenderResources, @@ -17,7 +18,9 @@ import { } from "../../../Source/Cesium.js"; describe("Scene/ModelExperimental/PrimitiveRenderResources", function () { - var mockModel = {}; + var mockModel = { + type: ModelExperimentalType.GLTF, + }; var mockNode = {}; var mockSceneGraph = { computedModelMatrix: Matrix4.IDENTITY, From 3be5e392a6912e2bde621adea457d0c86ddf4e12 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Mon, 10 Jan 2022 16:21:57 -0500 Subject: [PATCH 08/21] Move attenuation check to pipeline configuration --- .../ModelExperimentalPrimitive.js | 16 ++++--- .../PointCloudAttenuationPipelineStage.js | 45 +++++++++---------- .../ModelExperimentalPrimitiveSpec.js | 43 +++++++++++++++++- 3 files changed, 73 insertions(+), 31 deletions(-) diff --git a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js index 2a99579a9481..2301e38979c6 100644 --- a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js +++ b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js @@ -129,15 +129,19 @@ ModelExperimentalPrimitive.prototype.configurePipeline = function () { var hasQuantization = ModelExperimentalUtility.hasQuantizedAttributes( primitive.attributes ); - var is3DTiles = ModelExperimentalType.is3DTiles(model.type); - var hasPointCloudShading = is3DTiles || defined(model.pointCloudShading); + + var pointCloudShading; + if (ModelExperimentalType.is3DTiles(model.type)) { + pointCloudShading = model.content.tileset.pointCloudShading; + } else { + pointCloudShading = model.pointCloudShading; + } + var hasAttenuation = + defined(pointCloudShading) && pointCloudShading.attenuation; pipelineStages.push(GeometryPipelineStage); - if ( - hasPointCloudShading && - primitive.primitiveType === PrimitiveType.POINTS - ) { + if (hasAttenuation && primitive.primitiveType === PrimitiveType.POINTS) { pipelineStages.push(PointCloudAttenuationPipelineStage); } diff --git a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js index 8aff97648e3e..96f9eb076182 100644 --- a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js +++ b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js @@ -69,31 +69,28 @@ PointCloudAttenuationPipelineStage.process = function ( : 1.0; scratch.x *= frameState.pixelRatio; - if (pointCloudShading.attenuation) { - var context = frameState.context; - var frustum = frameState.camera.frustum; - var depthMultiplier; - - // Attenuation is maximumAttenuation in 2D/ortho - if ( - frameState.mode === SceneMode.SCENE2D || - frustum instanceof OrthographicFrustum - ) { - depthMultiplier = Number.POSITIVE_INFINITY; - } else { - depthMultiplier = - context.drawingBufferHeight / - frameState.camera.frustum.sseDenominator; - } - - // attenuation.y = geometricError - var geometricError = getGeometricError(pointCloudShading, content); - scratch.y = geometricError * pointCloudShading.geometricErrorScale; - - // attenuation.z = depth multiplier - scratch.z = depthMultiplier; + var context = frameState.context; + var frustum = frameState.camera.frustum; + var depthMultiplier; + + // Attenuation is maximumAttenuation in 2D/ortho + if ( + frameState.mode === SceneMode.SCENE2D || + frustum instanceof OrthographicFrustum + ) { + depthMultiplier = Number.POSITIVE_INFINITY; + } else { + depthMultiplier = + context.drawingBufferHeight / frameState.camera.frustum.sseDenominator; } + // attenuation.y = geometricError + var geometricError = getGeometricError(pointCloudShading, content); + scratch.y = geometricError * pointCloudShading.geometricErrorScale; + + // attenuation.z = depth multiplier + scratch.z = depthMultiplier; + return scratch; }; }; @@ -107,7 +104,7 @@ function getGeometricError(pointCloudShading, content) { } } - if (defined(pointCloudShading) && defined(pointCloudShading.baseResolution)) { + if (defined(pointCloudShading.baseResolution)) { return pointCloudShading.baseResolution; } diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js index 9fb5a0372afc..9727b6ab11c9 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js @@ -12,6 +12,7 @@ import { ModelExperimentalType, PickingPipelineStage, PointCloudAttenuationPipelineStage, + PointCloudShading, PrimitiveType, VertexAttributeSemantic, BatchTexturePipelineStage, @@ -306,6 +307,9 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { }); it("configures point cloud attenuation stage for 3D Tiles point clouds", function () { + var pointCloudShading = new PointCloudShading({ + attenuation: true, + }); var primitive = new ModelExperimentalPrimitive({ primitive: { featureIdAttributes: [], @@ -317,6 +321,11 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { model: { type: ModelExperimentalType.TILE_PNTS, featureIdAttributeIndex: 0, + content: { + tileset: { + pointCloudShading: pointCloudShading, + }, + }, }, }); @@ -332,6 +341,9 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { }); it("configures point cloud attenuation stage for point clouds", function () { + var pointCloudShading = new PointCloudShading({ + attenuation: true, + }); var primitive = new ModelExperimentalPrimitive({ primitive: { featureIdAttributes: [], @@ -343,7 +355,7 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { model: { type: ModelExperimentalType.GLTF, featureIdAttributeIndex: 0, - pointCloudShading: {}, + pointCloudShading: pointCloudShading, }, }); @@ -358,6 +370,35 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { verifyExpectedStages(primitive.pipelineStages, expectedStages); }); + it("skips point cloud attenuation if attenuation is false", function () { + var pointCloudShading = new PointCloudShading({ + attenuation: false, + }); + var primitive = new ModelExperimentalPrimitive({ + primitive: { + featureIdAttributes: [], + featureIdTextures: [], + attributes: [], + primitiveType: PrimitiveType.POINTS, + }, + node: mockNode, + model: { + type: ModelExperimentalType.GLTF, + featureIdAttributeIndex: 0, + pointCloudShading: pointCloudShading, + }, + }); + + var expectedStages = [ + GeometryPipelineStage, + MaterialPipelineStage, + LightingPipelineStage, + AlphaPipelineStage, + ]; + + verifyExpectedStages(primitive.pipelineStages, expectedStages); + }); + it("skips point cloud attenuation if point cloud shading is not set", function () { var primitive = new ModelExperimentalPrimitive({ primitive: { From d49d78760ff8f598c03116e700db81578674bffb Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Mon, 10 Jan 2022 16:35:21 -0500 Subject: [PATCH 09/21] Update attenuation tests --- .../PointCloudAttenuationPipelineStage.js | 10 ++--- .../PointCloudAttenuationPipelineStageSpec.js | 41 ++++--------------- 2 files changed, 14 insertions(+), 37 deletions(-) diff --git a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js index 96f9eb076182..4b3922d9e99f 100644 --- a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js +++ b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js @@ -9,7 +9,9 @@ import SceneMode from "../SceneMode.js"; import ModelExperimentalType from "./ModelExperimentalType.js"; /** - * Stage to handle point cloud attenuation. + * Stage to handle point cloud attenuation. This stage assumes that either + * tileset.pointCloudShading.attenuation (3D Tiles) or + * model.pointCloudShading.attenuation (individual model) is true * * @namespace PointCloudAttenuationPipelineStage * @@ -45,9 +47,9 @@ PointCloudAttenuationPipelineStage.process = function ( ShaderDestination.VERTEX ); - var model = renderResources.model; var pointCloudShading; var content; + var model = renderResources.model; if (ModelExperimentalType.is3DTiles(model.type)) { content = model.content; pointCloudShading = content.tileset.pointCloudShading; @@ -64,9 +66,7 @@ PointCloudAttenuationPipelineStage.process = function ( var scratch = scratchAttenuationUniform; // attenuation.x = pointSize - scratch.x = pointCloudShading.attenuation - ? defaultValue(pointCloudShading.maximumAttenuation, 1.0) - : 1.0; + scratch.x = defaultValue(pointCloudShading.maximumAttenuation, 1.0); scratch.x *= frameState.pixelRatio; var context = frameState.context; diff --git a/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js b/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js index d32e3d6c20bc..0e75a100c0ae 100644 --- a/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js +++ b/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js @@ -49,11 +49,12 @@ describe( expect(uniformMap.model_pointCloudAttenuation).toBeDefined(); }); - it("point size is set when attenuation is enabled", function () { + it("point size is determined by maximumAttenuation", function () { var uniformMap = {}; - var pointCloudShading = new PointCloudShading(); - pointCloudShading.attenuation = true; - pointCloudShading.maximumAttenuation = 4; + var pointCloudShading = new PointCloudShading({ + attenuation: true, + maximumAttenuation: 4, + }); var renderResources = { shaderBuilder: new ShaderBuilder(), uniformMap: uniformMap, @@ -74,36 +75,12 @@ describe( expect(attenuation.x).toEqual(4 * frameState.pixelRatio); }); - it("point size is set to 1dp when attenuation is disabled", function () { - var uniformMap = {}; - var pointCloudShading = new PointCloudShading(); - pointCloudShading.attenuation = false; - pointCloudShading.maximumAttenuation = 4; - var renderResources = { - shaderBuilder: new ShaderBuilder(), - uniformMap: uniformMap, - model: { - type: ModelExperimentalType.GLTF, - pointCloudShading: pointCloudShading, - }, - }; - - var frameState = scene.frameState; - PointCloudAttenuationPipelineStage.process( - renderResources, - mockPrimitive, - frameState - ); - - var attenuation = uniformMap.model_pointCloudAttenuation(); - expect(attenuation.x).toEqual(frameState.pixelRatio); - }); - it("point size defaults to 1dp when maximumAttenuation is not defined", function () { var uniformMap = {}; - var pointCloudShading = new PointCloudShading(); - pointCloudShading.attenuation = true; - pointCloudShading.maximumAttenuation = undefined; + var pointCloudShading = new PointCloudShading({ + attenuation: true, + maximumAttenuation: undefined, + }); var renderResources = { shaderBuilder: new ShaderBuilder(), uniformMap: uniformMap, From 7b832c8c824dd08427c96e8d938c9255ff68080a Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Tue, 11 Jan 2022 11:40:16 -0500 Subject: [PATCH 10/21] Add specs for depth multiplier --- .../PointCloudAttenuationPipelineStage.js | 10 +- .../PointCloudAttenuationPipelineStageSpec.js | 131 ++++++++++++++++++ 2 files changed, 136 insertions(+), 5 deletions(-) diff --git a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js index 4b3922d9e99f..69a91a4c30fb 100644 --- a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js +++ b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js @@ -69,6 +69,10 @@ PointCloudAttenuationPipelineStage.process = function ( scratch.x = defaultValue(pointCloudShading.maximumAttenuation, 1.0); scratch.x *= frameState.pixelRatio; + // attenuation.y = geometricError + var geometricError = getGeometricError(pointCloudShading, content); + scratch.y = geometricError * pointCloudShading.geometricErrorScale; + var context = frameState.context; var frustum = frameState.camera.frustum; var depthMultiplier; @@ -84,10 +88,6 @@ PointCloudAttenuationPipelineStage.process = function ( context.drawingBufferHeight / frameState.camera.frustum.sseDenominator; } - // attenuation.y = geometricError - var geometricError = getGeometricError(pointCloudShading, content); - scratch.y = geometricError * pointCloudShading.geometricErrorScale; - // attenuation.z = depth multiplier scratch.z = depthMultiplier; @@ -97,7 +97,7 @@ PointCloudAttenuationPipelineStage.process = function ( function getGeometricError(pointCloudShading, content) { if (defined(content)) { - var geometricError = content._tile.geometricError; + var geometricError = content.tile.geometricError; if (geometricError > 0) { return geometricError; diff --git a/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js b/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js index 0e75a100c0ae..8f1e5242c256 100644 --- a/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js +++ b/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js @@ -1,5 +1,7 @@ import { + Camera, ModelExperimentalType, + OrthographicFrustum, PointCloudAttenuationPipelineStage, PointCloudShading, ShaderBuilder, @@ -21,6 +23,12 @@ describe( scene.destroyForSpecs(); }); + beforeEach(function () { + scene.morphTo3D(0.0); + scene.camera = new Camera(scene); + scene.renderForSpecs(); + }); + it("adds uniform and define to the shader", function () { var shaderBuilder = new ShaderBuilder(); var uniformMap = {}; @@ -49,6 +57,44 @@ describe( expect(uniformMap.model_pointCloudAttenuation).toBeDefined(); }); + it("uses tileset.pointCloudShading for 3D Tiles", function () { + var uniformMap = {}; + var pointCloudShading1dp = new PointCloudShading({ + attenuation: true, + maximumAttenuation: 1, + }); + var pointCloudShading4dp = new PointCloudShading({ + attenuation: true, + maximumAttenuation: 4, + }); + var renderResources = { + shaderBuilder: new ShaderBuilder(), + uniformMap: uniformMap, + model: { + type: ModelExperimentalType.TILE_GLTF, + content: { + tile: { + geometricError: 3, + }, + tileset: { + pointCloudShading: pointCloudShading1dp, + }, + }, + pointCloudShading: pointCloudShading4dp, + }, + }; + + var frameState = scene.frameState; + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + frameState + ); + + var attenuation = uniformMap.model_pointCloudAttenuation(); + expect(attenuation.x).toEqual(1 * frameState.pixelRatio); + }); + it("point size is determined by maximumAttenuation", function () { var uniformMap = {}; var pointCloudShading = new PointCloudShading({ @@ -100,6 +146,91 @@ describe( var attenuation = uniformMap.model_pointCloudAttenuation(); expect(attenuation.x).toEqual(frameState.pixelRatio); }); + + it("computes depth multiplier from drawing buffer and frustum", function () { + var uniformMap = {}; + var pointCloudShading = new PointCloudShading({ + attenuation: true, + }); + var renderResources = { + shaderBuilder: new ShaderBuilder(), + uniformMap: uniformMap, + model: { + type: ModelExperimentalType.GLTF, + pointCloudShading: pointCloudShading, + }, + }; + + var frameState = scene.frameState; + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + frameState + ); + + var attenuation = uniformMap.model_pointCloudAttenuation(); + var expected = + scene.context.drawingBufferHeight / scene.camera.frustum.sseDenominator; + expect(attenuation.z).toEqual(expected); + }); + + it("depth multiplier is set to positive infinity when in 2D mode", function () { + scene.morphTo2D(0.0); + scene.renderForSpecs(); + var uniformMap = {}; + var pointCloudShading = new PointCloudShading({ + attenuation: true, + }); + var renderResources = { + shaderBuilder: new ShaderBuilder(), + uniformMap: uniformMap, + model: { + type: ModelExperimentalType.GLTF, + pointCloudShading: pointCloudShading, + }, + }; + + var frameState = scene.frameState; + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + frameState + ); + + var attenuation = uniformMap.model_pointCloudAttenuation(); + expect(attenuation.z).toEqual(Number.POSITIVE_INFINITY); + }); + + it("depth multiplier is set to positive infinity when the camera uses orthographic projection", function () { + var camera = scene.camera; + camera.frustum = new OrthographicFrustum(); + camera.frustum.aspectRatio = + scene.drawingBufferWidth / scene.drawingBufferHeight; + camera.frustum.width = camera.positionCartographic.height; + scene.renderForSpecs(); + var uniformMap = {}; + var pointCloudShading = new PointCloudShading({ + attenuation: true, + }); + var renderResources = { + shaderBuilder: new ShaderBuilder(), + uniformMap: uniformMap, + model: { + type: ModelExperimentalType.GLTF, + pointCloudShading: pointCloudShading, + }, + }; + + var frameState = scene.frameState; + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + frameState + ); + + var attenuation = uniformMap.model_pointCloudAttenuation(); + expect(attenuation.z).toBe(Number.POSITIVE_INFINITY); + }); }, "WebGL" ); From cd59f1c5a55d7c1fe7cf49de1c7fffcf1af22beb Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Tue, 11 Jan 2022 11:49:09 -0500 Subject: [PATCH 11/21] Start testing geometric error --- .../PointCloudAttenuationPipelineStageSpec.js | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js b/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js index 8f1e5242c256..d51ccfa0cc17 100644 --- a/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js +++ b/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js @@ -147,6 +147,160 @@ describe( expect(attenuation.x).toEqual(frameState.pixelRatio); }); + it("scales geometricError", function () { + var uniformMap = {}; + var pointCloudShading = new PointCloudShading({ + attenuation: true, + geometricErrorScale: 2, + }); + var renderResources = { + shaderBuilder: new ShaderBuilder(), + uniformMap: uniformMap, + model: { + type: ModelExperimentalType.TILE_GLTF, + content: { + tile: { + geometricError: 3, + }, + tileset: { + pointCloudShading: pointCloudShading, + }, + }, + }, + }; + + var frameState = scene.frameState; + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + frameState + ); + + var attenuation = uniformMap.model_pointCloudAttenuation(); + expect(attenuation.y).toEqual(6); + }); + + it("uses tile geometric error when available", function () { + var uniformMap = {}; + var pointCloudShading = new PointCloudShading({ + attenuation: true, + geometricErrorScale: 1, + }); + var renderResources = { + shaderBuilder: new ShaderBuilder(), + uniformMap: uniformMap, + model: { + type: ModelExperimentalType.TILE_GLTF, + content: { + tile: { + geometricError: 3, + }, + tileset: { + pointCloudShading: pointCloudShading, + }, + }, + }, + }; + + var frameState = scene.frameState; + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + frameState + ); + + var attenuation = uniformMap.model_pointCloudAttenuation(); + expect(attenuation.y).toEqual(3); + }); + + it("uses baseResolution when tile geometric error is 0", function () { + var uniformMap = {}; + var pointCloudShading = new PointCloudShading({ + attenuation: true, + geometricErrorScale: 1, + baseResolution: 4, + }); + var renderResources = { + shaderBuilder: new ShaderBuilder(), + uniformMap: uniformMap, + model: { + type: ModelExperimentalType.TILE_GLTF, + content: { + tile: { + geometricError: 0, + }, + tileset: { + pointCloudShading: pointCloudShading, + }, + }, + }, + }; + + var frameState = scene.frameState; + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + frameState + ); + + var attenuation = uniformMap.model_pointCloudAttenuation(); + expect(attenuation.y).toEqual(4); + }); + + it("uses baseResolution for glTF models", function () { + var uniformMap = {}; + var pointCloudShading = new PointCloudShading({ + attenuation: true, + geometricErrorScale: 1, + baseResolution: 4, + }); + var renderResources = { + shaderBuilder: new ShaderBuilder(), + uniformMap: uniformMap, + model: { + type: ModelExperimentalType.GLTF, + pointCloudShading: pointCloudShading, + }, + }; + + var frameState = scene.frameState; + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + frameState + ); + + var attenuation = uniformMap.model_pointCloudAttenuation(); + expect(attenuation.y).toEqual(4); + }); + + it("estimates geometric error when baseResolution is not available", function () { + var uniformMap = {}; + var pointCloudShading = new PointCloudShading({ + attenuation: true, + geometricErrorScale: 1, + baseResolution: undefined, + }); + var renderResources = { + shaderBuilder: new ShaderBuilder(), + uniformMap: uniformMap, + model: { + type: ModelExperimentalType.GLTF, + pointCloudShading: pointCloudShading, + }, + }; + + var frameState = scene.frameState; + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + frameState + ); + + var attenuation = uniformMap.model_pointCloudAttenuation(); + expect(attenuation.y).toEqual(100); + }); + it("computes depth multiplier from drawing buffer and frustum", function () { var uniformMap = {}; var pointCloudShading = new PointCloudShading({ From 16d79277cd02ec6b09a3155643971d21cc8b341f Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Tue, 11 Jan 2022 13:56:24 -0500 Subject: [PATCH 12/21] Unit test geometric error --- Source/Scene/ModelExperimental/PntsLoader.js | 1 + .../PointCloudAttenuationPipelineStage.js | 47 ++++++++++++++++--- .../PointCloudAttenuationPipelineStageSpec.js | 35 +++++++++++++- 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/Source/Scene/ModelExperimental/PntsLoader.js b/Source/Scene/ModelExperimental/PntsLoader.js index b2ade810b718..4b0a94f7ea8c 100644 --- a/Source/Scene/ModelExperimental/PntsLoader.js +++ b/Source/Scene/ModelExperimental/PntsLoader.js @@ -442,6 +442,7 @@ function makeAttributes(loader, parsedContent, context) { if (defined(positions)) { computeApproximateExtrema(positions); attribute = makeAttribute(loader, positions, context); + attribute.count = parsedContent.pointsLength; attributes.push(attribute); } diff --git a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js index 69a91a4c30fb..87c39f3a6e0d 100644 --- a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js +++ b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js @@ -1,12 +1,15 @@ import Cartesian3 from "../../Core/Cartesian3.js"; +import CesiumMath from "../../Core/Math.js"; import defaultValue from "../../Core/defaultValue.js"; import defined from "../../Core/defined.js"; +import Matrix4 from "../../Core/Matrix4.js"; import OrthographicFrustum from "../../Core/OrthographicFrustum.js"; - import ShaderDestination from "../../Renderer/ShaderDestination.js"; import PointCloudAttenuationStageVS from "../../Shaders/ModelExperimental/PointCloudAttenuationStageVS.js"; import SceneMode from "../SceneMode.js"; +import VertexAttributeSemantic from "../VertexAttributeSemantic.js"; import ModelExperimentalType from "./ModelExperimentalType.js"; +import ModelExperimentalUtility from "./ModelExperimentalUtility.js"; /** * Stage to handle point cloud attenuation. This stage assumes that either @@ -70,7 +73,12 @@ PointCloudAttenuationPipelineStage.process = function ( scratch.x *= frameState.pixelRatio; // attenuation.y = geometricError - var geometricError = getGeometricError(pointCloudShading, content); + var geometricError = getGeometricError( + renderResources, + primitive, + pointCloudShading, + content + ); scratch.y = geometricError * pointCloudShading.geometricErrorScale; var context = frameState.context; @@ -95,7 +103,13 @@ PointCloudAttenuationPipelineStage.process = function ( }; }; -function getGeometricError(pointCloudShading, content) { +var scratchDimensions = new Cartesian3(); +function getGeometricError( + renderResources, + primitive, + pointCloudShading, + content +) { if (defined(content)) { var geometricError = content.tile.geometricError; @@ -108,10 +122,29 @@ function getGeometricError(pointCloudShading, content) { return pointCloudShading.baseResolution; } - // TODO: Waiting on another PR which has updates to the model matrix. - // estimate the geometric error. Originally it was done as - // cbrt(boundingVolume.volume() / pointsLength) - return 0.79; + var positionAttribute = ModelExperimentalUtility.getAttributeBySemantic( + primitive, + VertexAttributeSemantic.POSITION + ); + var pointsLength = positionAttribute.count; + + // Estimate the geometric error + var nodeTransform = renderResources.runtimeNode.transform; + var dimensions = Cartesian3.clone(positionAttribute.max, scratchDimensions); + dimensions = Cartesian3.subtract( + dimensions, + positionAttribute.min, + scratchDimensions + ); + // dimensions is a vector, as (point - point) = displacement vector + dimensions = Matrix4.multiplyByPointAsVector( + nodeTransform, + dimensions, + scratchDimensions + ); + var volume = dimensions.x * dimensions.y * dimensions.z; + var geometricErrorEstimate = CesiumMath.cbrt(volume / pointsLength); + return geometricErrorEstimate; } export default PointCloudAttenuationPipelineStage; diff --git a/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js b/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js index d51ccfa0cc17..6f1e854c30ed 100644 --- a/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js +++ b/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js @@ -1,10 +1,14 @@ import { Camera, + Cartesian3, + Math as CesiumMath, + Matrix4, ModelExperimentalType, OrthographicFrustum, PointCloudAttenuationPipelineStage, PointCloudShading, ShaderBuilder, + VertexAttributeSemantic, } from "../../../Source/Cesium.js"; import createScene from "../../createScene.js"; import ShaderBuilderTester from "../../ShaderBuilderTester.js"; @@ -13,7 +17,20 @@ describe( "Scene/ModelExperimental/PointCloudAttenuationPipelineStage", function () { var scene; - var mockPrimitive = {}; + var mockPrimitive = { + attributes: [ + { + semantic: VertexAttributeSemantic.POSITION, + min: new Cartesian3(0, 0, 0), + max: new Cartesian3(1, 1, 1), + count: 64, + }, + ], + }; + + var mockRuntimeNode = { + transform: new Matrix4(2, 0, 0, 1, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1), + }; beforeAll(function () { scene = createScene(); @@ -35,6 +52,7 @@ describe( var renderResources = { shaderBuilder: shaderBuilder, uniformMap: uniformMap, + runtimeNode: mockRuntimeNode, model: { type: ModelExperimentalType.GLTF, }, @@ -70,6 +88,7 @@ describe( var renderResources = { shaderBuilder: new ShaderBuilder(), uniformMap: uniformMap, + runtimeNode: mockRuntimeNode, model: { type: ModelExperimentalType.TILE_GLTF, content: { @@ -104,6 +123,7 @@ describe( var renderResources = { shaderBuilder: new ShaderBuilder(), uniformMap: uniformMap, + runtimeNode: mockRuntimeNode, model: { type: ModelExperimentalType.GLTF, pointCloudShading: pointCloudShading, @@ -130,6 +150,7 @@ describe( var renderResources = { shaderBuilder: new ShaderBuilder(), uniformMap: uniformMap, + runtimeNode: mockRuntimeNode, model: { type: ModelExperimentalType.GLTF, pointCloudShading: pointCloudShading, @@ -189,6 +210,7 @@ describe( var renderResources = { shaderBuilder: new ShaderBuilder(), uniformMap: uniformMap, + runtimeNode: mockRuntimeNode, model: { type: ModelExperimentalType.TILE_GLTF, content: { @@ -223,6 +245,7 @@ describe( var renderResources = { shaderBuilder: new ShaderBuilder(), uniformMap: uniformMap, + runtimeNode: mockRuntimeNode, model: { type: ModelExperimentalType.TILE_GLTF, content: { @@ -257,6 +280,7 @@ describe( var renderResources = { shaderBuilder: new ShaderBuilder(), uniformMap: uniformMap, + runtimeNode: mockRuntimeNode, model: { type: ModelExperimentalType.GLTF, pointCloudShading: pointCloudShading, @@ -284,6 +308,7 @@ describe( var renderResources = { shaderBuilder: new ShaderBuilder(), uniformMap: uniformMap, + runtimeNode: mockRuntimeNode, model: { type: ModelExperimentalType.GLTF, pointCloudShading: pointCloudShading, @@ -298,7 +323,10 @@ describe( ); var attenuation = uniformMap.model_pointCloudAttenuation(); - expect(attenuation.y).toEqual(100); + var volume = 8; + var pointsLength = 64; + var expected = CesiumMath.cbrt(volume / pointsLength); + expect(attenuation.y).toEqual(expected); }); it("computes depth multiplier from drawing buffer and frustum", function () { @@ -309,6 +337,7 @@ describe( var renderResources = { shaderBuilder: new ShaderBuilder(), uniformMap: uniformMap, + runtimeNode: mockRuntimeNode, model: { type: ModelExperimentalType.GLTF, pointCloudShading: pointCloudShading, @@ -338,6 +367,7 @@ describe( var renderResources = { shaderBuilder: new ShaderBuilder(), uniformMap: uniformMap, + runtimeNode: mockRuntimeNode, model: { type: ModelExperimentalType.GLTF, pointCloudShading: pointCloudShading, @@ -369,6 +399,7 @@ describe( var renderResources = { shaderBuilder: new ShaderBuilder(), uniformMap: uniformMap, + runtimeNode: mockRuntimeNode, model: { type: ModelExperimentalType.GLTF, pointCloudShading: pointCloudShading, From 5e7d9ffc743e66a37727f3dacde93bf43acb5504 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Tue, 11 Jan 2022 14:44:39 -0500 Subject: [PATCH 13/21] Forward point cloud shading from tileset --- .../ModelExperimental/ModelExperimental.js | 30 ++++++++++- .../ModelExperimental3DTileContent.js | 1 + .../PointCloudAttenuationPipelineStage.js | 10 ++-- .../PointCloudAttenuationPipelineStageSpec.js | 51 ++----------------- 4 files changed, 37 insertions(+), 55 deletions(-) diff --git a/Source/Scene/ModelExperimental/ModelExperimental.js b/Source/Scene/ModelExperimental/ModelExperimental.js index 036f2692ffb5..1c7f14a74363 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental.js +++ b/Source/Scene/ModelExperimental/ModelExperimental.js @@ -1,4 +1,5 @@ import Check from "../../Core/Check.js"; +import Color from "../../Core/Color.js"; import ColorBlendMode from "../ColorBlendMode.js"; import defined from "../../Core/defined.js"; import defaultValue from "../../Core/defaultValue.js"; @@ -13,9 +14,9 @@ import when from "../../ThirdParty/when.js"; import destroyObject from "../../Core/destroyObject.js"; import Matrix4 from "../../Core/Matrix4.js"; import ModelFeatureTable from "./ModelFeatureTable.js"; +import PointCloudShading from "../PointCloudShading.js"; import B3dmLoader from "./B3dmLoader.js"; import PntsLoader from "./PntsLoader.js"; -import Color from "../../Core/Color.js"; /** * A 3D model. This is a new architecture that is more decoupled than the older {@link Model}. This class is still experimental. @@ -42,6 +43,7 @@ import Color from "../../Core/Color.js"; * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the colorBlendMode is MIX. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. * @param {Number} [options.featureIdAttributeIndex=0] The index of the feature ID attribute to use for picking features per-instance or per-primitive. * @param {Number} [options.featureIdTextureIndex=0] The index of the feature ID texture to use for picking features per-primitive. + * @param {Object} [options.pointCloudShading] Options for constructing a {@link PointCloudShading} object to control point attenuation based on geometric error and lighting. * * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy. */ @@ -115,6 +117,10 @@ export default function ModelExperimental(options) { this._boundingSphere = undefined; + var pointCloudShading = new PointCloudShading(options.pointCloudShading); + this._attenuation = pointCloudShading.attenuation; + this._pointCloudShading = pointCloudShading; + this._debugShowBoundingVolumeDirty = false; this._debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, @@ -292,6 +298,21 @@ Object.defineProperties(ModelExperimental.prototype, { }, }, + pointCloudShading: { + get: function () { + return this._pointCloudShading; + }, + set: function (value) { + //>>includeStart('debug', pragmas.debug); + Check.defined("pointCloudShading", value); + //>>includeEnd('debug'); + if (value !== this._pointCloudShading) { + this.resetDrawCommands(); + } + this._pointCloudShading = value; + }, + }, + /** * The model's custom shader, if it exists. Using custom shaders with a {@link Cesium3DTileStyle} * may lead to undefined behavior. @@ -621,6 +642,13 @@ ModelExperimental.prototype.update = function (frameState) { this._customShader.update(frameState); } + // Check if the shader needs to be updated for point cloud attenuation + // settings. + if (this.pointCloudShading.attenuation !== this._attenuation) { + this.resetDrawCommands(); + this._attenuation = this.pointCloudShading.attenuation; + } + // short-circuit if the model resources aren't ready. if (!this._resourcesLoaded) { return; diff --git a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js index d5f7f23d67a4..6228e56f86ef 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js +++ b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js @@ -174,6 +174,7 @@ ModelExperimental3DTileContent.prototype.update = function ( model.colorBlendMode = tileset.colorBlendMode; model.modelMatrix = tile.computedTransform; model.customShader = tileset.customShader; + model.pointCloudShading = tileset.pointCloudShading; model.update(frameState); }; diff --git a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js index 87c39f3a6e0d..7a33796e29d4 100644 --- a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js +++ b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js @@ -50,14 +50,12 @@ PointCloudAttenuationPipelineStage.process = function ( ShaderDestination.VERTEX ); - var pointCloudShading; - var content; var model = renderResources.model; + var pointCloudShading = model.pointCloudShading; + + var content; if (ModelExperimentalType.is3DTiles(model.type)) { content = model.content; - pointCloudShading = content.tileset.pointCloudShading; - } else { - pointCloudShading = model.pointCloudShading; } shaderBuilder.addUniform( @@ -136,7 +134,7 @@ function getGeometricError( positionAttribute.min, scratchDimensions ); - // dimensions is a vector, as (point - point) = displacement vector + // dimensions is a vector, as it is a subtraction between two points dimensions = Matrix4.multiplyByPointAsVector( nodeTransform, dimensions, diff --git a/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js b/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js index 6f1e854c30ed..4b447b4a169c 100644 --- a/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js +++ b/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js @@ -75,45 +75,6 @@ describe( expect(uniformMap.model_pointCloudAttenuation).toBeDefined(); }); - it("uses tileset.pointCloudShading for 3D Tiles", function () { - var uniformMap = {}; - var pointCloudShading1dp = new PointCloudShading({ - attenuation: true, - maximumAttenuation: 1, - }); - var pointCloudShading4dp = new PointCloudShading({ - attenuation: true, - maximumAttenuation: 4, - }); - var renderResources = { - shaderBuilder: new ShaderBuilder(), - uniformMap: uniformMap, - runtimeNode: mockRuntimeNode, - model: { - type: ModelExperimentalType.TILE_GLTF, - content: { - tile: { - geometricError: 3, - }, - tileset: { - pointCloudShading: pointCloudShading1dp, - }, - }, - pointCloudShading: pointCloudShading4dp, - }, - }; - - var frameState = scene.frameState; - PointCloudAttenuationPipelineStage.process( - renderResources, - mockPrimitive, - frameState - ); - - var attenuation = uniformMap.model_pointCloudAttenuation(); - expect(attenuation.x).toEqual(1 * frameState.pixelRatio); - }); - it("point size is determined by maximumAttenuation", function () { var uniformMap = {}; var pointCloudShading = new PointCloudShading({ @@ -179,13 +140,11 @@ describe( uniformMap: uniformMap, model: { type: ModelExperimentalType.TILE_GLTF, + pointCloudShading: pointCloudShading, content: { tile: { geometricError: 3, }, - tileset: { - pointCloudShading: pointCloudShading, - }, }, }, }; @@ -213,13 +172,11 @@ describe( runtimeNode: mockRuntimeNode, model: { type: ModelExperimentalType.TILE_GLTF, + pointCloudShading: pointCloudShading, content: { tile: { geometricError: 3, }, - tileset: { - pointCloudShading: pointCloudShading, - }, }, }, }; @@ -248,13 +205,11 @@ describe( runtimeNode: mockRuntimeNode, model: { type: ModelExperimentalType.TILE_GLTF, + pointCloudShading: pointCloudShading, content: { tile: { geometricError: 0, }, - tileset: { - pointCloudShading: pointCloudShading, - }, }, }, }; From 2fc05d960a36e12605924d9374a5b95605e00168 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Tue, 11 Jan 2022 14:57:25 -0500 Subject: [PATCH 14/21] Default maximumAttenuation to max screenspace error --- .../ModelExperimental3DTileContent.js | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js index 6228e56f86ef..40cfac7ab826 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js +++ b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js @@ -1,8 +1,11 @@ import Axis from "../Axis.js"; import defined from "../../Core/defined.js"; +import defaultValue from "../../Core/defaultValue.js"; import destroyObject from "../../Core/destroyObject.js"; -import ModelExperimental from "./ModelExperimental.js"; import Pass from "../../Renderer/Pass.js"; +import Cesium3DTileRefine from "../Cesium3DTileRefine.js"; +import PointCloudShading from "../PointCloudShading.js"; +import ModelExperimental from "./ModelExperimental.js"; /** * Represents the contents of a glTF, glb or @@ -163,6 +166,8 @@ ModelExperimental3DTileContent.prototype.applyStyle = function (style) { this._model.applyStyle(style); }; +var defaultShading = new PointCloudShading(); + ModelExperimental3DTileContent.prototype.update = function ( tileset, frameState @@ -174,7 +179,20 @@ ModelExperimental3DTileContent.prototype.update = function ( model.colorBlendMode = tileset.colorBlendMode; model.modelMatrix = tile.computedTransform; model.customShader = tileset.customShader; - model.pointCloudShading = tileset.pointCloudShading; + + var pointCloudShading = defaultValue( + tileset.pointCloudShading, + defaultShading + ); + + model.pointCloudShading = pointCloudShading; + + if (!defined(pointCloudShading.maximumAttenuation)) { + pointCloudShading.maximumAttenuation = + tile.refine === Cesium3DTileRefine.ADD + ? 5.0 + : tileset.maximumScreenSpaceError; + } model.update(frameState); }; From 0c84b40c898e91e16a4969b06a497b61dcdce9c6 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Tue, 11 Jan 2022 15:22:17 -0500 Subject: [PATCH 15/21] Add pointCloudShading settings to fromGltf --- Source/Scene/ModelExperimental/ModelExperimental.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Scene/ModelExperimental/ModelExperimental.js b/Source/Scene/ModelExperimental/ModelExperimental.js index 1c7f14a74363..dafb74ac36a4 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental.js +++ b/Source/Scene/ModelExperimental/ModelExperimental.js @@ -793,6 +793,7 @@ ModelExperimental.prototype.destroyResources = function () { * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the colorBlendMode is MIX. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. * @param {Number} [options.featureIdAttributeIndex=0] The index of the feature ID attribute to use for picking features per-instance or per-primitive. * @param {Number} [options.featureIdTextureIndex=0] The index of the feature ID texture to use for picking features per-primitive. + * @param {Object} [options.pointCloudShading] Options for constructing a {@link PointCloudShading} object to control point attenuation based on geometric error and lighting. * * @returns {ModelExperimental} The newly created model. */ @@ -850,6 +851,7 @@ ModelExperimental.fromGltf = function (options) { colorBlendMode: options.colorBlendMode, featureIdAttributeIndex: options.featureIdAttributeIndex, featureIdTextureIndex: options.featureIdTextureIndex, + pointCloudShading: options.pointCloudShading, }; var model = new ModelExperimental(modelOptions); From efee47da29af17e411769736d2b8409b3c9c4b32 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Wed, 12 Jan 2022 14:26:07 -0500 Subject: [PATCH 16/21] PR feedback --- Source/Scene/ModelExperimental/ModelExperimental.js | 11 ++++++++++- .../ModelExperimental/ModelExperimentalPrimitive.js | 8 +------- .../PointCloudAttenuationPipelineStage.js | 5 ++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Source/Scene/ModelExperimental/ModelExperimental.js b/Source/Scene/ModelExperimental/ModelExperimental.js index dafb74ac36a4..be898241c1b7 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental.js +++ b/Source/Scene/ModelExperimental/ModelExperimental.js @@ -298,6 +298,15 @@ Object.defineProperties(ModelExperimental.prototype, { }, }, + /** + * Point cloud shading settings for controlling point cloud attenuation + * and lighting. For 3D Tiles, this is inherited from the + * {@link Cesium3DTileset}. + * + * @memberof ModelExperimental.prototype + * + * @type {PointCloudShading} + */ pointCloudShading: { get: function () { return this._pointCloudShading; @@ -793,7 +802,7 @@ ModelExperimental.prototype.destroyResources = function () { * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the colorBlendMode is MIX. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. * @param {Number} [options.featureIdAttributeIndex=0] The index of the feature ID attribute to use for picking features per-instance or per-primitive. * @param {Number} [options.featureIdTextureIndex=0] The index of the feature ID texture to use for picking features per-primitive. - * @param {Object} [options.pointCloudShading] Options for constructing a {@link PointCloudShading} object to control point attenuation based on geometric error and lighting. + * @param {Object} [options.pointCloudShading] Options for constructing a {@link PointCloudShading} object to control point attenuation and lighting. * * @returns {ModelExperimental} The newly created model. */ diff --git a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js index 2301e38979c6..d61800f59b46 100644 --- a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js +++ b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js @@ -12,7 +12,6 @@ import DequantizationPipelineStage from "./DequantizationPipelineStage.js"; import GeometryPipelineStage from "./GeometryPipelineStage.js"; import LightingPipelineStage from "./LightingPipelineStage.js"; import MaterialPipelineStage from "./MaterialPipelineStage.js"; -import ModelExperimentalType from "./ModelExperimentalType.js"; import ModelExperimentalUtility from "./ModelExperimentalUtility.js"; import PickingPipelineStage from "./PickingPipelineStage.js"; import PointCloudAttenuationPipelineStage from "./PointCloudAttenuationPipelineStage.js"; @@ -130,12 +129,7 @@ ModelExperimentalPrimitive.prototype.configurePipeline = function () { primitive.attributes ); - var pointCloudShading; - if (ModelExperimentalType.is3DTiles(model.type)) { - pointCloudShading = model.content.tileset.pointCloudShading; - } else { - pointCloudShading = model.pointCloudShading; - } + var pointCloudShading = model.pointCloudShading; var hasAttenuation = defined(pointCloudShading) && pointCloudShading.attenuation; diff --git a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js index 7a33796e29d4..58944bb1a5a5 100644 --- a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js +++ b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js @@ -128,9 +128,8 @@ function getGeometricError( // Estimate the geometric error var nodeTransform = renderResources.runtimeNode.transform; - var dimensions = Cartesian3.clone(positionAttribute.max, scratchDimensions); - dimensions = Cartesian3.subtract( - dimensions, + var dimensions = Cartesian3.subtract( + positionAttribute.max, positionAttribute.min, scratchDimensions ); From 0bdcbce42195b145f190a223ff8f882cb8b57b5b Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Wed, 12 Jan 2022 15:40:31 -0500 Subject: [PATCH 17/21] Prevent tileset.pointCloudShading from being set to undefined --- Source/Scene/Cesium3DTileset.js | 26 ++++++++++++++++++++------ Specs/Scene/Cesium3DTilesetSpec.js | 10 ++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index f2bca1d4fee0..c5cc469798b4 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -453,12 +453,7 @@ function Cesium3DTileset(options) { */ this.colorBlendAmount = 0.5; - /** - * Options for controlling point size based on geometric error and eye dome lighting. - * @type {PointCloudShading} - */ - this.pointCloudShading = new PointCloudShading(options.pointCloudShading); - + this._pointCloudShading = new PointCloudShading(options.pointCloudShading); this._pointCloudEyeDomeLighting = new PointCloudEyeDomeLighting(); /** @@ -1432,6 +1427,25 @@ Object.defineProperties(Cesium3DTileset.prototype, { }, }, + /** + * Options for controlling point size based on geometric error and eye dome lighting. + * + * @memberof ModelExperimental.prototype + * + * @type {PointCloudShading} + */ + pointCloudShading: { + get: function () { + return this._pointCloudShading; + }, + set: function (value) { + //>>includeStart('debug', pragmas.debug); + Check.defined("pointCloudShading", value); + //>>includeEnd('debug'); + this._pointCloudShading = value; + }, + }, + /** * The root tile. * diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js index c9e8d205b9b1..aba6e659fe3d 100644 --- a/Specs/Scene/Cesium3DTilesetSpec.js +++ b/Specs/Scene/Cesium3DTilesetSpec.js @@ -4351,6 +4351,16 @@ describe( ); }); + it("throws if pointCloudShading is set to undefined", function () { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function ( + tileset + ) { + expect(function () { + tileset.pointCloudShading = undefined; + }).toThrowDeveloperError(); + }); + }); + describe("updateForPass", function () { it("updates for pass", function () { viewAllTiles(); From 4e460c8b5393f647c08a35d1f0f562525baf92c0 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Wed, 12 Jan 2022 15:45:04 -0500 Subject: [PATCH 18/21] Move MSSE handling to uniform callback --- .../ModelExperimental3DTileContent.js | 20 +---- .../PointCloudAttenuationPipelineStage.js | 16 +++- .../PointCloudAttenuationPipelineStageSpec.js | 74 +++++++++++++++++++ 3 files changed, 90 insertions(+), 20 deletions(-) diff --git a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js index 40cfac7ab826..a91e90ba4131 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js +++ b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js @@ -1,10 +1,7 @@ import Axis from "../Axis.js"; import defined from "../../Core/defined.js"; -import defaultValue from "../../Core/defaultValue.js"; import destroyObject from "../../Core/destroyObject.js"; import Pass from "../../Renderer/Pass.js"; -import Cesium3DTileRefine from "../Cesium3DTileRefine.js"; -import PointCloudShading from "../PointCloudShading.js"; import ModelExperimental from "./ModelExperimental.js"; /** @@ -166,8 +163,6 @@ ModelExperimental3DTileContent.prototype.applyStyle = function (style) { this._model.applyStyle(style); }; -var defaultShading = new PointCloudShading(); - ModelExperimental3DTileContent.prototype.update = function ( tileset, frameState @@ -179,20 +174,7 @@ ModelExperimental3DTileContent.prototype.update = function ( model.colorBlendMode = tileset.colorBlendMode; model.modelMatrix = tile.computedTransform; model.customShader = tileset.customShader; - - var pointCloudShading = defaultValue( - tileset.pointCloudShading, - defaultShading - ); - - model.pointCloudShading = pointCloudShading; - - if (!defined(pointCloudShading.maximumAttenuation)) { - pointCloudShading.maximumAttenuation = - tile.refine === Cesium3DTileRefine.ADD - ? 5.0 - : tileset.maximumScreenSpaceError; - } + model.pointCloudShading = tileset.pointCloudShading; model.update(frameState); }; diff --git a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js index 58944bb1a5a5..c782e3e58513 100644 --- a/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js +++ b/Source/Scene/ModelExperimental/PointCloudAttenuationPipelineStage.js @@ -6,6 +6,7 @@ import Matrix4 from "../../Core/Matrix4.js"; import OrthographicFrustum from "../../Core/OrthographicFrustum.js"; import ShaderDestination from "../../Renderer/ShaderDestination.js"; import PointCloudAttenuationStageVS from "../../Shaders/ModelExperimental/PointCloudAttenuationStageVS.js"; +import Cesium3DTileRefine from "../Cesium3DTileRefine.js"; import SceneMode from "../SceneMode.js"; import VertexAttributeSemantic from "../VertexAttributeSemantic.js"; import ModelExperimentalType from "./ModelExperimentalType.js"; @@ -54,8 +55,12 @@ PointCloudAttenuationPipelineStage.process = function ( var pointCloudShading = model.pointCloudShading; var content; + var is3DTiles; + var usesAddRefinement; if (ModelExperimentalType.is3DTiles(model.type)) { + is3DTiles = true; content = model.content; + usesAddRefinement = content.tile.refine === Cesium3DTileRefine.ADD; } shaderBuilder.addUniform( @@ -67,7 +72,16 @@ PointCloudAttenuationPipelineStage.process = function ( var scratch = scratchAttenuationUniform; // attenuation.x = pointSize - scratch.x = defaultValue(pointCloudShading.maximumAttenuation, 1.0); + var defaultPointSize = 1.0; + if (is3DTiles) { + defaultPointSize = usesAddRefinement + ? 5.0 + : content.tileset.maximumScreenSpaceError; + } + scratch.x = defaultValue( + pointCloudShading.maximumAttenuation, + defaultPointSize + ); scratch.x *= frameState.pixelRatio; // attenuation.y = geometricError diff --git a/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js b/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js index 4b447b4a169c..56ae51140347 100644 --- a/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js +++ b/Specs/Scene/ModelExperimental/PointCloudAttenuationPipelineStageSpec.js @@ -1,6 +1,7 @@ import { Camera, Cartesian3, + Cesium3DTileRefine, Math as CesiumMath, Matrix4, ModelExperimentalType, @@ -102,6 +103,76 @@ describe( expect(attenuation.x).toEqual(4 * frameState.pixelRatio); }); + it("point size defaults to 5dp for 3D Tiles with additive refinement", function () { + var uniformMap = {}; + var pointCloudShading = new PointCloudShading({ + attenuation: true, + maximumAttenuation: undefined, + }); + var renderResources = { + shaderBuilder: new ShaderBuilder(), + uniformMap: uniformMap, + runtimeNode: mockRuntimeNode, + model: { + type: ModelExperimentalType.TILE_GLTF, + pointCloudShading: pointCloudShading, + content: { + tile: { + refine: Cesium3DTileRefine.ADD, + }, + tileset: { + maximumScreenSpaceError: 16, + }, + }, + }, + }; + + var frameState = scene.frameState; + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + frameState + ); + + var attenuation = uniformMap.model_pointCloudAttenuation(); + expect(attenuation.x).toEqual(5 * frameState.pixelRatio); + }); + + it("point size defaults to tileset.maximumScreenSpaceError for 3D Tiles with replace refinement", function () { + var uniformMap = {}; + var pointCloudShading = new PointCloudShading({ + attenuation: true, + maximumAttenuation: undefined, + }); + var renderResources = { + shaderBuilder: new ShaderBuilder(), + uniformMap: uniformMap, + runtimeNode: mockRuntimeNode, + model: { + type: ModelExperimentalType.TILE_GLTF, + pointCloudShading: pointCloudShading, + content: { + tile: { + refine: Cesium3DTileRefine.REPLACE, + }, + tileset: { + maximumScreenSpaceError: 16, + }, + }, + }, + }; + + var frameState = scene.frameState; + PointCloudAttenuationPipelineStage.process( + renderResources, + mockPrimitive, + frameState + ); + + var attenuation = uniformMap.model_pointCloudAttenuation(); + expect(attenuation.x).toEqual(16 * frameState.pixelRatio); + }); + it("point size defaults to 1dp when maximumAttenuation is not defined", function () { var uniformMap = {}; var pointCloudShading = new PointCloudShading({ @@ -144,6 +215,7 @@ describe( content: { tile: { geometricError: 3, + refine: Cesium3DTileRefine.ADD, }, }, }, @@ -176,6 +248,7 @@ describe( content: { tile: { geometricError: 3, + refine: Cesium3DTileRefine.ADD, }, }, }, @@ -209,6 +282,7 @@ describe( content: { tile: { geometricError: 0, + refine: Cesium3DTileRefine.ADD, }, }, }, From d055289243669f9cd322d64958275eec508872c7 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Wed, 12 Jan 2022 15:56:55 -0500 Subject: [PATCH 19/21] update CHANGES.md --- CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 446bbbf7b23d..add8347ada7a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,10 +2,16 @@ ### 1.90 - 2022-02-01 +##### Additions :tada: + +- Added `PntsLoader` to transcode .pnts to `ModelExperimental`. [#9978](https://github.com/CesiumGS/cesium/pull/9978) +- Added point cloud attenuation support to `ModelExperimental`. [#9998](https://github.com/CesiumGS/cesium/pull/9998) + ##### Fixes :wrench: - Fixed an error when loading GeoJSON with null `stroke` or `fill` properties but valid opacity values. [#9717](https://github.com/CesiumGS/cesium/pull/9717) - Fixed `scene.pickTranslucentDepth` for translucent point clouds with eye dome lighting. [#9991](https://github.com/CesiumGS/cesium/pull/9991) +- Added a setter for `tileset.pointCloudShading` that throws if set to `undefined` to clarify that this is disallowed. [#9998](https://github.com/CesiumGS/cesium/pull/9998) ### 1.89 - 2022-01-03 From 08ce79d31018c2c188f0f14a6888f9ea42727494 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Wed, 12 Jan 2022 16:00:14 -0500 Subject: [PATCH 20/21] Fix unit test --- Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js index 9727b6ab11c9..4b90a222f842 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js @@ -321,6 +321,7 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { model: { type: ModelExperimentalType.TILE_PNTS, featureIdAttributeIndex: 0, + pointCloudShading: pointCloudShading, content: { tileset: { pointCloudShading: pointCloudShading, From 95b495c38969ca0ab9d2c4fa5113c9363e3610d4 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Wed, 12 Jan 2022 16:19:35 -0500 Subject: [PATCH 21/21] Fix typo --- Source/Scene/Cesium3DTileset.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index c5cc469798b4..792bf9b6d8f1 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -1430,7 +1430,7 @@ Object.defineProperties(Cesium3DTileset.prototype, { /** * Options for controlling point size based on geometric error and eye dome lighting. * - * @memberof ModelExperimental.prototype + * @memberof Cesium3DTileset.prototype * * @type {PointCloudShading} */