From f5fad3807f06c0613f245e8260a5100c948ae1ea Mon Sep 17 00:00:00 2001 From: Bouillaguet Quentin Date: Mon, 17 Jul 2023 14:17:04 +0200 Subject: [PATCH] feat(Points): Add support for return, elevation and source id attributes --- src/Layer/PointCloudLayer.js | 19 ++++++++++++++- src/Renderer/PointsMaterial.js | 8 +++++++ src/Renderer/Shader/PointsVS.glsl | 39 +++++++++++++++++++++++++++---- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/Layer/PointCloudLayer.js b/src/Layer/PointCloudLayer.js index 00dfcd563f..a6d4cc0752 100644 --- a/src/Layer/PointCloudLayer.js +++ b/src/Layer/PointCloudLayer.js @@ -66,8 +66,18 @@ function changeIntensityRange(layer) { } } +function changeElevationRange(layer) { + if (layer.material.elevationRange) { + layer.material.elevationRange.set( + layer.minElevationRange, + layer.maxElevationRange, + ); + } +} + /** * The basis for all point clouds related layers. + * @extends GeometryLayer * * @property {boolean} isPointCloudLayer - Used to checkout whether this layer * is a PointCloudLayer. Default is `true`. You should not change this, as it is @@ -97,6 +107,12 @@ function changeIntensityRange(layer) { * @property {number} [maxIntensityRange=1] - The maximal intensity of the * layer. Changing this value will affect the material, if it has the * corresponding uniform. The value is normalized between 0 and 1. + * @property {number} [minElevationRange=0] - The minimal elevation of the + * layer. Changing this value will affect the material, if it has the + * corresponding uniform. + * @property {number} [maxElevationRange=1] - The maximal elevation of the + * layer. Changing this value will affect the material, if it has the + * corresponding uniform. */ class PointCloudLayer extends GeometryLayer { /** @@ -105,7 +121,6 @@ class PointCloudLayer extends GeometryLayer { * directly, but rather implemented using `extends`. * * @constructor - * @extends GeometryLayer * * @param {string} id - The id of the layer, that should be unique. It is * not mandatory, but an error will be emitted if this layer is added a @@ -136,6 +151,8 @@ class PointCloudLayer extends GeometryLayer { this.defineLayerProperty('minIntensityRange', config.minIntensityRange || 0, changeIntensityRange); this.defineLayerProperty('maxIntensityRange', config.maxIntensityRange || 1, changeIntensityRange); + this.defineLayerProperty('minElevationRange', config.minElevationRange || 0, changeElevationRange); + this.defineLayerProperty('maxElevationRange', config.maxElevationRange || 1, changeElevationRange); this.material = config.material || {}; if (!this.material.isMaterial) { diff --git a/src/Renderer/PointsMaterial.js b/src/Renderer/PointsMaterial.js index 4ddb0f5f32..df2e8ce0ab 100644 --- a/src/Renderer/PointsMaterial.js +++ b/src/Renderer/PointsMaterial.js @@ -10,6 +10,10 @@ export const PNTS_MODE = { INTENSITY: 1, CLASSIFICATION: 2, NORMAL: 3, + RETURN_NUMBER: 4, + NUMBER_OF_RETURNS: 5, + POINT_SOURCE_ID: 6, + ELEVATION: 7, }; export const PNTS_SHAPE = { @@ -68,6 +72,7 @@ class PointsMaterial extends THREE.RawShaderMaterial { * @param {number} [options.mode=PNTS_SHAPE.CIRCLE] rendered points shape. * @param {THREE.Vector4} [options.overlayColor=new THREE.Vector4(0, 0, 0, 0)] overlay color. * @param {THREE.Vector2} [options.intensityRange=new THREE.Vector2(0, 1)] intensity range. + * @param {THREE.Vector2} [options.elevationRange=new THREE.Vector2(0, 1)] - elevation range. * @param {boolean} [options.applyOpacityClassication=false] apply opacity classification on all display mode. * @param {Classification} [options.classification] - define points classification. * @param {number} [options.sizeMode=PNTS_SIZE_MODE.VALUE] point cloud size mode. Only 'VALUE' or 'ATTENUATED' are possible. VALUE use constant size, ATTENUATED compute size depending on distance from point to camera. @@ -83,6 +88,7 @@ class PointsMaterial extends THREE.RawShaderMaterial { */ constructor(options = {}) { const intensityRange = options.intensityRange || new THREE.Vector2(0, 1); + const elevationRange = options.elevationRange || new THREE.Vector2(0, 1); const oiMaterial = options.orientedImageMaterial; const classification = options.classification || ClassificationScheme.DEFAULT; const applyOpacityClassication = options.applyOpacityClassication == undefined ? false : options.applyOpacityClassication; @@ -95,6 +101,7 @@ class PointsMaterial extends THREE.RawShaderMaterial { delete options.orientedImageMaterial; delete options.intensityRange; + delete options.elevationRange; delete options.classification; delete options.applyOpacityClassication; delete options.size; @@ -121,6 +128,7 @@ class PointsMaterial extends THREE.RawShaderMaterial { CommonMaterial.setUniformProperty(this, 'opacity', this.opacity); CommonMaterial.setUniformProperty(this, 'overlayColor', options.overlayColor || new THREE.Vector4(0, 0, 0, 0)); CommonMaterial.setUniformProperty(this, 'intensityRange', intensityRange); + CommonMaterial.setUniformProperty(this, 'elevationRange', elevationRange); CommonMaterial.setUniformProperty(this, 'applyOpacityClassication', applyOpacityClassication); CommonMaterial.setUniformProperty(this, 'sizeMode', sizeMode); CommonMaterial.setUniformProperty(this, 'preSSE', 1.0); diff --git a/src/Renderer/Shader/PointsVS.glsl b/src/Renderer/Shader/PointsVS.glsl index 019541fe6a..a18416ebbf 100644 --- a/src/Renderer/Shader/PointsVS.glsl +++ b/src/Renderer/Shader/PointsVS.glsl @@ -7,6 +7,24 @@ #include #include +// For now, we only consider 3-bits uint values for return numbers. +// On LAS 1.4 PDRF >= 6, return numbers are encoded on 4 bits, so we clamp them +// to 3 bits. +#define RETURN_NUMBER_MAX 7. + +attribute vec3 color; +attribute float intensity; +attribute float classification; +attribute float returnNumber; +attribute float numberOfReturns; +attribute float pointSourceID; +attribute float gpsTime; + +uniform mat4 modelMatrix; + +uniform vec2 intensityRange; +uniform vec2 elevationRange; + uniform float size; uniform float preSSE; @@ -14,12 +32,8 @@ uniform bool picking; uniform int mode; uniform float opacity; uniform vec4 overlayColor; -uniform vec2 intensityRange; uniform bool applyOpacityClassication; -attribute vec3 color; attribute vec4 unique_id; -attribute float intensity; -attribute float classification; uniform sampler2D classificationLUT; uniform int sizeMode; uniform float minAttenuatedSize; @@ -28,6 +42,7 @@ uniform float maxAttenuatedSize; #if defined(NORMAL_OCT16) attribute vec2 oct16Normal; #elif defined(NORMAL_SPHEREMAPPED) + attribute vec2 sphereMappedNormal; #else attribute vec3 normal; @@ -97,6 +112,22 @@ void main() { } else if (mode == PNTS_MODE_COLOR) { // default to color mode vColor.rgb = mix(color, overlayColor.rgb, overlayColor.a); + } else if (mode == PNTS_MODE_RETURN_NUMBER) { + float n = returnNumber / RETURN_NUMBER_MAX; + vColor.rgb = vec3(n, n, n); + } else if (mode == PNTS_MODE_NUMBER_OF_RETURNS) { + float n = numberOfReturns / RETURN_NUMBER_MAX; + vColor.rgb = vec3(n, n, n); + } else if (mode == PNTS_MODE_POINT_SOURCE_ID) { + // group ids by their 4 least significant bits + float i = mod(pointSourceID, 16.) / 16.; + vColor.rgb = vec3(i, i, i); + } else if (mode == PNTS_MODE_ELEVATION) { + // apply scale and offset transform + vec4 model = modelMatrix * vec4(position, 1.0); + float z = (model.z - elevationRange.x) / (elevationRange.y - elevationRange.x); + // adapt the grayscale knowing the range + vColor.rgb = vec3(z, z, z); } }