diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index 3b7ae944b..30cf0d952 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -103,7 +103,7 @@ const getStream = () => { // prettier-ignore // 'https://app.speckle.systems/streams/da9e320dad/commits/5388ef24b8?c=%5B-7.66134,10.82932,6.41935,-0.07739,-13.88552,1.8697,0,1%5D' // Revit sample house (good for bim-like stuff with many display meshes) - 'https://app.speckle.systems/streams/da9e320dad/commits/5388ef24b8' + // 'https://app.speckle.systems/streams/da9e320dad/commits/5388ef24b8' // 'https://latest.speckle.systems/streams/c1faab5c62/commits/ab1a1ab2b6' // 'https://app.speckle.systems/streams/da9e320dad/commits/5388ef24b8' // 'https://latest.speckle.systems/streams/58b5648c4d/commits/60371ecb2d' @@ -475,6 +475,15 @@ const getStream = () => { // Custom normals // 'https://latest.speckle.systems/projects/51c449c440/models/08e97226cf' + // Meshed together - like objects + // 'https://app.speckle.systems/projects/edccdee0fd/models/944c475581' + + // Engine thing with outlines + // 'https://app.speckle.systems/projects/8be1007be1/models/33fbee921f' + + // Dim's meshed together non instanced + instanced + 'https://latest.speckle.systems/projects/126cd4b7bb/models/338afee6be' + // A LOT of text objects // 'https://app.speckle.systems/projects/e771a388b1/models/f5c967dfa9' diff --git a/packages/viewer/src/modules/SpeckleRenderer.ts b/packages/viewer/src/modules/SpeckleRenderer.ts index 97fd73c33..555bc9442 100644 --- a/packages/viewer/src/modules/SpeckleRenderer.ts +++ b/packages/viewer/src/modules/SpeckleRenderer.ts @@ -348,10 +348,7 @@ export default class SpeckleRenderer { this._renderer.setSize(container.offsetWidth, container.offsetHeight) container.appendChild(this._renderer.domElement) - this.batcher = new Batcher( - this.renderer.capabilities.maxVertexUniforms, - this.renderer.capabilities.floatVertexTextures - ) + this.batcher = new Batcher(this.renderer.capabilities) this._pipeline = new DefaultPipeline(this) diff --git a/packages/viewer/src/modules/batching/Batch.ts b/packages/viewer/src/modules/batching/Batch.ts index d3893832a..ae9cd1d28 100644 --- a/packages/viewer/src/modules/batching/Batch.ts +++ b/packages/viewer/src/modules/batching/Batch.ts @@ -74,3 +74,7 @@ export const AllBatchUpdateRange = { export const INSTANCE_TRANSFORM_BUFFER_STRIDE = 16 export const INSTANCE_GRADIENT_BUFFER_STRIDE = 1 +let BATCH_INDEX_COUNTER = 0 +export const getNextBatchIndex = () => { + return ++BATCH_INDEX_COUNTER +} diff --git a/packages/viewer/src/modules/batching/Batcher.ts b/packages/viewer/src/modules/batching/Batcher.ts index 7bbc77a6f..1cf6de11b 100644 --- a/packages/viewer/src/modules/batching/Batcher.ts +++ b/packages/viewer/src/modules/batching/Batcher.ts @@ -1,4 +1,4 @@ -import { MathUtils } from 'three' +import { MathUtils, WebGLCapabilities } from 'three' import LineBatch from './LineBatch.js' import Materials, { FilterMaterialType, @@ -36,20 +36,18 @@ type BatchTypeMap = { } export default class Batcher { - private maxHardwareUniformCount = 0 - private floatTextures = false + private caps: WebGLCapabilities private maxBatchObjects = 0 private maxBatchVertices = 500000 private minInstancedBatchVertices = 10000 public materials: Materials public batches: { [id: string]: Batch } = {} - public constructor(maxUniformCount: number, floatTextures: boolean) { - this.maxHardwareUniformCount = maxUniformCount + public constructor(caps: WebGLCapabilities) { + this.caps = caps this.maxBatchObjects = Math.floor( - (this.maxHardwareUniformCount - Materials.UNIFORM_VECTORS_USED) / 4 + (this.caps.maxVertexUniforms - Materials.UNIFORM_VECTORS_USED) / 4 ) - this.floatTextures = floatTextures this.materials = new Materials() void this.materials.createDefaultMaterials() } @@ -299,7 +297,12 @@ export default class Batcher { const matRef = renderViews[0].renderData.renderMaterial const material = this.materials.getMaterial(materialHash, matRef, GeometryType.MESH) const batchID = MathUtils.generateUUID() - const geometryBatch = new InstancedMeshBatch(batchID, renderTree.id, renderViews) + const geometryBatch = new InstancedMeshBatch( + batchID, + renderTree.id, + renderViews, + this.caps.isWebGL2 + ) geometryBatch.setBatchMaterial(material) await geometryBatch.buildBatch() @@ -355,7 +358,7 @@ export default class Batcher { batchID, renderTree.id, renderViews, - this.floatTextures + this.caps.floatVertexTextures ? TransformStorage.VERTEX_TEXTURE : TransformStorage.UNIFORM_ARRAY ) diff --git a/packages/viewer/src/modules/batching/InstancedMeshBatch.ts b/packages/viewer/src/modules/batching/InstancedMeshBatch.ts index 37192d118..64ad40c1f 100644 --- a/packages/viewer/src/modules/batching/InstancedMeshBatch.ts +++ b/packages/viewer/src/modules/batching/InstancedMeshBatch.ts @@ -47,6 +47,7 @@ export class InstancedMeshBatch implements Batch { private instanceTransformBuffer1: Float32Array private transformBufferIndex: number = 0 private instanceGradientBuffer: Float32Array + private instanceObjectIdBuffer: Float32Array | undefined private needsShuffle = false @@ -107,10 +108,16 @@ export class InstancedMeshBatch implements Batch { return this.mesh.groups } - public constructor(id: string, subtreeId: string, renderViews: NodeRenderView[]) { + public constructor( + id: string, + subtreeId: string, + renderViews: NodeRenderView[], + webGL2: boolean + ) { this.id = id this.subtreeId = subtreeId this.renderViews = renderViews + if (!webGL2) this.instanceObjectIdBuffer = new Float32Array(this.renderViews.length) } public setBatchMaterial(material: Material) { @@ -323,7 +330,8 @@ export class InstancedMeshBatch implements Batch { } else this.mesh.updateDrawGroups( this.getCurrentTransformBuffer(), - this.getCurrentGradientBuffer() + this.getCurrentGradientBuffer(), + this.getCurrentObjectIndexBuffer() ) } @@ -448,7 +456,11 @@ export class InstancedMeshBatch implements Batch { }) } sourceGradientBuffer.set(targetGradientBuffer, 0) - this.mesh.updateDrawGroups(targetTransformBuffer, sourceGradientBuffer) + this.mesh.updateDrawGroups( + targetTransformBuffer, + sourceGradientBuffer, + this.getCurrentObjectIndexBuffer() + ) /** Solve hidden groups */ const hiddenGroup = this.groups.find((value) => { @@ -477,7 +489,8 @@ export class InstancedMeshBatch implements Batch { this.setVisibleRange([AllBatchUpdateRange]) this.mesh.updateDrawGroups( this.getCurrentTransformBuffer(), - this.getCurrentGradientBuffer() + this.getCurrentGradientBuffer(), + this.getCurrentObjectIndexBuffer() ) } @@ -497,6 +510,10 @@ export class InstancedMeshBatch implements Batch { return this.instanceGradientBuffer } + private getCurrentObjectIndexBuffer(): Float32Array | undefined { + return this.instanceObjectIdBuffer + } + public buildBatch(): Promise { const batchObjects: BatchObject[] = [] let instanceBVH = null @@ -528,6 +545,8 @@ export class InstancedMeshBatch implements Batch { k * INSTANCE_TRANSFORM_BUFFER_STRIDE, INSTANCE_TRANSFORM_BUFFER_STRIDE ) + if (this.instanceObjectIdBuffer) this.instanceObjectIdBuffer[k] = k + const batchObject = new InstancedBatchObject(this.renderViews[k], k) if (!instanceBVH) { const transform = new Matrix4().makeTranslation( @@ -600,7 +619,8 @@ export class InstancedMeshBatch implements Batch { }) this.mesh.updateDrawGroups( this.getCurrentTransformBuffer(), - this.getCurrentGradientBuffer() + this.getCurrentGradientBuffer(), + this.getCurrentObjectIndexBuffer() ) return Promise.resolve() diff --git a/packages/viewer/src/modules/batching/MeshBatch.ts b/packages/viewer/src/modules/batching/MeshBatch.ts index bc4c48c4f..9bb8587c6 100644 --- a/packages/viewer/src/modules/batching/MeshBatch.ts +++ b/packages/viewer/src/modules/batching/MeshBatch.ts @@ -20,11 +20,11 @@ import { ObjectLayers } from '../../IViewer.js' import Logger from '../utils/Logger.js' export class MeshBatch extends PrimitiveBatch { - protected primitive!: SpeckleMesh + protected primitive: SpeckleMesh protected transformStorage: TransformStorage - private indexBuffer0!: BufferAttribute - private indexBuffer1!: BufferAttribute + private indexBuffer0: BufferAttribute + private indexBuffer1: BufferAttribute private indexBufferIndex = 0 protected drawRanges: DrawRanges = new DrawRanges() diff --git a/packages/viewer/src/modules/batching/PrimitiveBatch.ts b/packages/viewer/src/modules/batching/PrimitiveBatch.ts index 75e6a11ad..28e00e8d8 100644 --- a/packages/viewer/src/modules/batching/PrimitiveBatch.ts +++ b/packages/viewer/src/modules/batching/PrimitiveBatch.ts @@ -22,13 +22,13 @@ export abstract class Primitive< } export abstract class PrimitiveBatch implements Batch { - public id!: string - public subtreeId!: string - public renderViews!: NodeRenderView[] - public batchMaterial!: Material + public id: string + public subtreeId: string + public renderViews: NodeRenderView[] + public batchMaterial: Material protected abstract primitive: Primitive - protected gradientIndexBuffer!: BufferAttribute + protected gradientIndexBuffer: BufferAttribute protected needsShuffle: boolean = false abstract get geometryType(): GeometryType diff --git a/packages/viewer/src/modules/materials/SpeckleDepthNormalIdMaterial.ts b/packages/viewer/src/modules/materials/SpeckleDepthNormalIdMaterial.ts new file mode 100644 index 000000000..4718b1c20 --- /dev/null +++ b/packages/viewer/src/modules/materials/SpeckleDepthNormalIdMaterial.ts @@ -0,0 +1,45 @@ +import { BufferGeometry, Camera, Object3D, Scene } from 'three' +import { SpeckleWebGLRenderer } from '../objects/SpeckleWebGLRenderer.js' +import { speckleDepthNormalIdFrag } from './shaders/speckle-depth-normal-id-frag.js' +import { speckleDepthNormalIdVert } from './shaders/speckle-depth-normal-id-vert.js' +import SpeckleDepthNormalMaterial from './SpeckleDepthNormalMaterial.js' +import SpeckleMesh from '../objects/SpeckleMesh.js' +import { Uniforms } from './SpeckleMaterial.js' +import { ExtendedInstancedMesh } from '../objects/ExtendedInstancedMesh.js' + +class SpeckleDepthNormalIdMaterial extends SpeckleDepthNormalMaterial { + protected get vertexProgram(): string { + return speckleDepthNormalIdVert + } + + protected get fragmentProgram(): string { + return speckleDepthNormalIdFrag + } + + protected get uniformsDef(): Uniforms { + return { ...super.uniformsDef, batchIndex: 0 } + } + + onBeforeRender( + _this: SpeckleWebGLRenderer, + _scene: Scene, + _camera: Camera, + _geometry: BufferGeometry, + object: Object3D + ) { + if (this.defines && this.defines['USE_RTE']) { + object.modelViewMatrix.copy(_this.RTEBuffers.rteViewModelMatrix) + this.userData.uViewer_low.value.copy(_this.RTEBuffers.viewerLow) + this.userData.uViewer_high.value.copy(_this.RTEBuffers.viewerHigh) + this.userData.rteModelViewMatrix.value.copy(_this.RTEBuffers.rteViewModelMatrix) + } + + if (object instanceof SpeckleMesh || object instanceof ExtendedInstancedMesh) { + this.userData.batchIndex.value = object.batchIndex + } + + this.needsUpdate = true + } +} + +export default SpeckleDepthNormalIdMaterial diff --git a/packages/viewer/src/modules/materials/shaders/speckle-depth-normal-id-frag.ts b/packages/viewer/src/modules/materials/shaders/speckle-depth-normal-id-frag.ts new file mode 100644 index 000000000..13fede70a --- /dev/null +++ b/packages/viewer/src/modules/materials/shaders/speckle-depth-normal-id-frag.ts @@ -0,0 +1,77 @@ +export const speckleDepthNormalIdFrag = /* glsl */ ` +#if __VERSION__ == 100 + #extension GL_EXT_draw_buffers : require +#endif + +#if DEPTH_PACKING == 3200 + uniform float opacity; +#endif +#ifdef LINEAR_DEPTH + varying vec4 vViewPosition; + uniform float near; + uniform float far; +#endif +#include +#include +#include +#include +#include +#include +#include +#include +varying vec2 vHighPrecisionZW; +varying vec3 vNormal; + +varying vec3 vIdColor; + +#if __VERSION__ == 300 + layout(location = 1) out vec4 gNormal; + layout(location = 2) out vec4 gId; +#endif + +void main() { + #include + vec4 diffuseColor = vec4( 1.0 ); + #if DEPTH_PACKING == 3200 + diffuseColor.a = opacity; + #endif + #include + #include + // #include + #ifdef USE_ALPHATEST + if ( diffuseColor.a < alphaTest ) discard; + /** This is a workaround for rejecting shadows for certain materials, since three.js gave me no choice*/ + #ifdef ALPHATEST_REJECTION + if (alphaTest > 0. ) discard; + #endif + #endif + #include + vec3 normal = normalize( vNormal ); + + /** Output view space normals*/ + + vec4 outNormal = vec4( packNormalToRGB( normal ), 1.0 ); + vec4 outDepth; + // Higher precision equivalent of gl_FragCoord.z. This assumes depthRange has been left to its default values. + #ifdef LINEAR_DEPTH + /** View z is negative moving away from the camera */ + outDepth = packDepthToRGBA((vViewPosition.z + near) / (near - far)); + #else + float fragCoordZ = (0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5); + #if DEPTH_PACKING == 3200 + outDepth = vec4( vec3( 1.0 - fragCoordZ ), opacity ); + #elif DEPTH_PACKING == 3201 + outDepth = packDepthToRGBA( fragCoordZ ); + #endif + #endif + #if __VERSION__ == 300 + pc_fragColor = outDepth; + gNormal = outNormal; + gId = vec4(vIdColor, 1.0); + #else + gl_FragData[0] = outDepth; + gl_FragData[1] = outNormal; + gl_FragData[2] = vec4(vIdColor, 1.0); + #endif +} +` diff --git a/packages/viewer/src/modules/materials/shaders/speckle-depth-normal-id-vert.ts b/packages/viewer/src/modules/materials/shaders/speckle-depth-normal-id-vert.ts new file mode 100644 index 000000000..269ed2632 --- /dev/null +++ b/packages/viewer/src/modules/materials/shaders/speckle-depth-normal-id-vert.ts @@ -0,0 +1,273 @@ +export const speckleDepthNormalIdVert = /* glsl */ ` +#include +#ifdef USE_RTE + // The high component is stored as the default 'position' attribute buffer + attribute vec3 position_low; + uniform vec3 uViewer_high; + uniform vec3 uViewer_low; + uniform mat4 rteModelViewMatrix; +#endif + +#if defined( TRANSFORM_STORAGE ) || ( defined( USE_INSTANCING ) && __VERSION__ == 100) + attribute float objIndex; +#endif + +#ifdef TRANSFORM_STORAGE + #if TRANSFORM_STORAGE == 0 + #if __VERSION__ == 300 + #define TRANSFORM_STRIDE 4 + #else + #define TRANSFORM_STRIDE 4. + #endif + uniform sampler2D tTransforms; + uniform float objCount; + #elif TRANSFORM_STORAGE == 1 + uniform mat4 uTransforms[OBJ_COUNT]; + #endif +#endif + +varying vec3 vIdColor; +uniform int batchIndex; + +#ifdef LINEAR_DEPTH + varying vec4 vViewPosition; +#endif +varying vec3 vNormal; + +#include +#include +#include +#include +#include +#include +// This is used for computing an equivalent of gl_FragCoord.z that is as high precision as possible. +// Some platforms compute gl_FragCoord at a lower precision which makes the manually computed value better for +// depth-based postprocessing effects. Reproduced on iPad with A10 processor / iPadOS 13.3.1. +varying vec2 vHighPrecisionZW; + +#ifdef TRANSFORM_STORAGE + void objectTransform(out vec4 quaternion, out vec4 pivotLow, out vec4 pivotHigh, out vec4 translation, out vec4 scale){ + #if TRANSFORM_STORAGE == 0 + #if __VERSION__ == 300 + ivec2 uv = ivec2(int(objIndex) * TRANSFORM_STRIDE, 0); + vec4 v0 = texelFetch( tTransforms, uv, 0 ); + vec4 v1 = texelFetch( tTransforms, uv + ivec2(1, 0), 0); + vec4 v2 = texelFetch( tTransforms, uv + ivec2(2, 0), 0); + vec4 v3 = texelFetch( tTransforms, uv + ivec2(3, 0), 0); + quaternion = v0; + pivotLow = vec4(v1.xyz, 1.); + pivotHigh = vec4(v2.xyz, 1.); + translation = vec4(v3.xyz, 1.); + scale = vec4(v1.w, v2.w, v3.w, 1.); + #else + float size = objCount * TRANSFORM_STRIDE; + vec2 cUv = vec2(0.5/size, 0.5); + vec2 dUv = vec2(1./size, 0.); + + vec2 uv = vec2((objIndex * TRANSFORM_STRIDE)/size + cUv.x, cUv.y); + vec4 v0 = texture2D( tTransforms, uv); + vec4 v1 = texture2D( tTransforms, uv + dUv); + vec4 v2 = texture2D( tTransforms, uv + 2. * dUv); + vec4 v3 = texture2D( tTransforms, uv + 3. * dUv); + quaternion = v0; + pivotLow = vec4(v1.xyz, 1.); + pivotHigh = vec4(v2.xyz, 1.); + translation = vec4(v3.xyz, 1.); + scale = vec4(v1.w, v2.w, v3.w, 1.); + #endif + #elif TRANSFORM_STORAGE == 1 + mat4 tMatrix = uTransforms[int(objIndex)]; + quaternion = tMatrix[0]; + pivotLow = vec4(tMatrix[1].xyz, 1.); + pivotHigh = vec4(tMatrix[2].xyz, 1.); + translation = vec4(tMatrix[3].xyz, 1.); + scale = vec4(tMatrix[1][3], tMatrix[2][3], tMatrix[3][3], 1.); + #endif + } + + vec3 rotate_vertex_position(vec3 position, vec4 quat) + { + return position + 2.0 * cross(quat.xyz, cross(quat.xyz, position) + quat.w * position); + } + + highp vec3 rotate_vertex_position_delta(highp vec4 v0, highp vec4 v1, highp vec4 quat) + { + /** !!! WORKAROUND FOR Intel IrisXe CARDS !!! */ + /** The code below will not produce correct results in intel IrisXE integrated GPUs. + * The geometry will turn mangled, albeit stable + * I can't know for sure what is going on, but rotating the difference seems to + * force the result into a lower precision? + */ + // highp vec4 position = v0 - v1; + // return position.xyz + 2.0 * cross(quat.xyz, cross(quat.xyz, position.xyz) + quat.w * position.xyz); + + /** Subtracting the rotated vectors works. */ + return rotate_vertex_position(v0.xyz, quat) - rotate_vertex_position(v1.xyz, quat); + + /** An alternate workaround is + * highp vec3 position = (v0.xyz * (1. + 1e-7)) - (v1.xyz * (1. _ 1e-7)); + return position + 2.0 * cross(quat.xyz, cross(quat.xyz, position) + quat.w * position); + + However I'm not such a fan of the (1. + 1e-7) part + */ + } +#endif + +#ifdef USE_RTE + highp vec4 computeRelativePosition(in highp vec3 position_low, in highp vec3 position_high, in highp vec3 relativeTo_low, in highp vec3 relativeTo_high){ + /* + Source https://github.com/virtualglobebook/OpenGlobe/blob/master/Source/Examples/Chapter05/Jitter/GPURelativeToEyeDSFUN90/Shaders/VS.glsl + Note here, we're storing the high part of the position encoding inside three's default 'position' attribute buffer so we avoid redundancy + */ + highp vec3 t1 = position_low.xyz - relativeTo_low.xyz; + highp vec3 e = t1 - position_low.xyz; + /** This is redunant, but necessary as a workaround for Apple platforms */ + highp float x = position_high.x - relativeTo_high.x; + highp float y = position_high.y - relativeTo_high.y; + highp float z = position_high.z - relativeTo_high.z; + highp vec3 v = vec3(x, y, z); + /** End of redundant part */ + highp vec3 t2 = ((-relativeTo_low - e) + (position_low.xyz - (t1 - e))) + v; + highp vec3 highDifference = t1 + t2; + highp vec3 lowDifference = t2 - (highDifference.xyz - t1.xyz); + + highp vec3 position = highDifference.xyz + lowDifference.xyz; + return vec4(position, 1.); + } +#endif + + +/** Original glsl100 and glsl300 has functions. Good outputs but maybe a bit slow? */ +/* +#if __VERSION__ == 300 + vec3 hashColor(uint id) { + // A simple integer hash function + id = (id ^ 61u) ^ (id >> 16u); + id = id * 9u; + id = id ^ (id >> 4u); + id = id * 0x27d4eb2du; + id = id ^ (id >> 15u); + + return vec3( + float((id >> 16u) & 0xFFu) / 255.0, + float((id >> 8u) & 0xFFu) / 255.0, + float(id & 0xFFu) / 255.0 + ); + } +#elif __VERSION__ == 100 + vec3 hashColor(float id) { + // Step 1: Simulate XOR by using mod and floating-point arithmetic + id = mod(id + 61.0, 4294967296.0); + id = mod(id - floor(id / 65536.0), 4294967296.0); // Approximate id ^ (id >> 16) + // Step 2: Multiply by 9 (same as original) + id = mod(id * 9.0, 4294967296.0); + // Step 3: Simulate XOR with division/mod trick + id = mod(id - floor(id / 16.0), 4294967296.0); // Approximate id ^ (id >> 4) + // Step 4: Multiply by large prime + id = mod(id * 666083407.0, 4294967296.0); // Approximate * 0x27d4eb2dU + // Step 5: Simulate final XOR + id = mod(id - floor(id / 32768.0), 4294967296.0); // Approximate id ^ (id >> 15) + // Convert hash to RGB by extracting "fake" bit shifts + return vec3( + mod(floor(id / 65536.0), 256.0) / 255.0, // Simulates (id >> 16) & 0xFF + mod(floor(id / 256.0), 256.0) / 255.0, // Simulates (id >> 8) & 0xFF + mod(id, 256.0) / 255.0 // Simulates id & 0xFF + ); + } +#endif +*/ + +/** Simpler hash function works on both glsl versions */ +highp vec3 hashColor(float id) { + // Large prime multipliers + highp float r = mod(id * 127.1, 256.0) / 255.0; + highp float g = mod(id * 987.654, 256.0) / 255.0; + highp float b = mod(id * 4321.123, 256.0) / 255.0; + + return vec3(r, g, b); +} + +int szudzikHash(int x, int y) { + return (x >= y) ? (x * x + x + y) : (y * y + x); +} + +void main() { + #include + #include + #include + #ifdef USE_DISPLACEMENTMAP + #include + #include + #endif + #include + #include + #include + #include + #include + #include + //#include // EDITED CHUNK + #ifdef TRANSFORM_STORAGE + vec4 tQuaternion, tPivotLow, tPivotHigh, tTranslation, tScale; + objectTransform(tQuaternion, tPivotLow, tPivotHigh, tTranslation, tScale); + #endif + #ifdef USE_RTE + vec4 position_lowT = vec4(position_low, 1.); + vec4 position_highT = vec4(position, 1.); + const vec3 ZERO3 = vec3(0., 0., 0.); + + vec4 rteLocalPosition = computeRelativePosition(position_lowT.xyz, position_highT.xyz, uViewer_low, uViewer_high); + #ifdef TRANSFORM_STORAGE + vec4 rtePivot = computeRelativePosition(tPivotLow.xyz, tPivotHigh.xyz, uViewer_low, uViewer_high); + rteLocalPosition.xyz = rotate_vertex_position_delta(rteLocalPosition, rtePivot, tQuaternion) * tScale.xyz + rtePivot.xyz + tTranslation.xyz; + #endif + #ifdef USE_INSTANCING + vec4 instancePivot = computeRelativePosition(ZERO3, ZERO3, uViewer_low, uViewer_high); + rteLocalPosition.xyz = (mat3(instanceMatrix) * (rteLocalPosition - instancePivot).xyz) + instancePivot.xyz + instanceMatrix[3].xyz; + #endif + #endif + + #ifdef USE_RTE + vec4 mvPosition = rteLocalPosition; + #else + vec4 mvPosition = vec4( transformed, 1.0 ); + #ifdef USE_INSTANCING + mvPosition = instanceMatrix * mvPosition; + #endif + #endif + + #ifdef USE_RTE + mvPosition = rteModelViewMatrix * mvPosition; + #else + mvPosition = modelViewMatrix * mvPosition; + #endif + + #ifdef LINEAR_DEPTH + vViewPosition = mvPosition; + #endif + + vNormal = normalize( transformedNormal ); + + #ifdef TRANSFORM_STORAGE + vIdColor = hashColor(float(szudzikHash(int(objIndex), batchIndex))); + #else + #if defined( USE_INSTANCING ) + #if __VERSION__ == 300 + vIdColor = hashColor(float(szudzikHash(int(gl_InstanceID), batchIndex))); + #elif __VERSION__ == 100 + vIdColor = hashColor(float(szudzikHash(int(objIndex), batchIndex))); + #endif + #else + vIdColor = vec3(0.); + #endif + #endif + + + gl_Position = projectionMatrix * mvPosition; + #include + // #include + #if NUM_CLIPPING_PLANES > 0 + vClipPosition = - mvPosition.xyz; + #endif + vHighPrecisionZW = gl_Position.zw; +} +` diff --git a/packages/viewer/src/modules/materials/shaders/speckle-edges-generator-frag.ts b/packages/viewer/src/modules/materials/shaders/speckle-edges-generator-frag.ts index a50f3d8c6..9dabbe76d 100644 --- a/packages/viewer/src/modules/materials/shaders/speckle-edges-generator-frag.ts +++ b/packages/viewer/src/modules/materials/shaders/speckle-edges-generator-frag.ts @@ -3,6 +3,7 @@ export const speckleEdgesGeneratorFrag = /* glsl */ ` varying vec2 vUv; uniform sampler2D tDepth; uniform sampler2D tNormal; +uniform sampler2D tId; uniform float uDepthMultiplier; uniform float uDepthBias; uniform float uNormalMultiplier; @@ -16,8 +17,7 @@ uniform float cameraFar; uniform mat4 cameraProjectionMatrix; uniform mat4 cameraInverseProjectionMatrix; -#define NORMALS_TYPE 0 -// RGBA depth +#define ID_GRADIENT_THRESHOLD 1e-4 #include @@ -26,163 +26,18 @@ float getDepth( const in ivec2 screenPosition ) { return unpackRGBAToDepth( texelFetch( tDepth, screenPosition, 0 ) ); #else vec2 cUv = vec2(0.5/size.x, 0.5/size.y); - return unpackRGBAToDepth( texture2D( tDepth, vec2(screenPosition) + cUv ) ); + return unpackRGBAToDepth( texture2D( tDepth, vec2(screenPosition)/size + cUv ) ); #endif } -// float getPerspectiveDepth(const in vec2 coords) { -// float linearDepth = unpackRGBAToDepth( texture2D( tDepth, coords ) ); -// #if PERSPECTIVE_CAMERA == 1 -// float viewZ = orthographicDepthToViewZ(linearDepth, cameraNear, cameraFar); -// float centerDepth = viewZToPerspectiveDepth(viewZ, cameraNear, cameraFar); -// return centerDepth; -// #else -// return linearDepth; -// #endif -// } -// float getViewDepth(const in float linearDepth) { -// return orthographicDepthToViewZ(linearDepth, cameraNear, cameraFar); -// } - -// float getViewZ( const in float depth ) { -// #if PERSPECTIVE_CAMERA == 1 -// return perspectiveDepthToViewZ( depth, cameraNear, cameraFar ); -// #else -// return orthographicDepthToViewZ( depth, cameraNear, cameraFar ); -// #endif -// } - -// vec3 getViewPosition( const in vec2 screenPosition, const in float depth, const in float viewZ ) { -// float clipW = cameraProjectionMatrix[2][3] * viewZ + cameraProjectionMatrix[3][3]; -// vec4 clipPosition = vec4( ( vec3( screenPosition, depth ) - 0.5 ) * 2.0, 1.0 ); -// clipPosition *= clipW; // unprojection. -// return ( cameraInverseProjectionMatrix * clipPosition ).xyz; -// } - -// //https://wickedengine.net/2019/09/22/improved-normal-reconstruction-from-depth/ -// vec3 viewNormalImproved(in vec2 uv, in vec3 origin) -// { -// highp vec2 dd = abs(vec2(1./size.x, 1./size.y)); -// highp vec2 ddx = vec2(dd.x, 0.); -// highp vec2 ddy = vec2(0., dd.y); - -// float sampleDepth = getPerspectiveDepth( uv - ddy ); -// float sampleViewZ = getViewZ( sampleDepth ); -// highp vec3 top = getViewPosition( uv - ddy, sampleDepth, sampleViewZ ); - -// sampleDepth = getPerspectiveDepth( uv + ddy ); -// sampleViewZ = getViewZ( sampleDepth ); -// highp vec3 bottom = getViewPosition( uv + ddy, sampleDepth, sampleViewZ ); - -// highp vec3 center = origin; - -// sampleDepth = getPerspectiveDepth( uv - ddx ); -// sampleViewZ = getViewZ( sampleDepth ); -// highp vec3 left = getViewPosition( uv - ddx, sampleDepth, sampleViewZ ); - -// sampleDepth = getPerspectiveDepth( uv + ddx ); -// sampleViewZ = getViewZ( sampleDepth ); -// highp vec3 right = getViewPosition( uv + ddx, sampleDepth, sampleViewZ ); - -// // get the difference between the current and each offset position -// vec3 l = center - left; -// vec3 r = right - center; -// vec3 d = center - top; -// vec3 u = bottom - center; - -// // pick horizontal and vertical diff with the smallest z difference -// vec3 hDeriv = abs(l.z) < abs(r.z) ? l : r; -// vec3 vDeriv = abs(d.z) < abs(u.z) ? d : u; - -// // get view space normal from the cross product of the two smallest offsets -// vec3 viewNormal = normalize(cross(hDeriv, vDeriv)); - -// return viewNormal; -// } - -// vec3 viewNormalAccurate(in vec2 uv, in vec3 origin, in float centerDepth) { -// highp vec2 dd = abs(vec2(1./size.x, 1./size.y)); -// highp vec2 ddx = vec2(dd.x, 0.); -// highp vec2 ddy = vec2(0., dd.y); - -// float sampleDepth = getPerspectiveDepth( uv - ddy ); -// float sampleViewZ = getViewZ( sampleDepth ); -// highp vec3 top = getViewPosition( uv - ddy, sampleDepth, sampleViewZ ); - -// sampleDepth = getPerspectiveDepth( uv + ddy ); -// sampleViewZ = getViewZ( sampleDepth ); -// highp vec3 bottom = getViewPosition( uv + ddy, sampleDepth, sampleViewZ ); - -// highp vec3 center = origin; - -// sampleDepth = getPerspectiveDepth( uv - ddx ); -// sampleViewZ = getViewZ( sampleDepth ); -// highp vec3 left = getViewPosition( uv - ddx, sampleDepth, sampleViewZ ); - -// sampleDepth = getPerspectiveDepth( uv + ddx ); -// sampleViewZ = getViewZ( sampleDepth ); -// highp vec3 right = getViewPosition( uv + ddx, sampleDepth, sampleViewZ ); - -// // get the difference between the current and each offset position -// vec3 l = center - left; -// vec3 r = right - center; -// vec3 d = center - top; -// vec3 u = bottom - center; - -// // get depth values at 1 & 2 pixels offsets from current along the horizontal axis -// vec4 H = vec4( -// getLinearDepth(uv - ddx), -// getLinearDepth(uv + ddx), -// getLinearDepth(uv - 2. * ddx), -// getLinearDepth(uv + 2. * ddx) -// ); - -// // get depth values at 1 & 2 pixels offsets from current along the vertical axis -// vec4 V = vec4( -// getLinearDepth(uv - ddy), -// getLinearDepth(uv + ddy), -// getLinearDepth(uv - 2. * ddy), -// getLinearDepth(uv + 2. * ddy) -// ); - -// // current pixel's depth difference from slope of offset depth samples -// // differs from original article because we're using non-linear depth values -// // see article's comments -// vec2 he = abs((2. * H.xy - H.zw) - centerDepth); -// vec2 ve = abs((2. * V.xy - V.zw) - centerDepth); - -// // pick horizontal and vertical diff with the smallest depth difference from slopes -// vec3 hDeriv = he.x < he.y ? l : r; -// vec3 vDeriv = ve.x < ve.y ? d : u; - -// // get view space normal from the cross product of the best derivatives -// vec3 viewNormal = normalize(cross(hDeriv, vDeriv)); - -// return viewNormal; - -// } - -// vec3 getViewNormal( const in vec3 viewPosition, const in vec2 screenPosition, in float centerDepth ) { -// #if NORMALS_TYPE == 0 -// return unpackRGBToNormal( texture2D( tNormal, screenPosition ).xyz ); -// #elif NORMALS_TYPE == 1 -// return viewNormalImproved(screenPosition, viewPosition); -// #elif NORMALS_TYPE == 2 -// return viewNormalAccurate(screenPosition, viewPosition, centerDepth); -// #else -// return normalize( cross( dFdx( viewPosition ), dFdy( viewPosition ) ) ); -// #endif -// } - - -vec3 SobelSampleNormal(sampler2D t, vec2 uv, vec3 offset){ - vec3 pixelCenter = unpackRGBToNormal(texture2D(t, uv).rgb); - vec3 pixelLeft = unpackRGBToNormal(texture2D(t, uv - offset.xz).rgb); - vec3 pixelRight = unpackRGBToNormal(texture2D(t, uv + offset.xz).rgb); - vec3 pixelUp = unpackRGBToNormal(texture2D(t, uv + offset.zy).rgb); - vec3 pixelDown = unpackRGBToNormal(texture2D(t, uv - offset.zy).rgb); +vec3 SobelSample(sampler2D t, vec2 uv, vec3 offset){ + vec3 pixelCenter = texture2D(t, uv).rgb; + vec3 pixelLeft = texture2D(t, uv - offset.xz).rgb; + vec3 pixelRight = texture2D(t, uv + offset.xz).rgb; + vec3 pixelUp = texture2D(t, uv + offset.zy).rgb; + vec3 pixelDown = texture2D(t, uv - offset.zy).rgb; return abs(pixelLeft - pixelCenter) + abs(pixelRight - pixelCenter) + @@ -190,58 +45,6 @@ vec3 SobelSampleNormal(sampler2D t, vec2 uv, vec3 offset){ abs(pixelDown - pixelCenter); } -vec3 SobelSampleNormal2(vec2 uv){ - float w = 1.0 / size.x; - float h = 1.0 / size.y; - vec3 n[9]; - n[0] = unpackRGBToNormal(texture2D(tNormal, uv + vec2( -w, -h)).rgb); - n[1] = unpackRGBToNormal(texture2D(tNormal, uv + vec2(0.0, -h)).rgb); - n[2] = unpackRGBToNormal(texture2D(tNormal, uv + vec2( w, -h)).rgb); - n[3] = unpackRGBToNormal(texture2D(tNormal, uv + vec2( -w, 0.0)).rgb); - n[4] = unpackRGBToNormal(texture2D(tNormal, uv).rgb); - n[5] = unpackRGBToNormal(texture2D(tNormal, uv + vec2( w, 0.0)).rgb); - n[6] = unpackRGBToNormal(texture2D(tNormal, uv + vec2( -w, h)).rgb); - n[7] = unpackRGBToNormal(texture2D(tNormal, uv + vec2(0.0, h)).rgb); - n[8] = unpackRGBToNormal(texture2D(tNormal, uv + vec2( w, h)).rgb); - - vec3 sobel_edge_h = n[2] + (2.0*n[5]) + n[8] - (n[0] + (2.0*n[3]) + n[6]); - vec3 sobel_edge_v = n[0] + (2.0*n[1]) + n[2] - (n[6] + (2.0*n[7]) + n[8]); - vec3 sobel = sqrt((sobel_edge_h * sobel_edge_h) + (sobel_edge_v * sobel_edge_v)); - return sobel; -} - -float SobelSampleDepth(vec2 uv, vec3 offset){ - float pixelCenter = orthographicDepthToViewZ(getDepth(ivec2(uv*size)), cameraNear, cameraFar); - float pixelLeft = orthographicDepthToViewZ(getDepth(ivec2((uv - offset.xz) * size)), cameraNear, cameraFar); - float pixelRight = orthographicDepthToViewZ(getDepth(ivec2((uv + offset.xz) * size)), cameraNear, cameraFar); - float pixelUp = orthographicDepthToViewZ(getDepth(ivec2((uv + offset.zy) * size)), cameraNear, cameraFar); - float pixelDown = orthographicDepthToViewZ(getDepth(ivec2((uv - offset.zy) * size)), cameraNear, cameraFar); - - return abs(pixelLeft - pixelCenter) + - abs(pixelRight - pixelCenter) + - abs(pixelUp - pixelCenter) + - abs(pixelDown - pixelCenter); -} - -// float SobelSampleDepth2(vec2 uv){ -// float w = 1.0 / size.x * uOutlineDensity; -// float h = 1.0 / size.y * uOutlineThickness; -// float n[9]; -// n[0] = getLinearDepth( uv + vec2( -w, -h)); -// n[1] = getLinearDepth( uv + vec2(0.0, -h)); -// n[2] = getLinearDepth( uv + vec2( w, -h)); -// n[3] = getLinearDepth( uv + vec2( -w, 0.0)); -// n[4] = getLinearDepth( uv); -// n[5] = getLinearDepth( uv + vec2( w, 0.0)); -// n[6] = getLinearDepth( uv + vec2( -w, h)); -// n[7] = getLinearDepth( uv + vec2(0.0, h)); -// n[8] = getLinearDepth( uv + vec2( w, h)); - -// float sobel_edge_h = n[2] + (2.0*n[5]) + n[8] - (n[0] + (2.0*n[3]) + n[6]); -// float sobel_edge_v = n[0] + (2.0*n[1]) + n[2] - (n[6] + (2.0*n[7]) + n[8]); -// float sobel = sqrt((sobel_edge_h * sobel_edge_h) + (sobel_edge_v * sobel_edge_v)); -// return sobel; -// } float GetTolerance(float d, float k) { @@ -291,7 +94,8 @@ float DetectSilho(ivec2 fragCoord, ivec2 dir, float tolerance) } -float DetectSilho(ivec2 fragCoord, float tolerance) +// Source: https://www.shadertoy.com/view/DslXz2 +float DepthEdge(ivec2 fragCoord, float tolerance) { return max( DetectSilho(fragCoord, ivec2(1,0), tolerance), // Horizontal @@ -323,175 +127,28 @@ float NormalEdge(float scale) return sqrt(dot(normalFiniteDifference0, normalFiniteDifference0) + dot(normalFiniteDifference1, normalFiniteDifference1)); } -vec2 rotate2D(vec2 v, float rad) { - float s = sin(rad); - float c = cos(rad); - return mat2(c, s, -s, c) * v; +/** Alternative to NormalEdge. */ +vec3 SobelSampleNormal(vec2 uv){ + float w = 1.0 / size.x; + float h = 1.0 / size.y; + vec3 n[9]; + n[0] = unpackRGBToNormal(texture2D(tNormal, uv + vec2( -w, -h)).rgb); + n[1] = unpackRGBToNormal(texture2D(tNormal, uv + vec2(0.0, -h)).rgb); + n[2] = unpackRGBToNormal(texture2D(tNormal, uv + vec2( w, -h)).rgb); + n[3] = unpackRGBToNormal(texture2D(tNormal, uv + vec2( -w, 0.0)).rgb); + n[4] = unpackRGBToNormal(texture2D(tNormal, uv).rgb); + n[5] = unpackRGBToNormal(texture2D(tNormal, uv + vec2( w, 0.0)).rgb); + n[6] = unpackRGBToNormal(texture2D(tNormal, uv + vec2( -w, h)).rgb); + n[7] = unpackRGBToNormal(texture2D(tNormal, uv + vec2(0.0, h)).rgb); + n[8] = unpackRGBToNormal(texture2D(tNormal, uv + vec2( w, h)).rgb); + + vec3 sobel_edge_h = n[2] + (2.0*n[5]) + n[8] - (n[0] + (2.0*n[3]) + n[6]); + vec3 sobel_edge_v = n[0] + (2.0*n[1]) + n[2] - (n[6] + (2.0*n[7]) + n[8]); + vec3 sobel = sqrt((sobel_edge_h * sobel_edge_h) + (sobel_edge_v * sobel_edge_v)); + return sobel; } -/** - * Return a vector with the same length as v - * but its direction is rounded to the nearest - * of 8 cardinal directions - */ -vec2 round2DVectorAngle(vec2 v) { - float len = length(v); - vec2 n = normalize(v); - float maximum = -1.; - float bestAngle; - for (int i = 0; i < 8; i++) { - float theta = (float(i) * 2. * PI) / 8.; - vec2 u = rotate2D(vec2(1., 0.), theta); - float scalarProduct = dot(u, n); - if (scalarProduct > maximum) { - bestAngle = theta; - maximum = scalarProduct; - } - } - return len * rotate2D(vec2(1., 0.), bestAngle); -} - -/** - * Apply a double threshold to each edge to classify each edge - * as a weak edge or a strong edge - */ -float applyDoubleThreshold( - vec2 gradient, - float weakThreshold, - float strongThreshold -) { - float gradientLength = gradient.x;//length(gradient); - if (gradientLength < weakThreshold) return 0.; - if (gradientLength < strongThreshold) return .5; - return 1.; -} - -const mat3 X_COMPONENT_MATRIX = mat3( - 1., 0., -1., - 2., 0., -2., - 1., 0., -1. -); -const mat3 Y_COMPONENT_MATRIX = mat3( - 1., 2., 1., - 0., 0., 0., - -1., -2., -1. -); - -/** - * 3x3 Matrix convolution - */ -float convoluteMatrices(mat3 A, mat3 B) { - return dot(A[0], B[0]) + dot(A[1], B[1]) + dot(A[2], B[2]); -} - - -/** - * Get the intensity of the color on a - * texture after a guassian blur is applied - */ -float getTextureIntensity( - sampler2D textureSampler, - vec2 textureCoord, - vec2 resolution -) { - vec3 color = unpackRGBToNormal(texture2D(textureSampler, textureCoord).rgb); - return pow(length(clamp(color, vec3(0.), vec3(1.))), 2.) / 3.; -} - -/** - * Get the gradient of the textures intensity - * as a function of the texture coordinate - */ -vec2 getTextureIntensityGradient( - sampler2D textureSampler, - vec2 textureCoord, - vec2 resolution -) { -// vec2 gradientStep = vec2(1.) / resolution; - -// mat3 imgMat = mat3(0.); - -// for (int i = 0; i < 3; i++) { -// for (int j = 0; j < 3; j++) { -// vec2 ds = vec2( -// -gradientStep.x + (float(i) * gradientStep.x), -// -gradientStep.y + (float(j) * gradientStep.y)); -// imgMat[i][j] = getTextureIntensity( -// textureSampler, clamp(textureCoord + ds, vec2(0.), vec2(1.)), resolution); -// } -// } - -// float gradX = convoluteMatrices(X_COMPONENT_MATRIX, imgMat); -// float gradY = convoluteMatrices(Y_COMPONENT_MATRIX, imgMat); - -// return vec2(gradX, gradY); - float edge = NormalEdge(uOutlineThickness); - return vec2(edge, edge); -} - -/** - * Get the texture intensity gradient of an image - * where the angle of the direction is rounded to - * one of the 8 cardinal directions and gradients - * that are not local extrema are zeroed out - */ -vec2 getSuppressedTextureIntensityGradient( - sampler2D textureSampler, - vec2 textureCoord, - vec2 resolution -) { - vec2 gradient = getTextureIntensityGradient(textureSampler, textureCoord, resolution); -// gradient = round2DVectorAngle(gradient); -// vec2 gradientStep = normalize(gradient) / resolution; -// float gradientLength = length(gradient); -// vec2 gradientPlusStep = getTextureIntensityGradient( -// textureSampler, textureCoord + gradientStep, resolution); -// if (length(gradientPlusStep) >= gradientLength) return vec2(0.); -// vec2 gradientMinusStep = getTextureIntensityGradient( -// textureSampler, textureCoord - gradientStep, resolution); -// if (length(gradientMinusStep) >= gradientLength) return vec2(0.); - return gradient; -} - -float applyHysteresis( - sampler2D textureSampler, - vec2 textureCoord, - vec2 resolution, - float weakThreshold, - float strongThreshold -) { - float dx = 1. / resolution.x; - float dy = 1. / resolution.y; - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - vec2 ds = vec2( - -dx + (float(i) * dx), - -dy + (float(j) * dy)); - vec2 gradient = getSuppressedTextureIntensityGradient( - textureSampler, clamp(textureCoord + ds, vec2(0.), vec2(1.)), resolution); - float edge = applyDoubleThreshold(gradient, weakThreshold, strongThreshold); - if (edge == 1.) return 1.; - } - } - return 0.; -} - -float cannyEdgeDetection( - sampler2D textureSampler, - vec2 textureCoord, - vec2 resolution, - float weakThreshold, - float strongThreshold -) { - vec2 gradient = getSuppressedTextureIntensityGradient(textureSampler, textureCoord, resolution); - float edge = applyDoubleThreshold(gradient, weakThreshold, strongThreshold); - if (edge == .5) { - edge = applyHysteresis( - textureSampler, textureCoord, resolution, weakThreshold, strongThreshold); - } - return edge; -} #ifdef TEXTURE_BACKGROUND uniform sampler2D tBackground; @@ -500,19 +157,20 @@ float cannyEdgeDetection( void main() { - // Silhouette-edge value - float depthEdge = DetectSilho(ivec2(gl_FragCoord), uDepthBias) * uDepthMultiplier; + // Depth edge + float depthEdge = DepthEdge(ivec2(gl_FragCoord), uDepthBias) * uDepthMultiplier; + // Normal edge float normalEdge = pow(NormalEdge(uOutlineThickness) * uNormalMultiplier, uNormalBias); - // vec3 offset = vec3((1.0 / size.x), (1.0 / size.y), 0.0) * uOutlineThickness; - // float sobel = SobelSampleDepth(vUv, offset); - // sobel = pow(abs(saturate(sobel) * uDepthMultiplier), uDepthBias); - - // vec3 sobelNormalVec = abs(SobelSampleNormal(tNormal, vUv, offset)); - // float sobelNormal = sobelNormalVec.x + sobelNormalVec.y + sobelNormalVec.z; - // sobelNormal = pow(abs(sobelNormal * uNormalMultiplier), uNormalBias); - float maxOutline = saturate(max(depthEdge, normalEdge)); + // Id edge + vec3 offset = vec3((1.0 / size.x), (1.0 / size.y), 0.0) * uOutlineThickness; + vec3 sobelIdVec = abs(SobelSample(tId, vUv, offset)); + // This is the branchless equivalent of sobelIdVec.x + sobelIdVec.y + sobelIdVec.z > ID_GRADIENT_THRESHOLD ? 1. : 0. + float sobelIdEdge = step(ID_GRADIENT_THRESHOLD, sobelIdVec.x + sobelIdVec.y + sobelIdVec.z); + + // Combine the three edges by taking the minimum + float maxOutline = saturate(max(sobelIdEdge, max(depthEdge, normalEdge))); + // Invert float sobelOutline = 1. - maxOutline * uOutlineDensity; - // float canny = cannyEdgeDetection(tNormal, vUv, size, uDepthMultiplier, uDepthBias) * uOutlineDensity; vec4 background = vec4(1.); float backgroundIntensity = 1.; diff --git a/packages/viewer/src/modules/materials/shaders/speckle-standard-frag.ts b/packages/viewer/src/modules/materials/shaders/speckle-standard-frag.ts index 3cc47e03c..f446f7c97 100644 --- a/packages/viewer/src/modules/materials/shaders/speckle-standard-frag.ts +++ b/packages/viewer/src/modules/materials/shaders/speckle-standard-frag.ts @@ -136,6 +136,7 @@ varying vec3 vViewPosition; #include #include + void main() { #include @@ -209,6 +210,7 @@ void main() { #ifdef CUSTOM_TONEMAPPING gl_FragColor.rgb = postProcess(gl_FragColor.rgb, toneMappingExposure, contrast, saturation); #else + gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); #endif #endif diff --git a/packages/viewer/src/modules/materials/shaders/speckle-standard-vert.ts b/packages/viewer/src/modules/materials/shaders/speckle-standard-vert.ts index a10534f92..6e0481eeb 100644 --- a/packages/viewer/src/modules/materials/shaders/speckle-standard-vert.ts +++ b/packages/viewer/src/modules/materials/shaders/speckle-standard-vert.ts @@ -12,7 +12,6 @@ export const speckleStandardVert = /* glsl */ ` #ifdef TRANSFORM_STORAGE attribute float objIndex; - #if TRANSFORM_STORAGE == 0 #if __VERSION__ == 300 #define TRANSFORM_STRIDE 4 @@ -139,6 +138,7 @@ varying vec3 vViewPosition; } #endif + void main() { #include diff --git a/packages/viewer/src/modules/objects/ExtendedInstancedMesh.ts b/packages/viewer/src/modules/objects/ExtendedInstancedMesh.ts new file mode 100644 index 000000000..74958bf59 --- /dev/null +++ b/packages/viewer/src/modules/objects/ExtendedInstancedMesh.ts @@ -0,0 +1,22 @@ +import { BufferGeometry, InstancedMesh, Material } from 'three' + +export class ExtendedInstancedMesh extends InstancedMesh< + BufferGeometry, + Material | Material[] +> { + private _batchIndex: number + + public constructor( + geometry: BufferGeometry | undefined, + material: Material | Material[] | undefined, + count: number, + batchIndex: number + ) { + super(geometry, material, count) + this._batchIndex = batchIndex + } + + public get batchIndex(): number { + return this._batchIndex + } +} diff --git a/packages/viewer/src/modules/objects/SpeckleInstancedMesh.ts b/packages/viewer/src/modules/objects/SpeckleInstancedMesh.ts index 8c03f99f1..2daebce9c 100644 --- a/packages/viewer/src/modules/objects/SpeckleInstancedMesh.ts +++ b/packages/viewer/src/modules/objects/SpeckleInstancedMesh.ts @@ -25,12 +25,14 @@ import { TopLevelAccelerationStructure } from './TopLevelAccelerationStructure.j import { ObjectLayers } from '../../IViewer.js' import { type DrawGroup, + getNextBatchIndex, INSTANCE_GRADIENT_BUFFER_STRIDE, INSTANCE_TRANSFORM_BUFFER_STRIDE } from '../batching/Batch.js' import { SpeckleRaycaster } from './SpeckleRaycaster.js' import Logger from '../utils/Logger.js' import SpeckleDepthMaterial from '../materials/SpeckleDepthMaterial.js' +import { ExtendedInstancedMesh } from './ExtendedInstancedMesh.js' const _inverseMatrix = new Matrix4() const _ray = new Ray() @@ -68,7 +70,7 @@ export default class SpeckleInstancedMesh extends Group { private batchMaterialStack: Array = [] private materialCacheLUT: { [id: string]: number } = {} - private _batchObjects!: BatchObject[] + private _batchObjects: BatchObject[] public groups: Array = [] public materials: Material[] = [] @@ -182,7 +184,11 @@ export default class SpeckleInstancedMesh extends Group { this.tas.refit() } - public updateDrawGroups(transformBuffer: Float32Array, gradientBuffer: Float32Array) { + public updateDrawGroups( + transformBuffer: Float32Array, + gradientBuffer: Float32Array, + objectIndexBuffer?: Float32Array + ) { this.instances.forEach((value: InstancedMesh) => { this.remove(value) value.customDepthMaterial?.dispose() @@ -194,10 +200,11 @@ export default class SpeckleInstancedMesh extends Group { const materialIndex = this.groups[k].materialIndex const material = this.materials[materialIndex] - const group = new InstancedMesh( + const group = new ExtendedInstancedMesh( this.getInstanceGeometryShallowCopy(), material, - 0 + 0, + getNextBatchIndex() ) group.instanceMatrix = new InstancedBufferAttribute( transformBuffer.subarray( @@ -217,6 +224,18 @@ export default class SpeckleInstancedMesh extends Group { INSTANCE_GRADIENT_BUFFER_STRIDE ) ) + if (objectIndexBuffer) + group.geometry.setAttribute( + 'objIndex', + new InstancedBufferAttribute( + objectIndexBuffer.subarray( + this.groups[k].start / INSTANCE_TRANSFORM_BUFFER_STRIDE, + (this.groups[k].start + this.groups[k].count) / + INSTANCE_TRANSFORM_BUFFER_STRIDE + ), + INSTANCE_GRADIENT_BUFFER_STRIDE + ) + ) group.count = this.groups[k].count / INSTANCE_TRANSFORM_BUFFER_STRIDE group.instanceMatrix.needsUpdate = true group.layers.set(ObjectLayers.STREAM_CONTENT_MESH) diff --git a/packages/viewer/src/modules/objects/SpeckleMesh.ts b/packages/viewer/src/modules/objects/SpeckleMesh.ts index 8dc10a217..175e72d56 100644 --- a/packages/viewer/src/modules/objects/SpeckleMesh.ts +++ b/packages/viewer/src/modules/objects/SpeckleMesh.ts @@ -25,6 +25,7 @@ import Materials from '../materials/Materials.js' import { TopLevelAccelerationStructure } from './TopLevelAccelerationStructure.js' import { SpeckleRaycaster } from './SpeckleRaycaster.js' import Logger from '../utils/Logger.js' +import { getNextBatchIndex } from '../batching/Batch.js' const _inverseMatrix = new Matrix4() const _ray = new Ray() @@ -67,9 +68,10 @@ export default class SpeckleMesh extends Mesh { private batchMaterialStack: Array = [] private materialCacheLUT: { [id: string]: number } = {} - private _batchObjects!: BatchObject[] + private _batchObjects: BatchObject[] + private _batchIndex: number private transformsBuffer: Float32Array | undefined = undefined - private transformStorage!: TransformStorage + private transformStorage: TransformStorage public transformsTextureUniform: DataTexture public transformsArrayUniforms: Matrix4[] | null = null @@ -82,8 +84,13 @@ export default class SpeckleMesh extends Mesh { return this._batchObjects } + public get batchIndex(): number { + return this._batchIndex + } + constructor(geometry: BufferGeometry) { super(geometry) + this._batchIndex = getNextBatchIndex() } public setBatchMaterial(material: Material) { diff --git a/packages/viewer/src/modules/pipeline/Passes/DepthNormalIdPass.ts b/packages/viewer/src/modules/pipeline/Passes/DepthNormalIdPass.ts new file mode 100644 index 000000000..5996e37e0 --- /dev/null +++ b/packages/viewer/src/modules/pipeline/Passes/DepthNormalIdPass.ts @@ -0,0 +1,52 @@ +import { DoubleSide, NearestFilter, NoBlending, RGBADepthPacking, Texture } from 'three' +import { Pipeline } from '../Pipelines/Pipeline.js' +import { + DefaultDepthNormalPassOptions, + DepthNormalPass, + DepthNormalPassOptions +} from './DepthNormalPass.js' +import SpeckleDepthNormalIdMaterial from '../../materials/SpeckleDepthNormalIdMaterial.js' + +export interface DepthNormalIdPassOptions extends DepthNormalPassOptions {} + +export const DefaultDepthNormalIdPassOptions: Required = { + ...DefaultDepthNormalPassOptions +} + +export class DepthNormalIdPass extends DepthNormalPass { + public _options: Required = Object.assign( + {}, + DefaultDepthNormalIdPassOptions + ) + + get displayName(): string { + return 'DEPTH-NORMAL-ID' + } + + get idTexture(): Texture { + return this.mrt.texture[2] + } + + public set options(value: DepthNormalIdPassOptions) { + super.options = value + } + + constructor() { + super() + + this.mrt = Pipeline.createMultipleRenderTarget(3, { + minFilter: NearestFilter, + magFilter: NearestFilter + }) + + this.mrtMaterial = new SpeckleDepthNormalIdMaterial( + { + depthPacking: RGBADepthPacking + }, + ['USE_RTE', 'ALPHATEST_REJECTION'] + ) + this.mrtMaterial.blending = NoBlending + this.mrtMaterial.side = DoubleSide + this.depthType = this._options.depthType + } +} diff --git a/packages/viewer/src/modules/pipeline/Passes/DepthNormalPass.ts b/packages/viewer/src/modules/pipeline/Passes/DepthNormalPass.ts index ee95d5bbc..7964e2e9b 100644 --- a/packages/viewer/src/modules/pipeline/Passes/DepthNormalPass.ts +++ b/packages/viewer/src/modules/pipeline/Passes/DepthNormalPass.ts @@ -25,8 +25,8 @@ export const DefaultDepthNormalPassOptions: Required = { } export class DepthNormalPass extends BaseGPass { - private depthNormalMaterial: SpeckleDepthNormalMaterial - private mrt: WebGLMultipleRenderTargets + protected mrtMaterial: SpeckleDepthNormalMaterial + protected mrt: WebGLMultipleRenderTargets public _options: Required = Object.assign( {}, @@ -38,7 +38,7 @@ export class DepthNormalPass extends BaseGPass { } get overrideMaterial(): Material { - return this.depthNormalMaterial + return this.mrtMaterial } get depthTexture(): Texture { @@ -60,14 +60,14 @@ export class DepthNormalPass extends BaseGPass { protected set depthType(value: DepthType) { if (value === DepthType.LINEAR_DEPTH) - if (this.depthNormalMaterial.defines) { - this.depthNormalMaterial.defines['LINEAR_DEPTH'] = ' ' + if (this.mrtMaterial.defines) { + this.mrtMaterial.defines['LINEAR_DEPTH'] = ' ' } else { - if (this.depthNormalMaterial.defines) { - delete this.depthNormalMaterial.defines['LINEAR_DEPTH'] + if (this.mrtMaterial.defines) { + delete this.mrtMaterial.defines['LINEAR_DEPTH'] } } - this.depthNormalMaterial.needsUpdate = true + this.mrtMaterial.needsUpdate = true } constructor() { @@ -78,25 +78,25 @@ export class DepthNormalPass extends BaseGPass { magFilter: NearestFilter }) - this.depthNormalMaterial = new SpeckleDepthNormalMaterial( + this.mrtMaterial = new SpeckleDepthNormalMaterial( { depthPacking: RGBADepthPacking }, ['USE_RTE', 'ALPHATEST_REJECTION'] ) - this.depthNormalMaterial.blending = NoBlending - this.depthNormalMaterial.side = DoubleSide + this.mrtMaterial.blending = NoBlending + this.mrtMaterial.side = DoubleSide this.depthType = this._options.depthType } public setClippingPlanes(planes: Plane[]) { - this.depthNormalMaterial.clippingPlanes = planes + this.mrtMaterial.clippingPlanes = planes } public update(camera: PerspectiveCamera | OrthographicCamera) { - this.depthNormalMaterial.userData.near.value = camera.near - this.depthNormalMaterial.userData.far.value = camera.far - this.depthNormalMaterial.needsUpdate = true + this.mrtMaterial.userData.near.value = camera.near + this.mrtMaterial.userData.far.value = camera.far + this.mrtMaterial.needsUpdate = true } public render( diff --git a/packages/viewer/src/modules/pipeline/Passes/EdgesPass.ts b/packages/viewer/src/modules/pipeline/Passes/EdgesPass.ts index 4fe0236ab..0527aed56 100644 --- a/packages/viewer/src/modules/pipeline/Passes/EdgesPass.ts +++ b/packages/viewer/src/modules/pipeline/Passes/EdgesPass.ts @@ -65,6 +65,7 @@ export class EdgePass extends BaseGPass { uniforms: { tDepth: { value: null }, tNormal: { value: null }, + tId: { value: null }, size: { value: new Vector2(512, 512) }, uDepthMultiplier: { value: this._options.depthMultiplier }, diff --git a/packages/viewer/src/modules/pipeline/Pipelines/MRT/MRTEdgesPipeline.ts b/packages/viewer/src/modules/pipeline/Pipelines/MRT/MRTEdgesPipeline.ts index 9a2ca2e6e..c80e0f5d9 100644 --- a/packages/viewer/src/modules/pipeline/Pipelines/MRT/MRTEdgesPipeline.ts +++ b/packages/viewer/src/modules/pipeline/Pipelines/MRT/MRTEdgesPipeline.ts @@ -9,24 +9,24 @@ import { ObjectLayers } from '../../../../IViewer.js' import { ProgressivePipeline } from '../ProgressivePipeline.js' import { StencilMaskPass } from '../../Passes/StencilMaskPass.js' import { StencilPass } from '../../Passes/StencilPass.js' -import { DepthNormalPass } from '../../Passes/DepthNormalPass.js' +import { DepthNormalIdPass } from '../../Passes/DepthNormalIdPass.js' export class MRTEdgesPipeline extends ProgressivePipeline { constructor(speckleRenderer: SpeckleRenderer) { super(speckleRenderer) - const depthNormalPass = new DepthNormalPass() - depthNormalPass.setLayers([ObjectLayers.STREAM_CONTENT_MESH]) - depthNormalPass.setVisibility(ObjectVisibility.DEPTH) - depthNormalPass.setJitter(true) - depthNormalPass.setClearColor(0x000000, 1) - depthNormalPass.setClearFlags(ClearFlags.COLOR | ClearFlags.DEPTH) + const depthNormalIdPass = new DepthNormalIdPass() + depthNormalIdPass.setLayers([ObjectLayers.STREAM_CONTENT_MESH]) + depthNormalIdPass.setVisibility(ObjectVisibility.DEPTH) + depthNormalIdPass.setJitter(true) + depthNormalIdPass.setClearColor(0x000000, 1) + depthNormalIdPass.setClearFlags(ClearFlags.COLOR | ClearFlags.DEPTH) - const depthPassNormalDynamic = new DepthNormalPass() - depthPassNormalDynamic.setLayers([ObjectLayers.STREAM_CONTENT_MESH]) - depthPassNormalDynamic.setVisibility(ObjectVisibility.DEPTH) - depthPassNormalDynamic.setClearColor(0x000000, 1) - depthPassNormalDynamic.setClearFlags(ClearFlags.COLOR | ClearFlags.DEPTH) + const depthPassNormalIdDynamic = new DepthNormalIdPass() + depthPassNormalIdDynamic.setLayers([ObjectLayers.STREAM_CONTENT_MESH]) + depthPassNormalIdDynamic.setVisibility(ObjectVisibility.DEPTH) + depthPassNormalIdDynamic.setClearColor(0x000000, 1) + depthPassNormalIdDynamic.setClearFlags(ClearFlags.COLOR | ClearFlags.DEPTH) const opaqueColorPass = new GeometryPass() opaqueColorPass.setLayers([ @@ -52,17 +52,19 @@ export class MRTEdgesPipeline extends ProgressivePipeline { transparentColorPass.setVisibility(ObjectVisibility.TRANSPARENT) const progressiveAOPass = new ProgressiveAOPass() - progressiveAOPass.setTexture('tDepth', depthNormalPass.depthTexture) + progressiveAOPass.setTexture('tDepth', depthNormalIdPass.depthTexture) progressiveAOPass.setClearColor(0xffffff, 1) progressiveAOPass.accumulationFrames = this.accumulationFrameCount const edgesPass = new EdgePass() - edgesPass.setTexture('tDepth', depthNormalPass.depthTexture) - edgesPass.setTexture('tNormal', depthNormalPass.normalTexture) + edgesPass.setTexture('tDepth', depthNormalIdPass.depthTexture) + edgesPass.setTexture('tNormal', depthNormalIdPass.normalTexture) + edgesPass.setTexture('tId', depthNormalIdPass.idTexture) const edgesPassDynamic = new EdgePass() - edgesPassDynamic.setTexture('tDepth', depthPassNormalDynamic.depthTexture) - edgesPassDynamic.setTexture('tNormal', depthPassNormalDynamic.normalTexture) + edgesPassDynamic.setTexture('tDepth', depthPassNormalIdDynamic.depthTexture) + edgesPassDynamic.setTexture('tNormal', depthPassNormalIdDynamic.normalTexture) + edgesPassDynamic.setTexture('tId', depthPassNormalIdDynamic.idTexture) const taaPass = new TAAPass() taaPass.inputTexture = edgesPass.outputTarget?.texture @@ -95,7 +97,7 @@ export class MRTEdgesPipeline extends ProgressivePipeline { overlayPass.setLayers([ObjectLayers.OVERLAY, ObjectLayers.MEASUREMENTS]) this.dynamicStage.push( - depthPassNormalDynamic, + depthPassNormalIdDynamic, edgesPassDynamic, stencilPass, opaqueColorPass, @@ -106,7 +108,7 @@ export class MRTEdgesPipeline extends ProgressivePipeline { overlayPass ) this.progressiveStage.push( - depthNormalPass, + depthNormalIdPass, edgesPass, progressiveAOPass, taaPass, diff --git a/packages/viewer/src/modules/pipeline/Pipelines/MRT/MRTPenViewPipeline.ts b/packages/viewer/src/modules/pipeline/Pipelines/MRT/MRTPenViewPipeline.ts index 16aca9891..ed9d57482 100644 --- a/packages/viewer/src/modules/pipeline/Pipelines/MRT/MRTPenViewPipeline.ts +++ b/packages/viewer/src/modules/pipeline/Pipelines/MRT/MRTPenViewPipeline.ts @@ -8,7 +8,6 @@ import { StencilMaskPass } from '../../Passes/StencilMaskPass.js' import { StencilPass } from '../../Passes/StencilPass.js' import { TAAPass } from '../../Passes/TAAPass.js' import { ProgressivePipeline } from '../ProgressivePipeline.js' -import { DepthNormalPass } from '../../Passes/DepthNormalPass.js' import SpeckleStandardMaterial from '../../../materials/SpeckleStandardMaterial.js' import { DoubleSide, @@ -18,31 +17,34 @@ import { Scene, WebGLRenderer } from 'three' +import { DepthNormalIdPass } from '../../Passes/DepthNormalIdPass.js' export class MRTPenViewPipeline extends ProgressivePipeline { constructor(speckleRenderer: SpeckleRenderer) { super(speckleRenderer) - const depthNormalPass = new DepthNormalPass() - depthNormalPass.setLayers([ObjectLayers.STREAM_CONTENT_MESH]) - depthNormalPass.setVisibility(ObjectVisibility.DEPTH) - depthNormalPass.setJitter(true) - depthNormalPass.setClearColor(0x000000, 1) - depthNormalPass.setClearFlags(ClearFlags.COLOR | ClearFlags.DEPTH) + const depthNormalIdPass = new DepthNormalIdPass() + depthNormalIdPass.setLayers([ObjectLayers.STREAM_CONTENT_MESH]) + depthNormalIdPass.setVisibility(ObjectVisibility.DEPTH) + depthNormalIdPass.setJitter(true) + depthNormalIdPass.setClearColor(0x000000, 1) + depthNormalIdPass.setClearFlags(ClearFlags.COLOR | ClearFlags.DEPTH) - const depthPassNormalDynamic = new DepthNormalPass() - depthPassNormalDynamic.setLayers([ObjectLayers.STREAM_CONTENT_MESH]) - depthPassNormalDynamic.setVisibility(ObjectVisibility.DEPTH) - depthPassNormalDynamic.setClearColor(0x000000, 1) - depthPassNormalDynamic.setClearFlags(ClearFlags.COLOR | ClearFlags.DEPTH) + const depthNormalIdPassDynamic = new DepthNormalIdPass() + depthNormalIdPassDynamic.setLayers([ObjectLayers.STREAM_CONTENT_MESH]) + depthNormalIdPassDynamic.setVisibility(ObjectVisibility.DEPTH) + depthNormalIdPassDynamic.setClearColor(0x000000, 1) + depthNormalIdPassDynamic.setClearFlags(ClearFlags.COLOR | ClearFlags.DEPTH) const edgesPass = new EdgePass() - edgesPass.setTexture('tDepth', depthNormalPass.depthTexture) - edgesPass.setTexture('tNormal', depthNormalPass.normalTexture) + edgesPass.setTexture('tDepth', depthNormalIdPass.depthTexture) + edgesPass.setTexture('tNormal', depthNormalIdPass.normalTexture) + edgesPass.setTexture('tId', depthNormalIdPass.idTexture) const edgesPassDynamic = new EdgePass() - edgesPassDynamic.setTexture('tDepth', depthPassNormalDynamic.depthTexture) - edgesPassDynamic.setTexture('tNormal', depthPassNormalDynamic.normalTexture) + edgesPassDynamic.setTexture('tDepth', depthNormalIdPassDynamic.depthTexture) + edgesPassDynamic.setTexture('tNormal', depthNormalIdPassDynamic.normalTexture) + edgesPassDynamic.setTexture('tId', depthNormalIdPassDynamic.idTexture) edgesPassDynamic.outputTarget = null const taaPass = new TAAPass() @@ -110,7 +112,7 @@ export class MRTPenViewPipeline extends ProgressivePipeline { outputPass.setTexture('tDiffuse', taaPass.outputTarget?.texture) this.dynamicStage.push( - depthPassNormalDynamic, + depthNormalIdPassDynamic, edgesPassDynamic, stencilPass, geometryPass, @@ -118,7 +120,7 @@ export class MRTPenViewPipeline extends ProgressivePipeline { overlayPass ) this.progressiveStage.push( - depthNormalPass, + depthNormalIdPass, edgesPass, taaPass, outputPass, diff --git a/packages/viewer/src/modules/pipeline/Pipelines/MRT/MRTShadedViewPipeline.ts b/packages/viewer/src/modules/pipeline/Pipelines/MRT/MRTShadedViewPipeline.ts index 178501186..b4129f70c 100644 --- a/packages/viewer/src/modules/pipeline/Pipelines/MRT/MRTShadedViewPipeline.ts +++ b/packages/viewer/src/modules/pipeline/Pipelines/MRT/MRTShadedViewPipeline.ts @@ -10,24 +10,24 @@ import { TAAPass } from '../../Passes/TAAPass.js' import { ViewportPass } from '../../Passes/ViewportPass.js' import { ProgressivePipeline } from '../ProgressivePipeline.js' import defaultMatcap from '../../../../assets/matcap.png' -import { DepthNormalPass } from '../../Passes/DepthNormalPass.js' +import { DepthNormalIdPass } from '../../Passes/DepthNormalIdPass.js' export class MRTShadedViewPipeline extends ProgressivePipeline { constructor(speckleRenderer: SpeckleRenderer) { super(speckleRenderer) - const depthNormalPass = new DepthNormalPass() - depthNormalPass.setLayers([ObjectLayers.STREAM_CONTENT_MESH]) - depthNormalPass.setVisibility(ObjectVisibility.DEPTH) - depthNormalPass.setJitter(true) - depthNormalPass.setClearColor(0x000000, 1) - depthNormalPass.setClearFlags(ClearFlags.COLOR | ClearFlags.DEPTH) + const depthNormalIdPass = new DepthNormalIdPass() + depthNormalIdPass.setLayers([ObjectLayers.STREAM_CONTENT_MESH]) + depthNormalIdPass.setVisibility(ObjectVisibility.DEPTH) + depthNormalIdPass.setJitter(true) + depthNormalIdPass.setClearColor(0x000000, 1) + depthNormalIdPass.setClearFlags(ClearFlags.COLOR | ClearFlags.DEPTH) - const depthPassNormalDynamic = new DepthNormalPass() - depthPassNormalDynamic.setLayers([ObjectLayers.STREAM_CONTENT_MESH]) - depthPassNormalDynamic.setVisibility(ObjectVisibility.DEPTH) - depthPassNormalDynamic.setClearColor(0x000000, 1) - depthPassNormalDynamic.setClearFlags(ClearFlags.COLOR | ClearFlags.DEPTH) + const depthPassNormalIdDynamic = new DepthNormalIdPass() + depthPassNormalIdDynamic.setLayers([ObjectLayers.STREAM_CONTENT_MESH]) + depthPassNormalIdDynamic.setVisibility(ObjectVisibility.DEPTH) + depthPassNormalIdDynamic.setClearColor(0x000000, 1) + depthPassNormalIdDynamic.setClearFlags(ClearFlags.COLOR | ClearFlags.DEPTH) const viewportPass = new ViewportPass() viewportPass.setLayers([ @@ -51,12 +51,14 @@ export class MRTShadedViewPipeline extends ProgressivePipeline { shadowcatcherPass.setLayers([ObjectLayers.SHADOWCATCHER]) const edgesPass = new EdgePass() - edgesPass.setTexture('tDepth', depthNormalPass.depthTexture) - edgesPass.setTexture('tNormal', depthNormalPass.normalTexture) + edgesPass.setTexture('tDepth', depthNormalIdPass.depthTexture) + edgesPass.setTexture('tNormal', depthNormalIdPass.normalTexture) + edgesPass.setTexture('tId', depthNormalIdPass.idTexture) const edgesPassDynamic = new EdgePass() - edgesPassDynamic.setTexture('tDepth', depthPassNormalDynamic.depthTexture) - edgesPassDynamic.setTexture('tNormal', depthPassNormalDynamic.normalTexture) + edgesPassDynamic.setTexture('tDepth', depthPassNormalIdDynamic.depthTexture) + edgesPassDynamic.setTexture('tNormal', depthPassNormalIdDynamic.normalTexture) + edgesPassDynamic.setTexture('tId', depthPassNormalIdDynamic.idTexture) const taaPass = new TAAPass() taaPass.inputTexture = edgesPass.outputTarget?.texture @@ -88,7 +90,7 @@ export class MRTShadedViewPipeline extends ProgressivePipeline { overlayPass.setLayers([ObjectLayers.OVERLAY, ObjectLayers.MEASUREMENTS]) this.dynamicStage.push( - depthPassNormalDynamic, + depthPassNormalIdDynamic, edgesPassDynamic, stencilPass, shadowcatcherPass, @@ -99,7 +101,7 @@ export class MRTShadedViewPipeline extends ProgressivePipeline { overlayPass ) this.progressiveStage.push( - depthNormalPass, + depthNormalIdPass, edgesPass, taaPass, stencilPass,