From c962d2c6fa70cfee1f6e82c8d1ea2026e3b208a4 Mon Sep 17 00:00:00 2001 From: Alexandru Popovici Date: Mon, 17 Mar 2025 11:07:37 +0200 Subject: [PATCH] Screen Space Outlines Update (#4185) * Color hash from object id attribute * feat(viewer-lib): Implemented screen space outlines based on object id gradients. Still slightly WIP, but the concept seems to be working fine * Added engine model for testing outlines update * feat(viewer-lib): Updates to object-id based gradients - Id gradients now are now binary. If they are greater than 0, they will get updated to 1. This fixes the issue where similar nearby objects that happen to have similar colors would exhibit a fainter gradient - Fixed an issue with DepthNormalIdPass where linear depth was not being used - Cleaned up the edge generator shader * feat(viewer-viewer): Added instancing support for screen space outline from color id gradients - Instances now generate a correct and unique color hash based off a batch index and gl_InstanceID when running WebGL 2.0. For WebGL1.0 it's currently not supported - Removed some unneeded non-null assertion left over from a long time ago - Extended three.js InstancedMesh in order to add a batch index variable to it. Nothing more * feat(viewer-lib): Added support for WebGL1.0 for both instanced and non instanced rendering of the color id gradients - Batcher now takes all the webgl caps instead of just mac vert uniforms and float textures - The depth-normal-id pass shader now works on both WebGL 1.0 and 2.0. - When running WebGL 1.0 we dynamically add a per instance attribute buffer with the object id in order to compute hash. If we're running WebGL 2.0 we do not do that and instead rely on the builtin gl_InstanceID so we conserve bandwidth - Fixed small type issue with the edge generation shader when running WebGL 1.0 * feat(viewer-lib): Updated the edged and shaded view modes to use the depth-normal-id pass * Restored to main version. --- packages/viewer-sandbox/src/main.ts | 11 +- .../viewer/src/modules/SpeckleRenderer.ts | 5 +- packages/viewer/src/modules/batching/Batch.ts | 4 + .../viewer/src/modules/batching/Batcher.ts | 21 +- .../modules/batching/InstancedMeshBatch.ts | 30 +- .../viewer/src/modules/batching/MeshBatch.ts | 6 +- .../src/modules/batching/PrimitiveBatch.ts | 10 +- .../materials/SpeckleDepthNormalIdMaterial.ts | 45 ++ .../shaders/speckle-depth-normal-id-frag.ts | 77 ++++ .../shaders/speckle-depth-normal-id-vert.ts | 273 +++++++++++ .../shaders/speckle-edges-generator-frag.ts | 426 ++---------------- .../shaders/speckle-standard-frag.ts | 2 + .../shaders/speckle-standard-vert.ts | 2 +- .../modules/objects/ExtendedInstancedMesh.ts | 22 + .../modules/objects/SpeckleInstancedMesh.ts | 27 +- .../viewer/src/modules/objects/SpeckleMesh.ts | 11 +- .../pipeline/Passes/DepthNormalIdPass.ts | 52 +++ .../pipeline/Passes/DepthNormalPass.ts | 30 +- .../src/modules/pipeline/Passes/EdgesPass.ts | 1 + .../Pipelines/MRT/MRTEdgesPipeline.ts | 40 +- .../Pipelines/MRT/MRTPenViewPipeline.ts | 38 +- .../Pipelines/MRT/MRTShadedViewPipeline.ts | 38 +- 22 files changed, 683 insertions(+), 488 deletions(-) create mode 100644 packages/viewer/src/modules/materials/SpeckleDepthNormalIdMaterial.ts create mode 100644 packages/viewer/src/modules/materials/shaders/speckle-depth-normal-id-frag.ts create mode 100644 packages/viewer/src/modules/materials/shaders/speckle-depth-normal-id-vert.ts create mode 100644 packages/viewer/src/modules/objects/ExtendedInstancedMesh.ts create mode 100644 packages/viewer/src/modules/pipeline/Passes/DepthNormalIdPass.ts 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,