From c7c98a2e2453af45ef2d664d0fe834aebc1d7d3a Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Wed, 21 Sep 2022 18:50:20 +0300 Subject: [PATCH 01/15] #1014 Started working on SAO --- packages/viewer-sandbox/src/Sandbox.ts | 97 +++++++++++++++++++ packages/viewer-sandbox/src/main.ts | 4 +- packages/viewer/src/modules/Pipeline.ts | 44 +++++++++ .../viewer/src/modules/SpeckleRenderer.ts | 38 +++++++- packages/viewer/src/modules/Viewer.ts | 3 +- .../src/modules/context/CameraHanlder.js | 5 +- 6 files changed, 183 insertions(+), 8 deletions(-) create mode 100644 packages/viewer/src/modules/Pipeline.ts diff --git a/packages/viewer-sandbox/src/Sandbox.ts b/packages/viewer-sandbox/src/Sandbox.ts index 9322f4652..b82df51e5 100644 --- a/packages/viewer-sandbox/src/Sandbox.ts +++ b/packages/viewer-sandbox/src/Sandbox.ts @@ -30,6 +30,21 @@ export default class Sandbox { tonemapping: 4 //'ACESFilmicToneMapping' } + public static postParams = { + saoEnabled: true, + saoParams: { + saoBias: 0, + saoIntensity: 1.5, + saoScale: 434, + saoKernelRadius: 6.52, + saoMinResolution: 0, + saoBlur: true, + saoBlurRadius: 2, + saoBlurStdDev: 4, + saoBlurDepthCutoff: 0.00007 + } + } + public static lightParams: SunLightConfiguration = { enabled: true, castShadow: true, @@ -321,6 +336,88 @@ export default class Sandbox { this.viewer.getRenderer().renderer.toneMapping = Sandbox.sceneParams.tonemapping this.viewer.requestRender() }) + postFolder + .addInput(Sandbox.postParams.saoParams, 'saoBias', { + min: -1, + max: 1 + }) + .on('change', () => { + this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + this.viewer.requestRender() + }) + postFolder + .addInput(Sandbox.postParams.saoParams, 'saoIntensity', { + min: 0, + max: 5 + }) + .on('change', () => { + this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + this.viewer.requestRender() + }) + + postFolder + .addInput(Sandbox.postParams.saoParams, 'saoScale', { + min: 0, + max: 2000 + }) + .on('change', () => { + this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + this.viewer.requestRender() + }) + + postFolder + .addInput(Sandbox.postParams.saoParams, 'saoKernelRadius', { + min: 0, + max: 100 + }) + .on('change', () => { + this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + this.viewer.requestRender() + }) + + postFolder + .addInput(Sandbox.postParams.saoParams, 'saoMinResolution', { + min: 0, + max: 1 + }) + .on('change', () => { + this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + this.viewer.requestRender() + }) + + postFolder + .addInput(Sandbox.postParams.saoParams, 'saoBlur', {}) + .on('change', () => { + this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + this.viewer.requestRender() + }) + + postFolder + .addInput(Sandbox.postParams.saoParams, 'saoBlurRadius', { min: 0, max: 100 }) + .on('change', () => { + this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + this.viewer.requestRender() + }) + + postFolder + .addInput(Sandbox.postParams.saoParams, 'saoBlurStdDev', { + min: 0, + max: 150 + }) + .on('change', () => { + this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + this.viewer.requestRender() + }) + + postFolder + .addInput(Sandbox.postParams.saoParams, 'saoBlurDepthCutoff', { + min: 0, + max: 10 + }) + .on('change', () => { + this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + this.viewer.requestRender() + }) const lightsFolder = this.tabs.pages[1].addFolder({ title: 'Lights', diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index b77b509b9..9a85d9d0f 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -82,7 +82,7 @@ sandbox.makeFilteringUI() await sandbox.loadUrl( // 'https://speckle.xyz/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://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' + // 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' // 'Super' heavy revit shit // 'https://speckle.xyz/streams/e6f9156405/commits/0694d53bb5' // Same sample revit house, local to dim's computer @@ -99,4 +99,6 @@ await sandbox.loadUrl( // 'https://latest.speckle.dev/streams/92b620fb17/commits/af6098915b?c=%5B0.02144,-0.0377,0.05554,0.00566,0.00236,0,0,1%5D' // AutoCAD // 'https://latest.speckle.dev/streams/3ed8357f29/commits/d10f2af1ce' + //Blizzard world + 'https://latest.speckle.dev/streams/0c6ad366c4/commits/aa1c393aec' ) diff --git a/packages/viewer/src/modules/Pipeline.ts b/packages/viewer/src/modules/Pipeline.ts new file mode 100644 index 000000000..d30d4016e --- /dev/null +++ b/packages/viewer/src/modules/Pipeline.ts @@ -0,0 +1,44 @@ +import { Camera, Scene, WebGLRenderer } from 'three' +import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js' +import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js' +import { SAOPass, SAOPassParams } from 'three/examples/jsm/postprocessing/SAOPass.js' + +export interface PipelineOptions { + saoEnabled?: boolean + saoParams: Partial +} + +export class Pipeline { + private _renderer: WebGLRenderer = null + private _pipelineOptions: PipelineOptions = null + private composer: EffectComposer = null + private renderPass: RenderPass = null + private saoPass: SAOPass = null + + public set pipelineOptions(options: PipelineOptions) { + this._pipelineOptions = options + if (this.saoPass) { + Object.assign(this.saoPass.params, options.saoParams) + } + } + + public constructor(renderer: WebGLRenderer) { + this._renderer = renderer + this.composer = new EffectComposer(renderer) + } + + public configure(scene: Scene, camera: Camera) { + this.renderPass = new RenderPass(scene, camera) + this.composer.addPass(this.renderPass) + this.saoPass = new SAOPass(scene, camera, false, true) + this.composer.addPass(this.saoPass) + } + + public render(scene: Scene, camera: Camera) { + this.renderPass.scene = scene + this.renderPass.camera = camera + this.saoPass.scene = scene + this.saoPass.camera = camera + this.composer.render() + } +} diff --git a/packages/viewer/src/modules/SpeckleRenderer.ts b/packages/viewer/src/modules/SpeckleRenderer.ts index 9a3bb8295..2a00612bd 100644 --- a/packages/viewer/src/modules/SpeckleRenderer.ts +++ b/packages/viewer/src/modules/SpeckleRenderer.ts @@ -45,6 +45,7 @@ import { SunLightConfiguration, ViewerEvent } from '../IViewer' +import { Pipeline, PipelineOptions } from './Pipeline' export default class SpeckleRenderer { private readonly SHOW_HELPERS = false @@ -59,6 +60,7 @@ export default class SpeckleRenderer { private sunConfiguration: SunLightConfiguration = DefaultLightConfiguration public viewer: Viewer // TEMPORARY private filterBatchRecording: string[] + private pipeline: Pipeline public get renderer(): WebGLRenderer { return this._renderer @@ -106,6 +108,10 @@ export default class SpeckleRenderer { return this.sun } + public set pipelineOptions(value: PipelineOptions) { + this.pipeline.pipelineOptions = value + } + public constructor(viewer: Viewer /** TEMPORARY */) { this.scene = new Scene() this.rootGroup = new Group() @@ -137,6 +143,9 @@ export default class SpeckleRenderer { this._renderer.setSize(container.offsetWidth, container.offsetHeight) container.appendChild(this._renderer.domElement) + this.pipeline = new Pipeline(this._renderer) + this.pipeline.configure(this.scene, this.viewer.cameraHandler.activeCam.camera) + this.input = new Input(this._renderer.domElement, InputOptionsDefault) this.input.on(ViewerEvent.ObjectClicked, this.onObjectClick.bind(this)) this.input.on('object-clicked-debug', this.onObjectClickDebug.bind(this)) @@ -232,11 +241,37 @@ export default class SpeckleRenderer { } } } + + const v = new Vector3() + const box = this.sceneBox + const camPos = new Vector3().copy( + this.viewer.cameraHandler.activeCam.camera.position + ) + let d = 0 + v.set(box.min.x, box.min.y, box.min.z) // 000 + d = Math.max(camPos.distanceTo(v), d) + v.set(box.min.x, box.min.y, box.max.z) // 001 + d = Math.max(camPos.distanceTo(v), d) + v.set(box.min.x, box.max.y, box.min.z) // 010 + d = Math.max(camPos.distanceTo(v), d) + v.set(box.min.x, box.max.y, box.max.z) // 011 + d = Math.max(camPos.distanceTo(v), d) + v.set(box.max.x, box.min.y, box.min.z) // 100 + d = Math.max(camPos.distanceTo(v), d) + v.set(box.max.x, box.min.y, box.max.z) // 101 + d = Math.max(camPos.distanceTo(v), d) + v.set(box.max.x, box.max.y, box.min.z) // 110 + d = Math.max(camPos.distanceTo(v), d) + v.set(box.max.x, box.max.y, box.max.z) // 111 + d = Math.max(camPos.distanceTo(v), d) + this.viewer.cameraHandler.activeCam.camera.far = d + this.viewer.cameraHandler.activeCam.camera.updateProjectionMatrix() + this.pipeline.pipelineOptions = { saoParams: { saoScale: d } } } public render(camera: Camera) { this.batcher.render(this.renderer) - this.renderer.render(this.scene, camera) + this.pipeline.render(this.scene, camera) } public addRenderTree(subtreeId: string) { @@ -490,7 +525,6 @@ export default class SpeckleRenderer { selectionCenter: result.point, // Ideally we'd get the selection center here multiple: multiSelect } as SelectionEvent - this.viewer.emit(ViewerEvent.ObjectClicked, selectionInfo) } diff --git a/packages/viewer/src/modules/Viewer.ts b/packages/viewer/src/modules/Viewer.ts index f8a5f2aec..469ff22f9 100644 --- a/packages/viewer/src/modules/Viewer.ts +++ b/packages/viewer/src/modules/Viewer.ts @@ -82,6 +82,8 @@ export class Viewer extends EventEmitter implements IViewer { this.clock = new THREE.Clock() this.inProgressOperations = 0 + this.cameraHandler = new CameraHandler(this) + this.speckleRenderer = new SpeckleRenderer(this) this.speckleRenderer.create(this.container) window.addEventListener('resize', this.resize.bind(this), false) @@ -92,7 +94,6 @@ export class Viewer extends EventEmitter implements IViewer { // eslint-disable-next-line @typescript-eslint/no-explicit-any ;(window as any)._V = this // For debugging! - this.cameraHandler = new CameraHandler(this) this.sectionBox = new SectionBox(this) this.sectionBox.off() this.sectionBox.controls.addEventListener('change', () => { diff --git a/packages/viewer/src/modules/context/CameraHanlder.js b/packages/viewer/src/modules/context/CameraHanlder.js index 5985a653e..40729c57f 100644 --- a/packages/viewer/src/modules/context/CameraHanlder.js +++ b/packages/viewer/src/modules/context/CameraHanlder.js @@ -30,10 +30,7 @@ export default class CameraHandler { this.orthoCamera.updateProjectionMatrix() CameraControls.install({ THREE }) - this.controls = new CameraControls( - this.camera, - this.viewer.speckleRenderer.renderer.domElement - ) + this.controls = new CameraControls(this.camera, this.viewer.container) this.controls.maxPolarAngle = Math.PI / 2 this.setupWASDControls() From 0bd362e1ef7923761484db521ad8484f1d2fe8a6 Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Thu, 22 Sep 2022 16:43:24 +0300 Subject: [PATCH 02/15] #1014 SAO WIP --- packages/viewer-sandbox/src/Sandbox.ts | 18 +- packages/viewer-sandbox/src/main.ts | 6 +- .../viewer/src/modules/SpeckleRenderer.ts | 4 +- packages/viewer/src/modules/Viewer.ts | 2 +- .../materials/shaders/speckle-sao-frag.ts | 106 +++++++ .../materials/shaders/speckle-sao-vert.ts | 6 + .../src/modules/pipeline/ApplySAOPass.ts | 48 +++ .../src/modules/{ => pipeline}/Pipeline.ts | 39 ++- .../src/modules/pipeline/SpeckleSAOPass.ts | 284 ++++++++++++++++++ 9 files changed, 496 insertions(+), 17 deletions(-) create mode 100644 packages/viewer/src/modules/materials/shaders/speckle-sao-frag.ts create mode 100644 packages/viewer/src/modules/materials/shaders/speckle-sao-vert.ts create mode 100644 packages/viewer/src/modules/pipeline/ApplySAOPass.ts rename packages/viewer/src/modules/{ => pipeline}/Pipeline.ts (52%) create mode 100644 packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts diff --git a/packages/viewer-sandbox/src/Sandbox.ts b/packages/viewer-sandbox/src/Sandbox.ts index b82df51e5..47490bd11 100644 --- a/packages/viewer-sandbox/src/Sandbox.ts +++ b/packages/viewer-sandbox/src/Sandbox.ts @@ -33,15 +33,15 @@ export default class Sandbox { public static postParams = { saoEnabled: true, saoParams: { - saoBias: 0, + saoBias: 0.15, saoIntensity: 1.5, saoScale: 434, - saoKernelRadius: 6.52, + saoKernelRadius: 20, saoMinResolution: 0, saoBlur: true, - saoBlurRadius: 2, + saoBlurRadius: 4, saoBlurStdDev: 4, - saoBlurDepthCutoff: 0.00007 + saoBlurDepthCutoff: 0.0007 } } @@ -53,7 +53,7 @@ export default class Sandbox { elevation: 1.33, azimuth: 0.75, radius: 0, - indirectLightIntensity: 1.85 + indirectLightIntensity: 1.2 } public static filterParams = { @@ -336,6 +336,12 @@ export default class Sandbox { this.viewer.getRenderer().renderer.toneMapping = Sandbox.sceneParams.tonemapping this.viewer.requestRender() }) + postFolder + .addInput(Sandbox.postParams, 'saoEnabled', { label: 'SAO-ENABLED' }) + .on('change', () => { + this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + this.viewer.requestRender() + }) postFolder .addInput(Sandbox.postParams.saoParams, 'saoBias', { min: -1, @@ -358,7 +364,7 @@ export default class Sandbox { postFolder .addInput(Sandbox.postParams.saoParams, 'saoScale', { min: 0, - max: 2000 + max: 100 }) .on('change', () => { this.viewer.getRenderer().pipelineOptions = Sandbox.postParams diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index 9a85d9d0f..05771aeb0 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -91,7 +91,7 @@ await sandbox.loadUrl( // IFC building (good for a tree based structure) // 'https://latest.speckle.dev/streams/92b620fb17/commits/2ebd336223' // IFC story, a subtree of the above - // 'https://latest.speckle.dev/streams/92b620fb17/objects/8247bbc53865b0e0cb5ee4e252e66216' + 'https://latest.speckle.dev/streams/92b620fb17/objects/8247bbc53865b0e0cb5ee4e252e66216' // Small scale lines // 'https://speckle.xyz/streams/638d3b1f83/commits/6025e2b546?c=%5B2.18058,-0.20814,9.67642,3.85491,5.05364,0,0,1%5D' // 'https://latest.speckle.dev/streams/3ed8357f29/commits/d10f2af1ce' @@ -100,5 +100,7 @@ await sandbox.loadUrl( // AutoCAD // 'https://latest.speckle.dev/streams/3ed8357f29/commits/d10f2af1ce' //Blizzard world - 'https://latest.speckle.dev/streams/0c6ad366c4/commits/aa1c393aec' + // 'https://latest.speckle.dev/streams/0c6ad366c4/commits/aa1c393aec' + //Car + // 'https://latest.speckle.dev/streams/17d2e25a97/commits/6b6cf3d43e' ) diff --git a/packages/viewer/src/modules/SpeckleRenderer.ts b/packages/viewer/src/modules/SpeckleRenderer.ts index 2a00612bd..12e1906ad 100644 --- a/packages/viewer/src/modules/SpeckleRenderer.ts +++ b/packages/viewer/src/modules/SpeckleRenderer.ts @@ -45,7 +45,7 @@ import { SunLightConfiguration, ViewerEvent } from '../IViewer' -import { Pipeline, PipelineOptions } from './Pipeline' +import { DefaultPipelineOptions, Pipeline, PipelineOptions } from './pipeline/Pipeline' export default class SpeckleRenderer { private readonly SHOW_HELPERS = false @@ -145,6 +145,7 @@ export default class SpeckleRenderer { this.pipeline = new Pipeline(this._renderer) this.pipeline.configure(this.scene, this.viewer.cameraHandler.activeCam.camera) + this.pipeline.pipelineOptions = DefaultPipelineOptions this.input = new Input(this._renderer.domElement, InputOptionsDefault) this.input.on(ViewerEvent.ObjectClicked, this.onObjectClick.bind(this)) @@ -267,6 +268,7 @@ export default class SpeckleRenderer { this.viewer.cameraHandler.activeCam.camera.far = d this.viewer.cameraHandler.activeCam.camera.updateProjectionMatrix() this.pipeline.pipelineOptions = { saoParams: { saoScale: d } } + // console.log(d) } public render(camera: Camera) { diff --git a/packages/viewer/src/modules/Viewer.ts b/packages/viewer/src/modules/Viewer.ts index 469ff22f9..33ace42d4 100644 --- a/packages/viewer/src/modules/Viewer.ts +++ b/packages/viewer/src/modules/Viewer.ts @@ -158,7 +158,7 @@ export class Viewer extends EventEmitter implements IViewer { private render() { if (this.needsRender) { this.speckleRenderer.render(this.cameraHandler.activeCam.camera) - this._needsRender = false + // this._needsRender = false } } diff --git a/packages/viewer/src/modules/materials/shaders/speckle-sao-frag.ts b/packages/viewer/src/modules/materials/shaders/speckle-sao-frag.ts new file mode 100644 index 000000000..5192bd974 --- /dev/null +++ b/packages/viewer/src/modules/materials/shaders/speckle-sao-frag.ts @@ -0,0 +1,106 @@ +export const speckleSaoFrag = /* glsl */ ` + #include + varying vec2 vUv; + #if DIFFUSE_TEXTURE == 1 + uniform sampler2D tDiffuse; + #endif + uniform sampler2D tDepth; + #if NORMAL_TEXTURE == 1 + uniform sampler2D tNormal; + #endif + uniform float cameraNear; + uniform float cameraFar; + uniform mat4 cameraProjectionMatrix; + uniform mat4 cameraInverseProjectionMatrix; + uniform float scale; + uniform float intensity; + uniform float bias; + uniform float kernelRadius; + uniform float minResolution; + uniform vec2 size; + uniform float randomSeed; + // RGBA depth + #include + vec4 getDefaultColor( const in vec2 screenPosition ) { + #if DIFFUSE_TEXTURE == 1 + return texture2D( tDiffuse, vUv ); + #else + return vec4( 1.0 ); + #endif + } + float getDepth( const in vec2 screenPosition ) { + #if DEPTH_PACKING == 1 + return unpackRGBAToDepth( texture2D( tDepth, screenPosition ) ); + #else + return texture2D( tDepth, screenPosition ).x; + #endif + } + 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; + } + vec3 getViewNormal( const in vec3 viewPosition, const in vec2 screenPosition ) { + #if NORMAL_TEXTURE == 1 + return unpackRGBToNormal( texture2D( tNormal, screenPosition ).xyz ); + #else + return normalize( cross( dFdx( viewPosition ), dFdy( viewPosition ) ) ); + #endif + } + float scaleDividedByCameraFar; + float minResolutionMultipliedByCameraFar; + float getOcclusion( const in vec3 centerViewPosition, const in vec3 centerViewNormal, const in vec3 sampleViewPosition ) { + vec3 viewDelta = sampleViewPosition - centerViewPosition; + float viewDistance = length( viewDelta ); + float scaledScreenDistance = scaleDividedByCameraFar * viewDistance; + return max(0.0, (dot(centerViewNormal, viewDelta) - minResolutionMultipliedByCameraFar) / scaledScreenDistance - bias) / (1.0 + pow2( scaledScreenDistance ) ); + } + // moving costly divides into consts + const float ANGLE_STEP = PI2 * float( NUM_RINGS ) / float( NUM_SAMPLES ); + const float INV_NUM_SAMPLES = 1.0 / float( NUM_SAMPLES ); + float getAmbientOcclusion( const in vec3 centerViewPosition ) { + // precompute some variables require in getOcclusion. + scaleDividedByCameraFar = scale / cameraFar; + minResolutionMultipliedByCameraFar = minResolution * cameraFar; + vec3 centerViewNormal = getViewNormal( centerViewPosition, vUv ); + // jsfiddle that shows sample pattern: https://jsfiddle.net/a16ff1p7/ + float angle = rand( vUv + randomSeed ) * PI2; + vec2 radius = vec2( kernelRadius * INV_NUM_SAMPLES ) / size; + vec2 radiusStep = radius; + float occlusionSum = 0.0; + float weightSum = 0.0; + for( int i = 0; i < NUM_SAMPLES; i ++ ) { + vec2 sampleUv = vUv + vec2( cos( angle ), sin( angle ) ) * radius; + radius += radiusStep; + angle += ANGLE_STEP; + float sampleDepth = getDepth( sampleUv ); + if( sampleDepth >= ( 1.0 - EPSILON ) ) { + continue; + } + float sampleViewZ = getViewZ( sampleDepth ); + vec3 sampleViewPosition = getViewPosition( sampleUv, sampleDepth, sampleViewZ ); + occlusionSum += getOcclusion( centerViewPosition, centerViewNormal, sampleViewPosition ); + weightSum += 1.0; + } + if( weightSum == 0.0 ) discard; + return occlusionSum * ( intensity / weightSum ); + } + void main() { + float centerDepth = getDepth( vUv ); + if( centerDepth >= ( 1.0 - EPSILON ) ) { + discard; + } + float centerViewZ = getViewZ( centerDepth ); + vec3 viewPosition = getViewPosition( vUv, centerDepth, centerViewZ ); + float ambientOcclusion = getAmbientOcclusion( viewPosition ); + gl_FragColor = getDefaultColor( vUv ); + gl_FragColor.xyz *= 1.0 - ambientOcclusion; + }` diff --git a/packages/viewer/src/modules/materials/shaders/speckle-sao-vert.ts b/packages/viewer/src/modules/materials/shaders/speckle-sao-vert.ts new file mode 100644 index 000000000..d85755936 --- /dev/null +++ b/packages/viewer/src/modules/materials/shaders/speckle-sao-vert.ts @@ -0,0 +1,6 @@ +export const speckleSaoVert = /* glsl */ ` + varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + }` diff --git a/packages/viewer/src/modules/pipeline/ApplySAOPass.ts b/packages/viewer/src/modules/pipeline/ApplySAOPass.ts new file mode 100644 index 000000000..cbddfdc58 --- /dev/null +++ b/packages/viewer/src/modules/pipeline/ApplySAOPass.ts @@ -0,0 +1,48 @@ +import { + AddEquation, + CustomBlending, + DstAlphaFactor, + DstColorFactor, + NoBlending, + ShaderMaterial, + Texture, + UniformsUtils, + ZeroFactor +} from 'three' +import { FullScreenQuad, Pass } from 'three/examples/jsm/postprocessing/Pass' +import { CopyShader } from 'three/examples/jsm/shaders/CopyShader.js' + +export class ApplySAOPass extends Pass { + private fsQuad: FullScreenQuad + private materialCopy: ShaderMaterial + + constructor(srcSao: Texture) { + super() + this.materialCopy = new ShaderMaterial({ + uniforms: UniformsUtils.clone(CopyShader.uniforms), + vertexShader: CopyShader.vertexShader, + fragmentShader: CopyShader.fragmentShader, + blending: NoBlending + }) + this.materialCopy.transparent = true + this.materialCopy.depthTest = false + this.materialCopy.depthWrite = false + this.materialCopy.blending = CustomBlending + this.materialCopy.blendSrc = DstColorFactor + this.materialCopy.blendDst = ZeroFactor + this.materialCopy.blendEquation = AddEquation + this.materialCopy.blendSrcAlpha = DstAlphaFactor + this.materialCopy.blendDstAlpha = ZeroFactor + this.materialCopy.blendEquationAlpha = AddEquation + this.materialCopy.uniforms['tDiffuse'].value = srcSao + this.materialCopy.needsUpdate = true + this.fsQuad = new FullScreenQuad(this.materialCopy) + } + + render(renderer, writeBuffer, readBuffer /*, deltaTime, maskActive*/) { + writeBuffer + readBuffer + renderer.setRenderTarget(null) + this.fsQuad.render(renderer) + } +} diff --git a/packages/viewer/src/modules/Pipeline.ts b/packages/viewer/src/modules/pipeline/Pipeline.ts similarity index 52% rename from packages/viewer/src/modules/Pipeline.ts rename to packages/viewer/src/modules/pipeline/Pipeline.ts index d30d4016e..f4a55ff40 100644 --- a/packages/viewer/src/modules/Pipeline.ts +++ b/packages/viewer/src/modules/pipeline/Pipeline.ts @@ -2,36 +2,61 @@ import { Camera, Scene, WebGLRenderer } from 'three' import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js' import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js' import { SAOPass, SAOPassParams } from 'three/examples/jsm/postprocessing/SAOPass.js' +import { ApplySAOPass } from './ApplySAOPass' +import { SpeckleSAOPass } from './SpeckleSAOPass' export interface PipelineOptions { saoEnabled?: boolean - saoParams: Partial + saoParams?: Partial +} + +export const DefaultPipelineOptions: PipelineOptions = { + saoEnabled: true, + saoParams: { + saoBias: 0.15, + saoIntensity: 1.5, + saoScale: 434, + saoKernelRadius: 20, + saoMinResolution: 0, + saoBlur: true, + saoBlurRadius: 4, + saoBlurStdDev: 4, + saoBlurDepthCutoff: 0.0007 + } } export class Pipeline { private _renderer: WebGLRenderer = null - private _pipelineOptions: PipelineOptions = null + private _pipelineOptions: PipelineOptions = {} private composer: EffectComposer = null private renderPass: RenderPass = null private saoPass: SAOPass = null + private applySaoPass: ApplySAOPass = null public set pipelineOptions(options: PipelineOptions) { - this._pipelineOptions = options + Object.assign(this._pipelineOptions, options) if (this.saoPass) { - Object.assign(this.saoPass.params, options.saoParams) + this.applySaoPass.enabled = this._pipelineOptions.saoEnabled + Object.assign(this.saoPass.params, this._pipelineOptions.saoParams) } } public constructor(renderer: WebGLRenderer) { this._renderer = renderer this.composer = new EffectComposer(renderer) + this.composer.readBuffer = null + this.composer.writeBuffer = null } public configure(scene: Scene, camera: Camera) { - this.renderPass = new RenderPass(scene, camera) - this.composer.addPass(this.renderPass) - this.saoPass = new SAOPass(scene, camera, false, true) + this.saoPass = new SpeckleSAOPass(scene, camera, false, true) this.composer.addPass(this.saoPass) + this.renderPass = new RenderPass(scene, camera) + this.renderPass.renderToScreen = true + this.composer.addPass(this.renderPass) + this.applySaoPass = new ApplySAOPass(this.saoPass.saoRenderTarget.texture) + this.applySaoPass.renderToScreen = true + this.composer.addPass(this.applySaoPass) } public render(scene: Scene, camera: Camera) { diff --git a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts new file mode 100644 index 000000000..c95919faf --- /dev/null +++ b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts @@ -0,0 +1,284 @@ +import { + NoBlending, + OrthographicCamera, + PerspectiveCamera, + ShaderMaterial, + UniformsUtils, + Vector2 +} from 'three' +import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass' +import { SAOPass } from 'three/examples/jsm/postprocessing/SAOPass.js' +import { BlurShaderUtils } from 'three/examples/jsm/shaders/DepthLimitedBlurShader.js' +import { speckleSaoFrag } from '../materials/shaders/speckle-sao-frag' +import { speckleSaoVert } from '../materials/shaders/speckle-sao-vert' +import { SAOShader } from 'three/examples/jsm/shaders/SAOShader.js' + +/** + * SAO implementation inspired from bhouston previous SAO work + */ + +export class SpeckleSAOPass extends SAOPass { + private _oldClearColor + private prevStdDev + private prevNumSamples + + constructor( + scene, + camera, + useDepthTexture = false, + useNormals = false, + resolution = new Vector2(256, 256) + ) { + super(scene, camera, useDepthTexture, useNormals, resolution) + + this.saoMaterial = new ShaderMaterial({ + defines: { + NUM_SAMPLES: 7, + NUM_RINGS: 4, + NORMAL_TEXTURE: 0, + DIFFUSE_TEXTURE: 0, + DEPTH_PACKING: 1, + PERSPECTIVE_CAMERA: 1 + }, + fragmentShader: speckleSaoFrag, + vertexShader: speckleSaoVert, + uniforms: UniformsUtils.clone(SAOShader.uniforms) + }) + this.saoMaterial.extensions.derivatives = true + this.saoMaterial.defines['DEPTH_PACKING'] = this.supportsDepthTextureExtension + ? 0 + : 1 + this.saoMaterial.defines['NORMAL_TEXTURE'] = this.supportsNormalTexture ? 1 : 0 + this.saoMaterial.defines['PERSPECTIVE_CAMERA'] = (this.camera as PerspectiveCamera) + .isPerspectiveCamera + ? 1 + : 0 + this.saoMaterial.uniforms['tDepth'].value = this.supportsDepthTextureExtension + ? this.beautyRenderTarget.depthTexture + : this.depthRenderTarget.texture + this.saoMaterial.uniforms['tNormal'].value = this.normalRenderTarget.texture + this.saoMaterial.uniforms['size'].value.set(this.resolution.x, this.resolution.y) + this.saoMaterial.uniforms['cameraInverseProjectionMatrix'].value.copy( + this.camera.projectionMatrixInverse + ) + this.saoMaterial.uniforms['cameraProjectionMatrix'].value = + this.camera.projectionMatrix + this.saoMaterial.blending = NoBlending + } + + render(renderer, writeBuffer, readBuffer /*, deltaTime, maskActive*/) { + // Rendering readBuffer first when rendering to screen + // if (this.renderToScreen) { + // this.materialCopy.blending = NoBlending + // this.materialCopy.uniforms['tDiffuse'].value = readBuffer.texture + // this.materialCopy.needsUpdate = true + // this.renderPass(renderer, this.materialCopy, null) + // } + writeBuffer + readBuffer + if (this.params.output === 1) { + return + } + + renderer.getClearColor(this._oldClearColor) + this.oldClearAlpha = renderer.getClearAlpha() + renderer.autoClear = false + + renderer.setRenderTarget(this.depthRenderTarget) + renderer.clear() + + this.saoMaterial.uniforms['bias'].value = this.params.saoBias + this.saoMaterial.uniforms['intensity'].value = this.params.saoIntensity + this.saoMaterial.uniforms['scale'].value = this.params.saoScale + this.saoMaterial.uniforms['kernelRadius'].value = this.params.saoKernelRadius + this.saoMaterial.uniforms['minResolution'].value = this.params.saoMinResolution + this.saoMaterial.uniforms['cameraNear'].value = ( + this.camera as PerspectiveCamera | OrthographicCamera + ).near + this.saoMaterial.uniforms['cameraFar'].value = ( + this.camera as PerspectiveCamera | OrthographicCamera + ).far + // this.saoMaterial.uniforms['randomSeed'].value = Math.random(); + + const depthCutoff = + this.params.saoBlurDepthCutoff * + ((this.camera as PerspectiveCamera | OrthographicCamera).far - + (this.camera as PerspectiveCamera | OrthographicCamera).near) + this.vBlurMaterial.uniforms['depthCutoff'].value = depthCutoff + this.hBlurMaterial.uniforms['depthCutoff'].value = depthCutoff + + this.vBlurMaterial.uniforms['cameraNear'].value = ( + this.camera as PerspectiveCamera | OrthographicCamera + ).near + this.vBlurMaterial.uniforms['cameraFar'].value = ( + this.camera as PerspectiveCamera | OrthographicCamera + ).far + this.hBlurMaterial.uniforms['cameraNear'].value = ( + this.camera as PerspectiveCamera | OrthographicCamera + ).near + this.hBlurMaterial.uniforms['cameraFar'].value = ( + this.camera as PerspectiveCamera | OrthographicCamera + ).far + + this.params.saoBlurRadius = Math.floor(this.params.saoBlurRadius) + if ( + this.prevStdDev !== this.params.saoBlurStdDev || + this.prevNumSamples !== this.params.saoBlurRadius + ) { + BlurShaderUtils.configure( + this.vBlurMaterial, + this.params.saoBlurRadius, + this.params.saoBlurStdDev, + new Vector2(0, 1) + ) + BlurShaderUtils.configure( + this.hBlurMaterial, + this.params.saoBlurRadius, + this.params.saoBlurStdDev, + new Vector2(1, 0) + ) + this.prevStdDev = this.params.saoBlurStdDev + this.prevNumSamples = this.params.saoBlurRadius + } + + // Rendering scene to depth texture + // renderer.setClearColor(0x000000) + // renderer.setRenderTarget(this.beautyRenderTarget) + // renderer.clear() + // renderer.render(this.scene, this.camera) + + // Re-render scene if depth texture extension is not supported + if (!this.supportsDepthTextureExtension) { + // Clear rule : far clipping plane in both RGBA and Basic encoding + this.renderOverride( + renderer, + this.depthMaterial, + this.depthRenderTarget, + 0x000000, + 1.0 + ) + } + + if (this.supportsNormalTexture) { + // Clear rule : default normal is facing the camera + this.renderOverride( + renderer, + this.normalMaterial, + this.normalRenderTarget, + 0x7777ff, + 1.0 + ) + } + + // Rendering SAO texture + this.renderPass(renderer, this.saoMaterial, this.saoRenderTarget, 0xffffff, 1.0) + + // Blurring SAO texture + if (this.params.saoBlur) { + this.renderPass( + renderer, + this.vBlurMaterial, + this.blurIntermediateRenderTarget, + 0xffffff, + 1.0 + ) + this.renderPass(renderer, this.hBlurMaterial, this.saoRenderTarget, 0xffffff, 1.0) + } + + // let outputMaterial = this.materialCopy + // // Setting up SAO rendering + // if (this.params.output === 3) { + // if (this.supportsDepthTextureExtension) { + // this.materialCopy.uniforms['tDiffuse'].value = + // this.beautyRenderTarget.depthTexture + // this.materialCopy.needsUpdate = true + // } else { + // this.depthCopy.uniforms['tDiffuse'].value = this.depthRenderTarget.texture + // this.depthCopy.needsUpdate = true + // outputMaterial = this.depthCopy + // } + // } else if (this.params.output === 4) { + // this.materialCopy.uniforms['tDiffuse'].value = this.normalRenderTarget.texture + // this.materialCopy.needsUpdate = true + // } else { + // this.materialCopy.uniforms['tDiffuse'].value = this.saoRenderTarget.texture + // this.materialCopy.needsUpdate = true + // } + + // // Blending depends on output, only want a CustomBlending when showing SAO + // if (this.params.output === 0) { + // outputMaterial.blending = CustomBlending + // } else { + // outputMaterial.blending = NoBlending + // } + + // // Rendering SAOPass result on top of previous pass + // // this.renderPass(renderer, outputMaterial, this.renderToScreen ? null : readBuffer) + + // renderer.setClearColor(this._oldClearColor, this.oldClearAlpha) + // renderer.autoClear = oldAutoClear + } + + renderPass( + renderer, + passMaterial, + renderTarget, + clearColor = undefined, + clearAlpha = undefined + ) { + // save original state + renderer.getClearColor(this.originalClearColor) + const originalClearAlpha = renderer.getClearAlpha() + const originalAutoClear = renderer.autoClear + + renderer.setRenderTarget(renderTarget) + + // setup pass state + renderer.autoClear = false + if (clearColor !== undefined && clearColor !== null) { + renderer.setClearColor(clearColor) + renderer.setClearAlpha(clearAlpha || 0.0) + renderer.clear() + } + + ;(this.fsQuad as FullScreenQuad).material = passMaterial + ;(this.fsQuad as FullScreenQuad).render(renderer) + + // restore original state + renderer.autoClear = originalAutoClear + renderer.setClearColor(this.originalClearColor) + renderer.setClearAlpha(originalClearAlpha) + } + + renderOverride(renderer, overrideMaterial, renderTarget, clearColor, clearAlpha) { + renderer.getClearColor(this.originalClearColor) + const originalClearAlpha = renderer.getClearAlpha() + const originalAutoClear = renderer.autoClear + + renderer.setRenderTarget(renderTarget) + renderer.autoClear = false + + clearColor = overrideMaterial.clearColor || clearColor + clearAlpha = overrideMaterial.clearAlpha || clearAlpha + if (clearColor !== undefined && clearColor !== null) { + renderer.setClearColor(clearColor) + renderer.setClearAlpha(clearAlpha || 0.0) + renderer.clear() + } + + const shadowmapEnabled = renderer.shadowMap.enabled + const shadowmapNeedsUpdate = renderer.shadowMap.needsUpdate + this.scene.overrideMaterial = overrideMaterial + renderer.shadowMap.enabled = false + renderer.shadowMap.needsUpdate = false + renderer.render(this.scene, this.camera) + renderer.shadowMap.enabled = shadowmapEnabled + renderer.shadowMap.needsUpdate = shadowmapNeedsUpdate + this.scene.overrideMaterial = null + + // restore original state + renderer.autoClear = originalAutoClear + renderer.setClearColor(this.originalClearColor) + renderer.setClearAlpha(originalClearAlpha) + } +} From c2d833026e38e220eb526db3c7e26fda6b4b900d Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Mon, 26 Sep 2022 17:20:08 +0300 Subject: [PATCH 03/15] Implemented selective opqaue objects only rendering in the depth and normal render targets for AO computation. Batcher now allows for various isolation of objects based on whatever category using batch update ranges --- packages/viewer-sandbox/src/Sandbox.ts | 12 +- packages/viewer-sandbox/src/main.ts | 4 +- .../viewer/src/modules/SpeckleRenderer.ts | 3 +- packages/viewer/src/modules/batching/Batch.ts | 6 + .../viewer/src/modules/batching/Batcher.ts | 226 +++++++++--------- .../viewer/src/modules/batching/LineBatch.ts | 7 +- .../viewer/src/modules/batching/MeshBatch.ts | 26 +- .../viewer/src/modules/batching/PointBatch.ts | 13 +- .../viewer/src/modules/pipeline/Pipeline.ts | 18 +- .../src/modules/pipeline/SpeckleSAOPass.ts | 16 +- 10 files changed, 199 insertions(+), 132 deletions(-) diff --git a/packages/viewer-sandbox/src/Sandbox.ts b/packages/viewer-sandbox/src/Sandbox.ts index 47490bd11..f0deceae9 100644 --- a/packages/viewer-sandbox/src/Sandbox.ts +++ b/packages/viewer-sandbox/src/Sandbox.ts @@ -42,7 +42,8 @@ export default class Sandbox { saoBlurRadius: 4, saoBlurStdDev: 4, saoBlurDepthCutoff: 0.0007 - } + }, + saoScaleOffset: 0 } public static lightParams: SunLightConfiguration = { @@ -370,6 +371,15 @@ export default class Sandbox { this.viewer.getRenderer().pipelineOptions = Sandbox.postParams this.viewer.requestRender() }) + postFolder + .addInput(Sandbox.postParams, 'saoScaleOffset', { + min: -100, + max: 100 + }) + .on('change', () => { + this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + this.viewer.requestRender() + }) postFolder .addInput(Sandbox.postParams.saoParams, 'saoKernelRadius', { diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index 05771aeb0..45924726c 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -82,7 +82,7 @@ sandbox.makeFilteringUI() await sandbox.loadUrl( // 'https://speckle.xyz/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://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' + 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' // 'Super' heavy revit shit // 'https://speckle.xyz/streams/e6f9156405/commits/0694d53bb5' // Same sample revit house, local to dim's computer @@ -91,7 +91,7 @@ await sandbox.loadUrl( // IFC building (good for a tree based structure) // 'https://latest.speckle.dev/streams/92b620fb17/commits/2ebd336223' // IFC story, a subtree of the above - 'https://latest.speckle.dev/streams/92b620fb17/objects/8247bbc53865b0e0cb5ee4e252e66216' + // 'https://latest.speckle.dev/streams/92b620fb17/objects/8247bbc53865b0e0cb5ee4e252e66216' // Small scale lines // 'https://speckle.xyz/streams/638d3b1f83/commits/6025e2b546?c=%5B2.18058,-0.20814,9.67642,3.85491,5.05364,0,0,1%5D' // 'https://latest.speckle.dev/streams/3ed8357f29/commits/d10f2af1ce' diff --git a/packages/viewer/src/modules/SpeckleRenderer.ts b/packages/viewer/src/modules/SpeckleRenderer.ts index 12e1906ad..35a394bef 100644 --- a/packages/viewer/src/modules/SpeckleRenderer.ts +++ b/packages/viewer/src/modules/SpeckleRenderer.ts @@ -143,7 +143,7 @@ export default class SpeckleRenderer { this._renderer.setSize(container.offsetWidth, container.offsetHeight) container.appendChild(this._renderer.domElement) - this.pipeline = new Pipeline(this._renderer) + this.pipeline = new Pipeline(this._renderer, this.batcher) this.pipeline.configure(this.scene, this.viewer.cameraHandler.activeCam.camera) this.pipeline.pipelineOptions = DefaultPipelineOptions @@ -274,6 +274,7 @@ export default class SpeckleRenderer { public render(camera: Camera) { this.batcher.render(this.renderer) this.pipeline.render(this.scene, camera) + // this.renderer.render(this.scene, camera) } public addRenderTree(subtreeId: string) { diff --git a/packages/viewer/src/modules/batching/Batch.ts b/packages/viewer/src/modules/batching/Batch.ts index 250660ec0..8577be8a3 100644 --- a/packages/viewer/src/modules/batching/Batch.ts +++ b/packages/viewer/src/modules/batching/Batch.ts @@ -20,6 +20,7 @@ export interface Batch { getCount(): number setBatchMaterial(material: Material): void setVisibleRange(...range: BatchUpdateRange[]) + getVisibleRange(): BatchUpdateRange setDrawRanges(...ranges: BatchUpdateRange[]) autoFillDrawRanges() resetDrawRanges() @@ -41,3 +42,8 @@ export const HideAllBatchUpdateRange = { offset: 0, count: 0 } as BatchUpdateRange + +export const AllBatchUpdateRange = { + offset: 0, + count: Infinity +} as BatchUpdateRange diff --git a/packages/viewer/src/modules/batching/Batcher.ts b/packages/viewer/src/modules/batching/Batcher.ts index 3c9848788..4d6e52953 100644 --- a/packages/viewer/src/modules/batching/Batcher.ts +++ b/packages/viewer/src/modules/batching/Batcher.ts @@ -5,10 +5,16 @@ import { WorldTree } from '../tree/WorldTree' import LineBatch from './LineBatch' import Materials from '../materials/Materials' import { NodeRenderView } from '../tree/NodeRenderView' -import { Batch, BatchUpdateRange, GeometryType, HideAllBatchUpdateRange } from './Batch' +import { + AllBatchUpdateRange, + Batch, + BatchUpdateRange, + GeometryType, + HideAllBatchUpdateRange +} from './Batch' import PointBatch from './PointBatch' // import { FilterMaterialType } from '../FilteringManager' -import { Material, WebGLRenderer } from 'three' +import { Material, Mesh, WebGLRenderer } from 'three' import { FilterMaterial, FilterMaterialType } from '../filtering/FilteringManager' export default class Batcher { @@ -96,6 +102,108 @@ export default class Batcher { } } + public saveVisiblity(): Record { + const visibilityRanges = {} + for (const k in this.batches) { + const batch: Batch = this.batches[k] + if (batch.geometryType !== GeometryType.MESH) continue + visibilityRanges[k] = batch.getVisibleRange() + } + return visibilityRanges + } + + public applyVisibility(ranges: Record) { + for (const k in this.batches) { + const batch: Batch = this.batches[k] + if (batch.geometryType !== GeometryType.MESH) continue + const range = ranges[k] + if (!range) { + batch.setVisibleRange(HideAllBatchUpdateRange) + } else { + batch.setVisibleRange(range) + } + } + } + + public getTransparent(): Record { + const visibilityRanges = {} + for (const k in this.batches) { + const batch: Batch = this.batches[k] + if (batch.geometryType !== GeometryType.MESH) continue + const batchMesh: Mesh = batch.renderObject as Mesh + if (batchMesh.geometry.groups.length === 0) { + if ((batchMesh.material as Material).transparent === true) + visibilityRanges[k] = AllBatchUpdateRange + } else { + const transparentGroup = batchMesh.geometry.groups.find((value) => { + return batchMesh.material[value.materialIndex].visible === true + }) + const hiddenGroup = batchMesh.geometry.groups.find((value) => { + return batchMesh.material[value.materialIndex].visible === false + }) + if (transparentGroup) { + visibilityRanges[k] = { + offset: transparentGroup.start, + count: + hiddenGroup !== undefined + ? hiddenGroup.start + : batch.getCount() - transparentGroup.start + } + } + } + } + return visibilityRanges + } + + public getOpaque() { + const visibilityRanges = {} + for (const k in this.batches) { + const batch: Batch = this.batches[k] + if (batch.geometryType !== GeometryType.MESH) continue + const batchMesh: Mesh = batch.renderObject as Mesh + if (batchMesh.geometry.groups.length === 0) { + if ((batchMesh.material as Material).transparent === false) + visibilityRanges[k] = AllBatchUpdateRange + } else { + const transparentOrHiddenGroup = batchMesh.geometry.groups.find((value) => { + return ( + batchMesh.material[value.materialIndex].transparent === true || + batchMesh.material[value.materialIndex].visible === false + ) + }) + visibilityRanges[k] = { + offset: 0, + count: + transparentOrHiddenGroup !== undefined + ? transparentOrHiddenGroup.start + : batch.getCount() + } + } + } + return visibilityRanges + } + + public enableTransparent(value: boolean) { + for (const k in this.batches) { + const batch: Batch = this.batches[k] + if (batch.geometryType !== GeometryType.MESH) continue + const batchMesh: Mesh = batch.renderObject as Mesh + if (batchMesh.geometry.groups.length === 0) { + batchMesh.visible = (batchMesh.material as Material).transparent + ? value + : batchMesh.visible + } else { + const transparentGroup = batchMesh.geometry.groups.find((value) => { + return batchMesh.material[value.materialIndex].transparent === true + }) + batch.setVisibleRange({ + offset: 0, + count: transparentGroup.start + }) + } + } + } + public purgeBatches(subtreeId: string) { for (const k in this.batches) { if (this.batches[k].subtreeId === subtreeId) { @@ -129,20 +237,6 @@ export default class Batcher { filterMaterial: FilterMaterial, uniqueRvsOnly = true ): string[] { - // const rvs = [] - // ids.forEach((val: string) => { - // rvs.push(WorldTree.getRenderTree().getRenderViewForNodeId(val)) - // /** The batcher should take the explicit IDs it's given and roll with them - // * It shouldn;t try to expand the list of render views on it's own - // */ - // // const views = WorldTree.getRenderTree().getRenderViewsForNodeId(val) - // // for (let k = 0; k < views.length; k++) { - // // if (rvs.includes(views[k])) return - // // } - // // rvs = rvs.concat(views) - // }) - // console.log(ids) - // console.log(rvs) let renderViews = rvs if (uniqueRvsOnly) renderViews = [...Array.from(new Set(rvs.map((value) => value)))] const batchIds = [...Array.from(new Set(renderViews.map((value) => value.batchId)))] @@ -175,12 +269,6 @@ export default class Batcher { if (!value) return this.batches[value].autoFillDrawRanges() }) - // let groupCount = 0 - // for (const k in this.batches) { - // const gLength = (this.batches[k].renderObject as Mesh).geometry.groups.length - // groupCount += gLength === 0 ? 1 : gLength - // } - // console.warn(groupCount) } /** Conveniece method. This should also work as a filtering action @@ -248,98 +336,4 @@ export default class Batcher { } } } - - /** KEEPING THESE FOR REFERENCE FOR NOW */ - /* - public selectRenderViews(renderViews: NodeRenderView[]) { - this.resetBatchesDrawRanges() - const batchIds = [...Array.from(new Set(renderViews.map((value) => value.batchId)))] - console.warn('<<<< BATCHES >>>>>>') - for (let i = 0; i < batchIds.length; i++) { - const batch = this.batches[batchIds[i]] - const views = renderViews - .filter((value) => value.batchId === batchIds[i]) - .map((rv: NodeRenderView) => { - return { - offset: rv.batchStart, - count: rv.batchCount, - material: this.materials.getHighlightMaterial(rv) - } - }) - // console.warn(views) - batch.setDrawRanges(true, ...views) - } - } - - public selectRenderView(renderView: NodeRenderView) { - this.resetBatchesDrawRanges() - const batch = this.batches[renderView.batchId] - batch.setDrawRanges( - false, - { - offset: 0, - count: renderView.batchStart, - material: batch.batchMaterial - } as BatchUpdateRange, - { - offset: renderView.batchStart, - count: renderView.batchCount, - material: this.materials.getHighlightMaterial(renderView) - } as BatchUpdateRange, - { - offset: renderView.batchEnd, - count: Infinity, - material: batch.batchMaterial - } as BatchUpdateRange - ) - } - - public isolateRenderView(renderView: NodeRenderView) { - this.resetBatchesDrawRanges() - - for (const k in this.batches) { - if (k === renderView.batchId) { - const batch = this.batches[renderView.batchId] - batch.setVisibleRange({ - offset: renderView.batchStart, - count: renderView.batchCount, - material: batch.batchMaterial - } as BatchUpdateRange) - batch.setDrawRanges(false, { - offset: renderView.batchStart, - count: renderView.batchCount, - material: batch.batchMaterial - } as BatchUpdateRange) - } else { - this.batches[k].setVisibleRange(HideAllBatchUpdateRange) - } - } - } - - public isolateRenderViews(renderViews: NodeRenderView[]) { - this.resetBatchesDrawRanges() - const batchIds = [...Array.from(new Set(renderViews.map((value) => value.batchId)))] - // console.warn('<<<< BATCHES >>>>>>') - for (const k in this.batches) { - if (!batchIds.includes(k)) { - this.batches[k].setVisibleRange(HideAllBatchUpdateRange) - } - } - for (let i = 0; i < batchIds.length; i++) { - const batch = this.batches[batchIds[i]] - const views = renderViews - .filter((value) => value.batchId === batchIds[i]) - .map((rv: NodeRenderView) => { - return { - offset: rv.batchStart, - count: rv.batchCount, - material: batch.batchMaterial - } - }) - // console.warn(views) - batch.setDrawRanges(false, ...views) - batch.setVisibleRange(...views) - } - } - */ } diff --git a/packages/viewer/src/modules/batching/LineBatch.ts b/packages/viewer/src/modules/batching/LineBatch.ts index 94bf38b30..509f952d1 100644 --- a/packages/viewer/src/modules/batching/LineBatch.ts +++ b/packages/viewer/src/modules/batching/LineBatch.ts @@ -15,7 +15,7 @@ import { Geometry } from '../converter/Geometry' import SpeckleLineMaterial from '../materials/SpeckleLineMaterial' import { NodeRenderView } from '../tree/NodeRenderView' import { Viewer } from '../Viewer' -import { Batch, BatchUpdateRange, GeometryType } from './Batch' +import { AllBatchUpdateRange, Batch, BatchUpdateRange, GeometryType } from './Batch' export default class LineBatch implements Batch { public id: string @@ -78,6 +78,11 @@ export default class LineBatch implements Batch { this.geometry.attributes['instanceColorEnd'].needsUpdate = true } + public getVisibleRange() { + return AllBatchUpdateRange + // TO DO if required + } + public setDrawRanges(...ranges: BatchUpdateRange[]) { const data = this.colorBuffer.array as number[] diff --git a/packages/viewer/src/modules/batching/MeshBatch.ts b/packages/viewer/src/modules/batching/MeshBatch.ts index d1eff75c2..4d715218b 100644 --- a/packages/viewer/src/modules/batching/MeshBatch.ts +++ b/packages/viewer/src/modules/batching/MeshBatch.ts @@ -14,7 +14,13 @@ import SpeckleStandardColoredMaterial from '../materials/SpeckleStandardColoredM import SpeckleMesh from '../objects/SpeckleMesh' import { NodeRenderView } from '../tree/NodeRenderView' import { Viewer } from '../Viewer' -import { Batch, BatchUpdateRange, GeometryType, HideAllBatchUpdateRange } from './Batch' +import { + AllBatchUpdateRange, + Batch, + BatchUpdateRange, + GeometryType, + HideAllBatchUpdateRange +} from './Batch' export default class MeshBatch implements Batch { public id: string @@ -64,6 +70,16 @@ export default class MeshBatch implements Batch { this.mesh.visible = false return } + if ( + ranges.length === 1 && + ranges[0].offset === AllBatchUpdateRange.offset && + ranges[0].count === AllBatchUpdateRange.count + ) { + this.geometry.setDrawRange(0, this.getCount()) + this.mesh.visible = true + return + } + let minOffset = Infinity let maxOffset = 0 ranges.forEach((range) => { @@ -77,6 +93,14 @@ export default class MeshBatch implements Batch { ) } + public getVisibleRange(): BatchUpdateRange { + if (this.geometry.groups.length === 0) return AllBatchUpdateRange + return { + offset: this.geometry.drawRange.start, + count: this.geometry.drawRange.count + } + } + public setDrawRanges(...ranges: BatchUpdateRange[]) { const materials = ranges.map((val) => val.material) const uniqueMaterials = [...Array.from(new Set(materials.map((value) => value)))] diff --git a/packages/viewer/src/modules/batching/PointBatch.ts b/packages/viewer/src/modules/batching/PointBatch.ts index 002e36459..3ad67f4f2 100644 --- a/packages/viewer/src/modules/batching/PointBatch.ts +++ b/packages/viewer/src/modules/batching/PointBatch.ts @@ -9,7 +9,13 @@ import { import { Geometry } from '../converter/Geometry' import { NodeRenderView } from '../tree/NodeRenderView' import { Viewer } from '../Viewer' -import { Batch, BatchUpdateRange, GeometryType, HideAllBatchUpdateRange } from './Batch' +import { + AllBatchUpdateRange, + Batch, + BatchUpdateRange, + GeometryType, + HideAllBatchUpdateRange +} from './Batch' export default class PointBatch implements Batch { public id: string @@ -67,6 +73,11 @@ export default class PointBatch implements Batch { maxOffset - minOffset + ranges.find((val) => val.offset === maxOffset).count ) } + + public getVisibleRange() { + return AllBatchUpdateRange + } + /** * This is the first version for multi draw ranges with automatic fill support * In the near future, we'll re-sort the index buffer so we minimize draw calls to diff --git a/packages/viewer/src/modules/pipeline/Pipeline.ts b/packages/viewer/src/modules/pipeline/Pipeline.ts index f4a55ff40..199ac0e43 100644 --- a/packages/viewer/src/modules/pipeline/Pipeline.ts +++ b/packages/viewer/src/modules/pipeline/Pipeline.ts @@ -2,31 +2,35 @@ import { Camera, Scene, WebGLRenderer } from 'three' import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js' import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js' import { SAOPass, SAOPassParams } from 'three/examples/jsm/postprocessing/SAOPass.js' +import Batcher from '../batching/Batcher' import { ApplySAOPass } from './ApplySAOPass' import { SpeckleSAOPass } from './SpeckleSAOPass' export interface PipelineOptions { saoEnabled?: boolean saoParams?: Partial + saoScaleOffset?: number } export const DefaultPipelineOptions: PipelineOptions = { saoEnabled: true, saoParams: { - saoBias: 0.15, - saoIntensity: 1.5, + saoBias: 0, + saoIntensity: 1.25, saoScale: 434, - saoKernelRadius: 20, + saoKernelRadius: 10, saoMinResolution: 0, saoBlur: true, saoBlurRadius: 4, saoBlurStdDev: 4, saoBlurDepthCutoff: 0.0007 - } + }, + saoScaleOffset: 0 } export class Pipeline { private _renderer: WebGLRenderer = null + private _batcher: Batcher = null private _pipelineOptions: PipelineOptions = {} private composer: EffectComposer = null private renderPass: RenderPass = null @@ -38,18 +42,20 @@ export class Pipeline { if (this.saoPass) { this.applySaoPass.enabled = this._pipelineOptions.saoEnabled Object.assign(this.saoPass.params, this._pipelineOptions.saoParams) + this.saoPass.params.saoScale += this._pipelineOptions.saoScaleOffset } } - public constructor(renderer: WebGLRenderer) { + public constructor(renderer: WebGLRenderer, batcher: Batcher) { this._renderer = renderer + this._batcher = batcher this.composer = new EffectComposer(renderer) this.composer.readBuffer = null this.composer.writeBuffer = null } public configure(scene: Scene, camera: Camera) { - this.saoPass = new SpeckleSAOPass(scene, camera, false, true) + this.saoPass = new SpeckleSAOPass(scene, camera, this._batcher, false, true) this.composer.addPass(this.saoPass) this.renderPass = new RenderPass(scene, camera) this.renderPass.renderToScreen = true diff --git a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts index c95919faf..7ebb065b9 100644 --- a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts +++ b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts @@ -1,7 +1,9 @@ import { + Camera, NoBlending, OrthographicCamera, PerspectiveCamera, + Scene, ShaderMaterial, UniformsUtils, Vector2 @@ -12,6 +14,7 @@ import { BlurShaderUtils } from 'three/examples/jsm/shaders/DepthLimitedBlurShad import { speckleSaoFrag } from '../materials/shaders/speckle-sao-frag' import { speckleSaoVert } from '../materials/shaders/speckle-sao-vert' import { SAOShader } from 'three/examples/jsm/shaders/SAOShader.js' +import Batcher from '../batching/Batcher' /** * SAO implementation inspired from bhouston previous SAO work @@ -21,16 +24,19 @@ export class SpeckleSAOPass extends SAOPass { private _oldClearColor private prevStdDev private prevNumSamples + private batcher: Batcher = null constructor( - scene, - camera, + scene: Scene, + camera: Camera, + batcher: Batcher, useDepthTexture = false, useNormals = false, resolution = new Vector2(256, 256) ) { super(scene, camera, useDepthTexture, useNormals, resolution) + this.batcher = batcher this.saoMaterial = new ShaderMaterial({ defines: { NUM_SAMPLES: 7, @@ -146,7 +152,10 @@ export class SpeckleSAOPass extends SAOPass { // renderer.setRenderTarget(this.beautyRenderTarget) // renderer.clear() // renderer.render(this.scene, this.camera) - + const restoreVisibility = this.batcher.saveVisiblity() + const opaque = this.batcher.getOpaque() + // const transparent = this.batcher.getTransparent() + this.batcher.applyVisibility(opaque) // Re-render scene if depth texture extension is not supported if (!this.supportsDepthTextureExtension) { // Clear rule : far clipping plane in both RGBA and Basic encoding @@ -169,6 +178,7 @@ export class SpeckleSAOPass extends SAOPass { 1.0 ) } + this.batcher.applyVisibility(restoreVisibility) // Rendering SAO texture this.renderPass(renderer, this.saoMaterial, this.saoRenderTarget, 0xffffff, 1.0) From 5d02fb2423ef18d273952032a2a232ad072c259c Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Tue, 27 Sep 2022 10:36:59 +0300 Subject: [PATCH 04/15] Fix for frontend which starts off the viewer with 0 width and heiht. This makes the framebuffers incomplete and errors are thrown. We now render to framebuffers only after we have a valid rendering size --- packages/viewer/src/modules/SpeckleRenderer.ts | 5 +++++ packages/viewer/src/modules/Viewer.ts | 5 +---- packages/viewer/src/modules/pipeline/Pipeline.ts | 10 +++++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/viewer/src/modules/SpeckleRenderer.ts b/packages/viewer/src/modules/SpeckleRenderer.ts index 35a394bef..76111efee 100644 --- a/packages/viewer/src/modules/SpeckleRenderer.ts +++ b/packages/viewer/src/modules/SpeckleRenderer.ts @@ -277,6 +277,11 @@ export default class SpeckleRenderer { // this.renderer.render(this.scene, camera) } + public resize(width: number, height: number) { + this.renderer.setSize(width, height) + this.pipeline.resize(width, height) + } + public addRenderTree(subtreeId: string) { this.batcher.makeBatches( subtreeId, diff --git a/packages/viewer/src/modules/Viewer.ts b/packages/viewer/src/modules/Viewer.ts index 33ace42d4..9fbbd82a2 100644 --- a/packages/viewer/src/modules/Viewer.ts +++ b/packages/viewer/src/modules/Viewer.ts @@ -135,10 +135,7 @@ export class Viewer extends EventEmitter implements IViewer { } public resize() { - this.speckleRenderer.renderer.setSize( - this.container.offsetWidth, - this.container.offsetHeight - ) + this.speckleRenderer.resize(this.container.offsetWidth, this.container.offsetHeight) this.needsRender = true } diff --git a/packages/viewer/src/modules/pipeline/Pipeline.ts b/packages/viewer/src/modules/pipeline/Pipeline.ts index 199ac0e43..162f88071 100644 --- a/packages/viewer/src/modules/pipeline/Pipeline.ts +++ b/packages/viewer/src/modules/pipeline/Pipeline.ts @@ -1,4 +1,4 @@ -import { Camera, Scene, WebGLRenderer } from 'three' +import { Camera, Scene, Vector2, WebGLRenderer } from 'three' import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js' import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js' import { SAOPass, SAOPassParams } from 'three/examples/jsm/postprocessing/SAOPass.js' @@ -36,6 +36,7 @@ export class Pipeline { private renderPass: RenderPass = null private saoPass: SAOPass = null private applySaoPass: ApplySAOPass = null + private drawingSize: Vector2 = new Vector2() public set pipelineOptions(options: PipelineOptions) { Object.assign(this._pipelineOptions, options) @@ -66,10 +67,17 @@ export class Pipeline { } public render(scene: Scene, camera: Camera) { + this._renderer.getDrawingBufferSize(this.drawingSize) + if (this.drawingSize.length() === 0) return + this.renderPass.scene = scene this.renderPass.camera = camera this.saoPass.scene = scene this.saoPass.camera = camera this.composer.render() } + + public resize(width: number, height: number) { + this.composer.setSize(width, height) + } } From 38113886a7dace63773b12cc753b0b29d88acef4 Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Tue, 27 Sep 2022 11:31:28 +0300 Subject: [PATCH 05/15] Projection matrix and it's inverse were not timely updated and caused the SAO output to be all black --- packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts index 7ebb065b9..3c1a7ea0e 100644 --- a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts +++ b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts @@ -104,6 +104,11 @@ export class SpeckleSAOPass extends SAOPass { this.saoMaterial.uniforms['cameraFar'].value = ( this.camera as PerspectiveCamera | OrthographicCamera ).far + this.saoMaterial.uniforms['cameraInverseProjectionMatrix'].value.copy( + this.camera.projectionMatrixInverse + ) + this.saoMaterial.uniforms['cameraProjectionMatrix'].value = + this.camera.projectionMatrix // this.saoMaterial.uniforms['randomSeed'].value = Math.random(); const depthCutoff = From 63714e9a173063ef105607c68f8e49963b12f343 Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Tue, 27 Sep 2022 13:15:24 +0300 Subject: [PATCH 06/15] Fixed an issue where line batches were rendered during the SAO passes and messing up the result. Fixed an existing issue where transparent ranges were shuffled before opaque ones --- .../viewer/src/modules/batching/Batcher.ts | 14 +++++++--- .../viewer/src/modules/batching/LineBatch.ts | 27 ++++++++++++++++++- .../viewer/src/modules/batching/MeshBatch.ts | 15 ++++++++++- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/packages/viewer/src/modules/batching/Batcher.ts b/packages/viewer/src/modules/batching/Batcher.ts index 4d6e52953..79ab95f60 100644 --- a/packages/viewer/src/modules/batching/Batcher.ts +++ b/packages/viewer/src/modules/batching/Batcher.ts @@ -106,7 +106,7 @@ export default class Batcher { const visibilityRanges = {} for (const k in this.batches) { const batch: Batch = this.batches[k] - if (batch.geometryType !== GeometryType.MESH) continue + // if (batch.geometryType !== GeometryType.MESH) continue visibilityRanges[k] = batch.getVisibleRange() } return visibilityRanges @@ -115,7 +115,7 @@ export default class Batcher { public applyVisibility(ranges: Record) { for (const k in this.batches) { const batch: Batch = this.batches[k] - if (batch.geometryType !== GeometryType.MESH) continue + // if (batch.geometryType !== GeometryType.MESH) continue const range = ranges[k] if (!range) { batch.setVisibleRange(HideAllBatchUpdateRange) @@ -129,7 +129,10 @@ export default class Batcher { const visibilityRanges = {} for (const k in this.batches) { const batch: Batch = this.batches[k] - if (batch.geometryType !== GeometryType.MESH) continue + if (batch.geometryType !== GeometryType.MESH) { + visibilityRanges[k] = HideAllBatchUpdateRange + continue + } const batchMesh: Mesh = batch.renderObject as Mesh if (batchMesh.geometry.groups.length === 0) { if ((batchMesh.material as Material).transparent === true) @@ -159,7 +162,10 @@ export default class Batcher { const visibilityRanges = {} for (const k in this.batches) { const batch: Batch = this.batches[k] - if (batch.geometryType !== GeometryType.MESH) continue + if (batch.geometryType !== GeometryType.MESH) { + visibilityRanges[k] = HideAllBatchUpdateRange + continue + } const batchMesh: Mesh = batch.renderObject as Mesh if (batchMesh.geometry.groups.length === 0) { if ((batchMesh.material as Material).transparent === false) diff --git a/packages/viewer/src/modules/batching/LineBatch.ts b/packages/viewer/src/modules/batching/LineBatch.ts index 509f952d1..cfcd57692 100644 --- a/packages/viewer/src/modules/batching/LineBatch.ts +++ b/packages/viewer/src/modules/batching/LineBatch.ts @@ -15,7 +15,13 @@ import { Geometry } from '../converter/Geometry' import SpeckleLineMaterial from '../materials/SpeckleLineMaterial' import { NodeRenderView } from '../tree/NodeRenderView' import { Viewer } from '../Viewer' -import { AllBatchUpdateRange, Batch, BatchUpdateRange, GeometryType } from './Batch' +import { + AllBatchUpdateRange, + Batch, + BatchUpdateRange, + GeometryType, + HideAllBatchUpdateRange +} from './Batch' export default class LineBatch implements Batch { public id: string @@ -58,6 +64,24 @@ export default class LineBatch implements Batch { } public setVisibleRange(...ranges: BatchUpdateRange[]) { + if ( + ranges.length === 1 && + ranges[0].offset === HideAllBatchUpdateRange.offset && + ranges[0].count === HideAllBatchUpdateRange.count + ) { + this.mesh.visible = false + return + } + + if ( + ranges.length === 1 && + ranges[0].offset === AllBatchUpdateRange.offset && + ranges[0].count === AllBatchUpdateRange.count + ) { + this.mesh.visible = true + return + } + this.mesh.visible = true const data = this.colorBuffer.array as number[] for (let k = 0; k < data.length; k += 4) { data[k + 3] = 0 @@ -123,6 +147,7 @@ export default class LineBatch implements Batch { material: this.batchMaterial }) this.mesh.material = this.batchMaterial + this.mesh.visible = true } public buildBatch() { diff --git a/packages/viewer/src/modules/batching/MeshBatch.ts b/packages/viewer/src/modules/batching/MeshBatch.ts index 4d715218b..2c352f297 100644 --- a/packages/viewer/src/modules/batching/MeshBatch.ts +++ b/packages/viewer/src/modules/batching/MeshBatch.ts @@ -219,7 +219,7 @@ export default class MeshBatch implements Batch { b.materialIndex ] const visibleOrder = +materialB.visible - +materialA.visible - const transparentOrder = +materialB.transparent - +materialA.transparent + const transparentOrder = +materialA.transparent - +materialB.transparent if (visibleOrder !== 0) return visibleOrder return transparentOrder }) @@ -231,6 +231,19 @@ export default class MeshBatch implements Batch { return previousValue }, materialOrder) + // if (materialOrder.length > 1) { + // for (let m = 0; m < materialOrder.length; m++) { + // if (!this.mesh.material[materialOrder[m]].visible) + // console.log( + // `Batch ${this.id} material: Hidden -> ${!this.mesh.material[ + // materialOrder[m] + // ].visible}, Transparent -> ${ + // this.mesh.material[materialOrder[m]].transparent + // }` + // ) + // } + // } + const grouped = [] for (let k = 0; k < materialOrder.length; k++) { grouped.push( From 21b2e906220266a9f52372721f30ee9398796a27 Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Tue, 27 Sep 2022 13:55:17 +0300 Subject: [PATCH 07/15] Added RTE to the SAO pipeline --- packages/viewer-sandbox/src/main.ts | 4 +- .../modules/materials/SpeckleDepthMaterial.ts | 4 + .../materials/SpeckleNormalMaterial.ts | 95 +++++++++++++++++++ .../materials/shaders/speckle-normal-frag.ts | 24 +++++ .../materials/shaders/speckle-normal-vert.ts | 68 +++++++++++++ .../src/modules/pipeline/SpeckleSAOPass.ts | 15 +++ 6 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 packages/viewer/src/modules/materials/SpeckleNormalMaterial.ts create mode 100644 packages/viewer/src/modules/materials/shaders/speckle-normal-frag.ts create mode 100644 packages/viewer/src/modules/materials/shaders/speckle-normal-vert.ts diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index ed2c08258..cdac9b6f6 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -82,7 +82,7 @@ sandbox.makeFilteringUI() await sandbox.loadUrl( // 'https://speckle.xyz/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://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' + // 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' // 'Super' heavy revit shit // 'https://speckle.xyz/streams/e6f9156405/commits/0694d53bb5' // Same sample revit house, local to dim's computer @@ -103,4 +103,6 @@ await sandbox.loadUrl( // 'https://latest.speckle.dev/streams/0c6ad366c4/commits/aa1c393aec' //Car // 'https://latest.speckle.dev/streams/17d2e25a97/commits/6b6cf3d43e' + // Jonathon's + 'https://latest.speckle.dev/streams/501258ee5f/commits/f885570011' ) diff --git a/packages/viewer/src/modules/materials/SpeckleDepthMaterial.ts b/packages/viewer/src/modules/materials/SpeckleDepthMaterial.ts index f1b1f011f..3a905f119 100644 --- a/packages/viewer/src/modules/materials/SpeckleDepthMaterial.ts +++ b/packages/viewer/src/modules/materials/SpeckleDepthMaterial.ts @@ -89,6 +89,9 @@ class SpeckleDepthMaterial extends MeshDepthMaterial { return this } + /** Another note here, this will NOT get called by three when rendering shadowmaps. We update the uniforms manually + * inside SpeckleRenderer for shadowmaps + */ onBeforeRender(_this, scene, camera, geometry, object, group) { SpeckleDepthMaterial.matBuff.copy(camera.matrixWorldInverse) SpeckleDepthMaterial.matBuff.elements[12] = 0 @@ -111,6 +114,7 @@ class SpeckleDepthMaterial extends MeshDepthMaterial { this.userData.uViewer_low.value.copy(SpeckleDepthMaterial.vecBuff1) this.userData.uViewer_high.value.copy(SpeckleDepthMaterial.vecBuff2) + this.userData.rteModelViewMatrix.value.copy(object.modelViewMatrix) this.needsUpdate = true } diff --git a/packages/viewer/src/modules/materials/SpeckleNormalMaterial.ts b/packages/viewer/src/modules/materials/SpeckleNormalMaterial.ts new file mode 100644 index 000000000..d54193b17 --- /dev/null +++ b/packages/viewer/src/modules/materials/SpeckleNormalMaterial.ts @@ -0,0 +1,95 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable camelcase */ +import { speckleNormalVert } from './shaders/speckle-normal-vert' +import { speckleNormalFrag } from './shaders/speckle-normal-frag' +import { UniformsUtils, ShaderLib, Vector3, MeshNormalMaterial } from 'three' +import { Matrix4 } from 'three' +import { Geometry } from '../converter/Geometry' + +class SpeckleNormalMaterial extends MeshNormalMaterial { + protected static readonly matBuff: Matrix4 = new Matrix4() + protected static readonly vecBuff0: Vector3 = new Vector3() + protected static readonly vecBuff1: Vector3 = new Vector3() + protected static readonly vecBuff2: Vector3 = new Vector3() + + constructor(parameters, defines = []) { + super(parameters) + + this.userData.uViewer_high = { + value: new Vector3() + } + this.userData.uViewer_low = { + value: new Vector3() + } + ;(this as any).vertProgram = speckleNormalVert + ;(this as any).fragProgram = speckleNormalFrag + ;(this as any).uniforms = UniformsUtils.merge([ + ShaderLib.standard.uniforms, + { + uViewer_high: { + value: this.userData.uViewer_high.value + }, + uViewer_low: { + value: this.userData.uViewer_low.value + } + } + ]) + + this.onBeforeCompile = function (shader) { + shader.uniforms.uViewer_high = this.userData.uViewer_high + shader.uniforms.uViewer_low = this.userData.uViewer_low + shader.vertexShader = this.vertProgram + shader.fragmentShader = this.fragProgram + } + + if (defines) { + this.defines = {} + } + for (let k = 0; k < defines.length; k++) { + this.defines[defines[k]] = ' ' + } + } + + copy(source) { + super.copy(source) + this.userData = {} + this.userData.uViewer_high = { + value: new Vector3() + } + this.userData.uViewer_low = { + value: new Vector3() + } + this.defines['USE_RTE'] = ' ' + + return this + } + + onBeforeRender(_this, scene, camera, geometry, object, group) { + SpeckleNormalMaterial.matBuff.copy(camera.matrixWorldInverse) + SpeckleNormalMaterial.matBuff.elements[12] = 0 + SpeckleNormalMaterial.matBuff.elements[13] = 0 + SpeckleNormalMaterial.matBuff.elements[14] = 0 + SpeckleNormalMaterial.matBuff.multiply(object.matrixWorld) + object.modelViewMatrix.copy(SpeckleNormalMaterial.matBuff) + + SpeckleNormalMaterial.vecBuff0.set( + camera.matrixWorld.elements[12], + camera.matrixWorld.elements[13], + camera.matrixWorld.elements[14] + ) + + Geometry.DoubleToHighLowVector( + SpeckleNormalMaterial.vecBuff0, + SpeckleNormalMaterial.vecBuff1, + SpeckleNormalMaterial.vecBuff2 + ) + + this.userData.uViewer_low.value.copy(SpeckleNormalMaterial.vecBuff1) + this.userData.uViewer_high.value.copy(SpeckleNormalMaterial.vecBuff2) + + this.needsUpdate = true + } +} + +export default SpeckleNormalMaterial diff --git a/packages/viewer/src/modules/materials/shaders/speckle-normal-frag.ts b/packages/viewer/src/modules/materials/shaders/speckle-normal-frag.ts new file mode 100644 index 000000000..b42c4df73 --- /dev/null +++ b/packages/viewer/src/modules/materials/shaders/speckle-normal-frag.ts @@ -0,0 +1,24 @@ +export const speckleNormalFrag = /* glsl */ ` +#define NORMAL +uniform float opacity; +#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) + varying vec3 vViewPosition; +#endif +#include +#include +#include +#include +#include +#include +#include +void main() { + #include + #include + #include + #include + gl_FragColor = vec4( packNormalToRGB( normal ), opacity ); + #ifdef OPAQUE + gl_FragColor.a = 1.0; + #endif +} +` diff --git a/packages/viewer/src/modules/materials/shaders/speckle-normal-vert.ts b/packages/viewer/src/modules/materials/shaders/speckle-normal-vert.ts new file mode 100644 index 000000000..d54bd0091 --- /dev/null +++ b/packages/viewer/src/modules/materials/shaders/speckle-normal-vert.ts @@ -0,0 +1,68 @@ +export const speckleNormalVert = /* glsl */ ` +#define NORMAL +#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; +#endif +#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) + varying vec3 vViewPosition; +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +vec4 computeRelativePosition(in vec3 position_low, in vec3 position_high, in vec3 relativeTo_low, in 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 + */ + vec3 t1 = position_low.xyz - relativeTo_low; + vec3 e = t1 - position_low.xyz; + vec3 t2 = ((-relativeTo_low - e) + (position_low.xyz - (t1 - e))) + position_high.xyz - relativeTo_high; + vec3 highDifference = t1 + t2; + vec3 lowDifference = t2 - (highDifference - t1); + vec3 position = highDifference.xyz + lowDifference.xyz; + return vec4(position, 1.); +} + +void main() { + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + //#include // EDITED CHUNK + #ifdef USE_RTE + vec4 mvPosition = computeRelativePosition(position_low.xyz, position.xyz, uViewer_low, uViewer_high); + #else + vec4 mvPosition = vec4( transformed, 1.0 ); + #endif + + #ifdef USE_INSTANCING + + mvPosition = instanceMatrix * mvPosition; + + #endif + mvPosition = modelViewMatrix * mvPosition; + + gl_Position = projectionMatrix * mvPosition; + #include + #include +#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) + vViewPosition = - mvPosition.xyz; +#endif +} +` diff --git a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts index 3c1a7ea0e..6d772bcce 100644 --- a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts +++ b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts @@ -3,6 +3,7 @@ import { NoBlending, OrthographicCamera, PerspectiveCamera, + RGBADepthPacking, Scene, ShaderMaterial, UniformsUtils, @@ -15,6 +16,8 @@ import { speckleSaoFrag } from '../materials/shaders/speckle-sao-frag' import { speckleSaoVert } from '../materials/shaders/speckle-sao-vert' import { SAOShader } from 'three/examples/jsm/shaders/SAOShader.js' import Batcher from '../batching/Batcher' +import SpeckleDepthMaterial from '../materials/SpeckleDepthMaterial' +import SpeckleNormalMaterial from '../materials/SpeckleNormalMaterial' /** * SAO implementation inspired from bhouston previous SAO work @@ -37,6 +40,18 @@ export class SpeckleSAOPass extends SAOPass { super(scene, camera, useDepthTexture, useNormals, resolution) this.batcher = batcher + + this.depthMaterial = new SpeckleDepthMaterial( + { + depthPacking: RGBADepthPacking + }, + ['USE_RTE', 'ALPHATEST_REJECTION'] + ) + this.depthMaterial.blending = NoBlending + + this.normalMaterial = new SpeckleNormalMaterial({}, ['USE_RTE']) + this.normalMaterial.blending = NoBlending + this.saoMaterial = new ShaderMaterial({ defines: { NUM_SAMPLES: 7, From 5f169a2d2332cd64d8c9ed91ccd4b94f4ac38d88 Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Tue, 27 Sep 2022 14:04:16 +0300 Subject: [PATCH 08/15] SAO now takes the clipping planes into account --- packages/viewer-sandbox/src/main.ts | 4 ++-- packages/viewer/src/modules/SpeckleRenderer.ts | 1 + packages/viewer/src/modules/pipeline/Pipeline.ts | 7 ++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index cdac9b6f6..18786bef8 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -82,7 +82,7 @@ sandbox.makeFilteringUI() await sandbox.loadUrl( // 'https://speckle.xyz/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://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' + 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' // 'Super' heavy revit shit // 'https://speckle.xyz/streams/e6f9156405/commits/0694d53bb5' // Same sample revit house, local to dim's computer @@ -104,5 +104,5 @@ await sandbox.loadUrl( //Car // 'https://latest.speckle.dev/streams/17d2e25a97/commits/6b6cf3d43e' // Jonathon's - 'https://latest.speckle.dev/streams/501258ee5f/commits/f885570011' + // 'https://latest.speckle.dev/streams/501258ee5f/commits/f885570011' ) diff --git a/packages/viewer/src/modules/SpeckleRenderer.ts b/packages/viewer/src/modules/SpeckleRenderer.ts index c5c5cdb13..c22812ed0 100644 --- a/packages/viewer/src/modules/SpeckleRenderer.ts +++ b/packages/viewer/src/modules/SpeckleRenderer.ts @@ -374,6 +374,7 @@ export default class SpeckleRenderer { } } }) + this.pipeline.updateClippingPlanes(planes) this.renderer.shadowMap.needsUpdate = true } diff --git a/packages/viewer/src/modules/pipeline/Pipeline.ts b/packages/viewer/src/modules/pipeline/Pipeline.ts index 162f88071..27758dfea 100644 --- a/packages/viewer/src/modules/pipeline/Pipeline.ts +++ b/packages/viewer/src/modules/pipeline/Pipeline.ts @@ -1,4 +1,4 @@ -import { Camera, Scene, Vector2, WebGLRenderer } from 'three' +import { Camera, Plane, Scene, Vector2, WebGLRenderer } from 'three' import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js' import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js' import { SAOPass, SAOPassParams } from 'three/examples/jsm/postprocessing/SAOPass.js' @@ -66,6 +66,11 @@ export class Pipeline { this.composer.addPass(this.applySaoPass) } + public updateClippingPlanes(planes: Plane[]) { + this.saoPass.depthMaterial.clippingPlanes = planes + this.saoPass.normalMaterial.clippingPlanes = planes + } + public render(scene: Scene, camera: Camera) { this._renderer.getDrawingBufferSize(this.drawingSize) if (this.drawingSize.length() === 0) return From dcbfa448e91e5882a488c38103c0ec6b33f5b815 Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Wed, 28 Sep 2022 14:04:44 +0300 Subject: [PATCH 09/15] We need to render both front and back faces to depth and normal passes for AO --- packages/viewer-sandbox/src/main.ts | 2 ++ packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts | 3 +++ 2 files changed, 5 insertions(+) diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index 18786bef8..fce396e4e 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -105,4 +105,6 @@ await sandbox.loadUrl( // 'https://latest.speckle.dev/streams/17d2e25a97/commits/6b6cf3d43e' // Jonathon's // 'https://latest.speckle.dev/streams/501258ee5f/commits/f885570011' + // Alex's cube + // 'https://latest.speckle.dev/streams/46e3e0e1ec/commits/a6392c19d6?c=%5B6.85874,2.9754,0.79022,0,0,0,0,1%5D' ) diff --git a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts index 6d772bcce..28f2b0b61 100644 --- a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts +++ b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts @@ -1,5 +1,6 @@ import { Camera, + DoubleSide, NoBlending, OrthographicCamera, PerspectiveCamera, @@ -48,9 +49,11 @@ export class SpeckleSAOPass extends SAOPass { ['USE_RTE', 'ALPHATEST_REJECTION'] ) this.depthMaterial.blending = NoBlending + this.depthMaterial.side = DoubleSide this.normalMaterial = new SpeckleNormalMaterial({}, ['USE_RTE']) this.normalMaterial.blending = NoBlending + this.normalMaterial.side = DoubleSide this.saoMaterial = new ShaderMaterial({ defines: { From df316ceddb65c5bfe4462138cc20363e6cb29d9d Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Wed, 28 Sep 2022 15:18:44 +0300 Subject: [PATCH 10/15] Implemented improved normal reconstruction for the SAO pass. This gets rid of the artifacts created by incorrect vertex normals --- packages/viewer-sandbox/src/main.ts | 4 +- .../materials/shaders/speckle-sao-frag.ts | 67 ++++++++++++++++++- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index fce396e4e..984346a66 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -82,7 +82,7 @@ sandbox.makeFilteringUI() await sandbox.loadUrl( // 'https://speckle.xyz/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://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' + // 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' // 'Super' heavy revit shit // 'https://speckle.xyz/streams/e6f9156405/commits/0694d53bb5' // Same sample revit house, local to dim's computer @@ -107,4 +107,6 @@ await sandbox.loadUrl( // 'https://latest.speckle.dev/streams/501258ee5f/commits/f885570011' // Alex's cube // 'https://latest.speckle.dev/streams/46e3e0e1ec/commits/a6392c19d6?c=%5B6.85874,2.9754,0.79022,0,0,0,0,1%5D' + // Groups of groups + 'https://speckle.xyz/streams/1ce562e99a/commits/6fa28a5a0f' ) diff --git a/packages/viewer/src/modules/materials/shaders/speckle-sao-frag.ts b/packages/viewer/src/modules/materials/shaders/speckle-sao-frag.ts index 5192bd974..cdb16d1b3 100644 --- a/packages/viewer/src/modules/materials/shaders/speckle-sao-frag.ts +++ b/packages/viewer/src/modules/materials/shaders/speckle-sao-frag.ts @@ -48,13 +48,75 @@ export const speckleSaoFrag = /* glsl */ ` clipPosition *= clipW; // unprojection. return ( cameraInverseProjectionMatrix * clipPosition ).xyz; } + + //https://wickedengine.net/2019/09/22/improved-normal-reconstruction-from-depth/ + vec3 depth_cross(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 = getDepth( uv - ddy ); + float sampleViewZ = getViewZ( sampleDepth ); + highp vec3 top = getViewPosition( uv - ddy, sampleDepth, sampleViewZ ); + + sampleDepth = getDepth( uv + ddy ); + sampleViewZ = getViewZ( sampleDepth ); + highp vec3 bottom = getViewPosition( uv + ddy, sampleDepth, sampleViewZ ); + + highp vec3 center = origin; + + sampleDepth = getDepth( uv - ddx ); + sampleViewZ = getViewZ( sampleDepth ); + highp vec3 left = getViewPosition( uv - ddx, sampleDepth, sampleViewZ ); + + sampleDepth = getDepth( uv + ddx ); + sampleViewZ = getViewZ( sampleDepth ); + highp vec3 right = getViewPosition( uv + ddx, sampleDepth, sampleViewZ ); + + highp float dx0 = abs(right.z - center.z); + highp float dx1 = abs(left.z - center.z); + highp float dy0 = abs(bottom.z - center.z); + highp float dy1 = abs(top.z - center.z); + + int best_Z_horizontal = dx0 < dx1 ? 1 : 2; + int best_Z_vertical = dy0 < dy1 ? 3 : 4; + + vec3 P1, P2; + if (best_Z_horizontal == 1 && best_Z_vertical == 4) + { + P1 = right; + P2 = top; + } + else if (best_Z_horizontal == 1 && best_Z_vertical == 3) + { + P1 = bottom; + P2 = right; + } + else if (best_Z_horizontal == 2 && best_Z_vertical == 4) + { + P1 = top; + P2 = left; + } + else if (best_Z_horizontal == 2 && best_Z_vertical == 3) + { + P1 = left; + P2 = bottom; + } + + return normalize(cross(P2 - center, P1 - center)); + } + vec3 getViewNormal( const in vec3 viewPosition, const in vec2 screenPosition ) { #if NORMAL_TEXTURE == 1 - return unpackRGBToNormal( texture2D( tNormal, screenPosition ).xyz ); + return unpackRGBToNormal( texture2D( tNormal, screenPosition ).xyz ); + #elif IMPROVED_NORMAL_RECONSTRUCTION == 1 + return depth_cross(screenPosition, viewPosition); #else - return normalize( cross( dFdx( viewPosition ), dFdy( viewPosition ) ) ); + return normalize( cross( dFdx( viewPosition ), dFdy( viewPosition ) ) ); #endif } + float scaleDividedByCameraFar; float minResolutionMultipliedByCameraFar; float getOcclusion( const in vec3 centerViewPosition, const in vec3 centerViewNormal, const in vec3 sampleViewPosition ) { @@ -103,4 +165,5 @@ export const speckleSaoFrag = /* glsl */ ` float ambientOcclusion = getAmbientOcclusion( viewPosition ); gl_FragColor = getDefaultColor( vUv ); gl_FragColor.xyz *= 1.0 - ambientOcclusion; + // gl_FragColor.xyz = depth_cross(vUv, viewPosition) * 0.5 + 0.5; }` From 54bd645f6fd7858b897834ff31c3467fdf76bea7 Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Thu, 29 Sep 2022 13:31:19 +0300 Subject: [PATCH 11/15] Real time switching between default and improved normals generation --- packages/viewer-sandbox/src/Sandbox.ts | 91 +++++++++-------- packages/viewer-sandbox/src/main.ts | 4 +- .../viewer/src/modules/pipeline/Pipeline.ts | 21 ++-- .../src/modules/pipeline/SpeckleSAOPass.ts | 99 +++++++------------ 4 files changed, 107 insertions(+), 108 deletions(-) diff --git a/packages/viewer-sandbox/src/Sandbox.ts b/packages/viewer-sandbox/src/Sandbox.ts index f0deceae9..f49cd8ea2 100644 --- a/packages/viewer-sandbox/src/Sandbox.ts +++ b/packages/viewer-sandbox/src/Sandbox.ts @@ -16,7 +16,7 @@ export default class Sandbox { private viewsFolder!: FolderApi private streams: { [url: string]: Array } = {} private properties: PropertyInfo[] - private selectionList: SelectionEvent[] = null + private selectionList: SelectionEvent[] public static urlParams = { url: 'https://latest.speckle.dev/streams/c43ac05d04/commits/ec724cfbeb' @@ -34,16 +34,17 @@ export default class Sandbox { saoEnabled: true, saoParams: { saoBias: 0.15, - saoIntensity: 1.5, + saoIntensity: 1.25, saoScale: 434, - saoKernelRadius: 20, + saoKernelRadius: 10, saoMinResolution: 0, saoBlur: true, saoBlurRadius: 4, saoBlurStdDev: 4, saoBlurDepthCutoff: 0.0007 }, - saoScaleOffset: 0 + saoScaleOffset: 0, + saoNormalsRendering: 1 } public static lightParams: SunLightConfiguration = { @@ -188,7 +189,7 @@ export default class Sandbox { }) toggleSectionBox.on('click', () => { this.viewer.setSectionBoxFromObjects( - this.selectionList.map((val) => val.userData.id) as string[] + this.selectionList.map((val) => val.hits[0].object.id) as string[] ) this.viewer.toggleSectionBox() }) @@ -205,7 +206,7 @@ export default class Sandbox { }) zoomExtents.on('click', () => { this.viewer.zoom( - this.selectionList.map((val) => val.userData.id) as string[], + this.selectionList.map((val) => val.hits[0].object.id) as string[], undefined, true ) @@ -362,19 +363,31 @@ export default class Sandbox { this.viewer.requestRender() }) + // postFolder + // .addInput(Sandbox.postParams.saoParams, 'saoScale', { + // min: 0, + // max: 100 + // }) + // .on('change', () => { + // this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + // this.viewer.requestRender() + // }) postFolder - .addInput(Sandbox.postParams.saoParams, 'saoScale', { - min: 0, + .addInput(Sandbox.postParams, 'saoScaleOffset', { + min: -100, max: 100 }) .on('change', () => { this.viewer.getRenderer().pipelineOptions = Sandbox.postParams this.viewer.requestRender() }) + postFolder - .addInput(Sandbox.postParams, 'saoScaleOffset', { - min: -100, - max: 100 + .addInput(Sandbox.postParams, 'saoNormalsRendering', { + options: { + DEFAULT: 0, + ADVANCED: 1 + } }) .on('change', () => { this.viewer.getRenderer().pipelineOptions = Sandbox.postParams @@ -391,15 +404,15 @@ export default class Sandbox { this.viewer.requestRender() }) - postFolder - .addInput(Sandbox.postParams.saoParams, 'saoMinResolution', { - min: 0, - max: 1 - }) - .on('change', () => { - this.viewer.getRenderer().pipelineOptions = Sandbox.postParams - this.viewer.requestRender() - }) + // postFolder + // .addInput(Sandbox.postParams.saoParams, 'saoMinResolution', { + // min: 0, + // max: 1 + // }) + // .on('change', () => { + // this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + // this.viewer.requestRender() + // }) postFolder .addInput(Sandbox.postParams.saoParams, 'saoBlur', {}) @@ -409,31 +422,31 @@ export default class Sandbox { }) postFolder - .addInput(Sandbox.postParams.saoParams, 'saoBlurRadius', { min: 0, max: 100 }) + .addInput(Sandbox.postParams.saoParams, 'saoBlurRadius', { min: 0, max: 10 }) .on('change', () => { this.viewer.getRenderer().pipelineOptions = Sandbox.postParams this.viewer.requestRender() }) - postFolder - .addInput(Sandbox.postParams.saoParams, 'saoBlurStdDev', { - min: 0, - max: 150 - }) - .on('change', () => { - this.viewer.getRenderer().pipelineOptions = Sandbox.postParams - this.viewer.requestRender() - }) + // postFolder + // .addInput(Sandbox.postParams.saoParams, 'saoBlurStdDev', { + // min: 0, + // max: 150 + // }) + // .on('change', () => { + // this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + // this.viewer.requestRender() + // }) - postFolder - .addInput(Sandbox.postParams.saoParams, 'saoBlurDepthCutoff', { - min: 0, - max: 10 - }) - .on('change', () => { - this.viewer.getRenderer().pipelineOptions = Sandbox.postParams - this.viewer.requestRender() - }) + // postFolder + // .addInput(Sandbox.postParams.saoParams, 'saoBlurDepthCutoff', { + // min: 0, + // max: 10 + // }) + // .on('change', () => { + // this.viewer.getRenderer().pipelineOptions = Sandbox.postParams + // this.viewer.requestRender() + // }) const lightsFolder = this.tabs.pages[1].addFolder({ title: 'Lights', diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index 984346a66..067770572 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -82,7 +82,7 @@ sandbox.makeFilteringUI() await sandbox.loadUrl( // 'https://speckle.xyz/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://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' + 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' // 'Super' heavy revit shit // 'https://speckle.xyz/streams/e6f9156405/commits/0694d53bb5' // Same sample revit house, local to dim's computer @@ -108,5 +108,5 @@ await sandbox.loadUrl( // Alex's cube // 'https://latest.speckle.dev/streams/46e3e0e1ec/commits/a6392c19d6?c=%5B6.85874,2.9754,0.79022,0,0,0,0,1%5D' // Groups of groups - 'https://speckle.xyz/streams/1ce562e99a/commits/6fa28a5a0f' + // 'https://speckle.xyz/streams/1ce562e99a/commits/6fa28a5a0f' ) diff --git a/packages/viewer/src/modules/pipeline/Pipeline.ts b/packages/viewer/src/modules/pipeline/Pipeline.ts index 27758dfea..1c6bcdfbc 100644 --- a/packages/viewer/src/modules/pipeline/Pipeline.ts +++ b/packages/viewer/src/modules/pipeline/Pipeline.ts @@ -1,21 +1,22 @@ import { Camera, Plane, Scene, Vector2, WebGLRenderer } from 'three' import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js' import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js' -import { SAOPass, SAOPassParams } from 'three/examples/jsm/postprocessing/SAOPass.js' +import { SAOPassParams } from 'three/examples/jsm/postprocessing/SAOPass.js' import Batcher from '../batching/Batcher' import { ApplySAOPass } from './ApplySAOPass' -import { SpeckleSAOPass } from './SpeckleSAOPass' +import { NormalsType, SpeckleSAOPass } from './SpeckleSAOPass' export interface PipelineOptions { saoEnabled?: boolean saoParams?: Partial saoScaleOffset?: number + saoNormalsRendering?: NormalsType } export const DefaultPipelineOptions: PipelineOptions = { saoEnabled: true, saoParams: { - saoBias: 0, + saoBias: 0.15, saoIntensity: 1.25, saoScale: 434, saoKernelRadius: 10, @@ -25,7 +26,8 @@ export const DefaultPipelineOptions: PipelineOptions = { saoBlurStdDev: 4, saoBlurDepthCutoff: 0.0007 }, - saoScaleOffset: 0 + saoScaleOffset: 0, + saoNormalsRendering: NormalsType.IMPROVED } export class Pipeline { @@ -34,7 +36,7 @@ export class Pipeline { private _pipelineOptions: PipelineOptions = {} private composer: EffectComposer = null private renderPass: RenderPass = null - private saoPass: SAOPass = null + private saoPass: SpeckleSAOPass = null private applySaoPass: ApplySAOPass = null private drawingSize: Vector2 = new Vector2() @@ -44,6 +46,7 @@ export class Pipeline { this.applySaoPass.enabled = this._pipelineOptions.saoEnabled Object.assign(this.saoPass.params, this._pipelineOptions.saoParams) this.saoPass.params.saoScale += this._pipelineOptions.saoScaleOffset + this.saoPass.normalsRendering = this._pipelineOptions.saoNormalsRendering } } @@ -56,7 +59,13 @@ export class Pipeline { } public configure(scene: Scene, camera: Camera) { - this.saoPass = new SpeckleSAOPass(scene, camera, this._batcher, false, true) + this.saoPass = new SpeckleSAOPass( + scene, + camera, + this._batcher, + false, + NormalsType.IMPROVED + ) this.composer.addPass(this.saoPass) this.renderPass = new RenderPass(scene, camera) this.renderPass.renderToScreen = true diff --git a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts index 28f2b0b61..d543bbdcb 100644 --- a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts +++ b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts @@ -20,6 +20,11 @@ import Batcher from '../batching/Batcher' import SpeckleDepthMaterial from '../materials/SpeckleDepthMaterial' import SpeckleNormalMaterial from '../materials/SpeckleNormalMaterial' +export enum NormalsType { + DEFAULT = 0, + IMPROVED = 1 +} + /** * SAO implementation inspired from bhouston previous SAO work */ @@ -29,16 +34,26 @@ export class SpeckleSAOPass extends SAOPass { private prevStdDev private prevNumSamples private batcher: Batcher = null + private normalsType: NormalsType = NormalsType.IMPROVED + + public set normalsRendering(type: NormalsType) { + this.normalsType = type + this.saoMaterial.defines['NORMAL_TEXTURE'] = + this.normalsType === NormalsType.DEFAULT ? 1 : 0 + this.saoMaterial.defines['IMPROVED_NORMAL_RECONSTRUCTION'] = + this.normalsType === NormalsType.IMPROVED ? 1 : 0 + this.saoMaterial.needsUpdate = true + } constructor( scene: Scene, camera: Camera, batcher: Batcher, useDepthTexture = false, - useNormals = false, + normalsType: NormalsType, resolution = new Vector2(256, 256) ) { - super(scene, camera, useDepthTexture, useNormals, resolution) + super(scene, camera, useDepthTexture, true, resolution) this.batcher = batcher @@ -68,11 +83,11 @@ export class SpeckleSAOPass extends SAOPass { vertexShader: speckleSaoVert, uniforms: UniformsUtils.clone(SAOShader.uniforms) }) + this.normalsRendering = normalsType this.saoMaterial.extensions.derivatives = true this.saoMaterial.defines['DEPTH_PACKING'] = this.supportsDepthTextureExtension ? 0 : 1 - this.saoMaterial.defines['NORMAL_TEXTURE'] = this.supportsNormalTexture ? 1 : 0 this.saoMaterial.defines['PERSPECTIVE_CAMERA'] = (this.camera as PerspectiveCamera) .isPerspectiveCamera ? 1 @@ -90,14 +105,7 @@ export class SpeckleSAOPass extends SAOPass { this.saoMaterial.blending = NoBlending } - render(renderer, writeBuffer, readBuffer /*, deltaTime, maskActive*/) { - // Rendering readBuffer first when rendering to screen - // if (this.renderToScreen) { - // this.materialCopy.blending = NoBlending - // this.materialCopy.uniforms['tDiffuse'].value = readBuffer.texture - // this.materialCopy.needsUpdate = true - // this.renderPass(renderer, this.materialCopy, null) - // } + public render(renderer, writeBuffer, readBuffer) { writeBuffer readBuffer if (this.params.output === 1) { @@ -170,14 +178,8 @@ export class SpeckleSAOPass extends SAOPass { this.prevNumSamples = this.params.saoBlurRadius } - // Rendering scene to depth texture - // renderer.setClearColor(0x000000) - // renderer.setRenderTarget(this.beautyRenderTarget) - // renderer.clear() - // renderer.render(this.scene, this.camera) const restoreVisibility = this.batcher.saveVisiblity() const opaque = this.batcher.getOpaque() - // const transparent = this.batcher.getTransparent() this.batcher.applyVisibility(opaque) // Re-render scene if depth texture extension is not supported if (!this.supportsDepthTextureExtension) { @@ -191,15 +193,17 @@ export class SpeckleSAOPass extends SAOPass { ) } - if (this.supportsNormalTexture) { - // Clear rule : default normal is facing the camera - this.renderOverride( - renderer, - this.normalMaterial, - this.normalRenderTarget, - 0x7777ff, - 1.0 - ) + if (this.normalsType === NormalsType.DEFAULT) { + if (this.supportsNormalTexture) { + // Clear rule : default normal is facing the camera + this.renderOverride( + renderer, + this.normalMaterial, + this.normalRenderTarget, + 0x7777ff, + 1.0 + ) + } } this.batcher.applyVisibility(restoreVisibility) @@ -217,42 +221,9 @@ export class SpeckleSAOPass extends SAOPass { ) this.renderPass(renderer, this.hBlurMaterial, this.saoRenderTarget, 0xffffff, 1.0) } - - // let outputMaterial = this.materialCopy - // // Setting up SAO rendering - // if (this.params.output === 3) { - // if (this.supportsDepthTextureExtension) { - // this.materialCopy.uniforms['tDiffuse'].value = - // this.beautyRenderTarget.depthTexture - // this.materialCopy.needsUpdate = true - // } else { - // this.depthCopy.uniforms['tDiffuse'].value = this.depthRenderTarget.texture - // this.depthCopy.needsUpdate = true - // outputMaterial = this.depthCopy - // } - // } else if (this.params.output === 4) { - // this.materialCopy.uniforms['tDiffuse'].value = this.normalRenderTarget.texture - // this.materialCopy.needsUpdate = true - // } else { - // this.materialCopy.uniforms['tDiffuse'].value = this.saoRenderTarget.texture - // this.materialCopy.needsUpdate = true - // } - - // // Blending depends on output, only want a CustomBlending when showing SAO - // if (this.params.output === 0) { - // outputMaterial.blending = CustomBlending - // } else { - // outputMaterial.blending = NoBlending - // } - - // // Rendering SAOPass result on top of previous pass - // // this.renderPass(renderer, outputMaterial, this.renderToScreen ? null : readBuffer) - - // renderer.setClearColor(this._oldClearColor, this.oldClearAlpha) - // renderer.autoClear = oldAutoClear } - renderPass( + public renderPass( renderer, passMaterial, renderTarget, @@ -283,7 +254,13 @@ export class SpeckleSAOPass extends SAOPass { renderer.setClearAlpha(originalClearAlpha) } - renderOverride(renderer, overrideMaterial, renderTarget, clearColor, clearAlpha) { + public renderOverride( + renderer, + overrideMaterial, + renderTarget, + clearColor, + clearAlpha + ) { renderer.getClearColor(this.originalClearColor) const originalClearAlpha = renderer.getClearAlpha() const originalAutoClear = renderer.autoClear From d6795954fc3df2c5a67703db8594f07c5a4c0673 Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Fri, 30 Sep 2022 12:15:12 +0300 Subject: [PATCH 12/15] Fixed an issue where the camera far plane was incorrectly overriden --- packages/viewer-sandbox/src/Sandbox.ts | 5 +- packages/viewer-sandbox/src/main.ts | 4 +- .../viewer/src/modules/SpeckleRenderer.ts | 8 +- .../materials/shaders/speckle-sao-frag.ts | 117 ++++++++++++------ .../viewer/src/modules/pipeline/Pipeline.ts | 2 +- .../src/modules/pipeline/SpeckleSAOPass.ts | 5 +- 6 files changed, 97 insertions(+), 44 deletions(-) diff --git a/packages/viewer-sandbox/src/Sandbox.ts b/packages/viewer-sandbox/src/Sandbox.ts index f49cd8ea2..4d84de649 100644 --- a/packages/viewer-sandbox/src/Sandbox.ts +++ b/packages/viewer-sandbox/src/Sandbox.ts @@ -44,7 +44,7 @@ export default class Sandbox { saoBlurDepthCutoff: 0.0007 }, saoScaleOffset: 0, - saoNormalsRendering: 1 + saoNormalsRendering: 2 } public static lightParams: SunLightConfiguration = { @@ -386,7 +386,8 @@ export default class Sandbox { .addInput(Sandbox.postParams, 'saoNormalsRendering', { options: { DEFAULT: 0, - ADVANCED: 1 + ADVANCED: 1, + ACCURATE: 2 } }) .on('change', () => { diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index 067770572..2384b49f2 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -82,14 +82,14 @@ sandbox.makeFilteringUI() await sandbox.loadUrl( // 'https://speckle.xyz/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://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' + // 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' // 'Super' heavy revit shit // 'https://speckle.xyz/streams/e6f9156405/commits/0694d53bb5' // Same sample revit house, local to dim's computer // 'http://localhost:3000/streams/6960695d7b/commits/da0a2343fa' // 'http://100.66.180.109:3000/streams/6960695d7b/commits/417526751d' // IFC building (good for a tree based structure) - // 'https://latest.speckle.dev/streams/92b620fb17/commits/2ebd336223' + 'https://latest.speckle.dev/streams/92b620fb17/commits/2ebd336223' // IFC story, a subtree of the above // 'https://latest.speckle.dev/streams/92b620fb17/objects/8247bbc53865b0e0cb5ee4e252e66216' // Small scale lines diff --git a/packages/viewer/src/modules/SpeckleRenderer.ts b/packages/viewer/src/modules/SpeckleRenderer.ts index c22812ed0..fda25f9b0 100644 --- a/packages/viewer/src/modules/SpeckleRenderer.ts +++ b/packages/viewer/src/modules/SpeckleRenderer.ts @@ -265,8 +265,10 @@ export default class SpeckleRenderer { d = Math.max(camPos.distanceTo(v), d) v.set(box.max.x, box.max.y, box.max.z) // 111 d = Math.max(camPos.distanceTo(v), d) + this.viewer.cameraHandler.camera.far = d this.viewer.cameraHandler.activeCam.camera.far = d this.viewer.cameraHandler.activeCam.camera.updateProjectionMatrix() + this.viewer.cameraHandler.camera.updateProjectionMatrix() this.pipeline.pipelineOptions = { saoParams: { saoScale: d } } // console.log(d) } @@ -674,9 +676,9 @@ export default class SpeckleRenderer { this.viewer.cameraHandler.controls.minDistance = distance / 100 this.viewer.cameraHandler.controls.maxDistance = distance * 100 - this.viewer.cameraHandler.camera.near = distance / 100 - this.viewer.cameraHandler.camera.far = distance * 100 - this.viewer.cameraHandler.camera.updateProjectionMatrix() + // this.viewer.cameraHandler.camera.near = distance / 100 + // this.viewer.cameraHandler.camera.far = distance * 100 + // this.viewer.cameraHandler.camera.updateProjectionMatrix() if (this.viewer.cameraHandler.activeCam.name === 'ortho') { this.viewer.cameraHandler.orthoCamera.far = distance * 100 diff --git a/packages/viewer/src/modules/materials/shaders/speckle-sao-frag.ts b/packages/viewer/src/modules/materials/shaders/speckle-sao-frag.ts index cdb16d1b3..b7c283856 100644 --- a/packages/viewer/src/modules/materials/shaders/speckle-sao-frag.ts +++ b/packages/viewer/src/modules/materials/shaders/speckle-sao-frag.ts @@ -50,7 +50,7 @@ export const speckleSaoFrag = /* glsl */ ` } //https://wickedengine.net/2019/09/22/improved-normal-reconstruction-from-depth/ - vec3 depth_cross(in vec2 uv, in vec3 origin) + 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.); @@ -74,44 +74,91 @@ export const speckleSaoFrag = /* glsl */ ` sampleViewZ = getViewZ( sampleDepth ); highp vec3 right = getViewPosition( uv + ddx, sampleDepth, sampleViewZ ); - highp float dx0 = abs(right.z - center.z); - highp float dx1 = abs(left.z - center.z); - highp float dy0 = abs(bottom.z - center.z); - highp float dy1 = abs(top.z - center.z); - - int best_Z_horizontal = dx0 < dx1 ? 1 : 2; - int best_Z_vertical = dy0 < dy1 ? 3 : 4; + // 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; - vec3 P1, P2; - if (best_Z_horizontal == 1 && best_Z_vertical == 4) - { - P1 = right; - P2 = top; - } - else if (best_Z_horizontal == 1 && best_Z_vertical == 3) - { - P1 = bottom; - P2 = right; - } - else if (best_Z_horizontal == 2 && best_Z_vertical == 4) - { - P1 = top; - P2 = left; - } - else if (best_Z_horizontal == 2 && best_Z_vertical == 3) - { - P1 = left; - P2 = bottom; - } + // 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; - return normalize(cross(P2 - center, P1 - center)); + // get view space normal from the cross product of the two smallest offsets + vec3 viewNormal = normalize(cross(hDeriv, vDeriv)); + + return viewNormal; } - vec3 getViewNormal( const in vec3 viewPosition, const in vec2 screenPosition ) { + 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 = getDepth( uv - ddy ); + float sampleViewZ = getViewZ( sampleDepth ); + highp vec3 top = getViewPosition( uv - ddy, sampleDepth, sampleViewZ ); + + sampleDepth = getDepth( uv + ddy ); + sampleViewZ = getViewZ( sampleDepth ); + highp vec3 bottom = getViewPosition( uv + ddy, sampleDepth, sampleViewZ ); + + highp vec3 center = origin; + + sampleDepth = getDepth( uv - ddx ); + sampleViewZ = getViewZ( sampleDepth ); + highp vec3 left = getViewPosition( uv - ddx, sampleDepth, sampleViewZ ); + + sampleDepth = getDepth( 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( + getDepth(uv - ddx), + getDepth(uv + ddx), + getDepth(uv - 2. * ddx), + getDepth(uv + 2. * ddx) + ); + + // get depth values at 1 & 2 pixels offsets from current along the vertical axis + vec4 V = vec4( + getDepth(uv - ddy), + getDepth(uv + ddy), + getDepth(uv - 2. * ddy), + getDepth(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 NORMAL_TEXTURE == 1 return unpackRGBToNormal( texture2D( tNormal, screenPosition ).xyz ); #elif IMPROVED_NORMAL_RECONSTRUCTION == 1 - return depth_cross(screenPosition, viewPosition); + return viewNormalImproved(screenPosition, viewPosition); + #elif ACCURATE_NORMAL_RECONSTRUCTION == 1 + return viewNormalAccurate(screenPosition, viewPosition, centerDepth); #else return normalize( cross( dFdx( viewPosition ), dFdy( viewPosition ) ) ); #endif @@ -128,11 +175,11 @@ export const speckleSaoFrag = /* glsl */ ` // moving costly divides into consts const float ANGLE_STEP = PI2 * float( NUM_RINGS ) / float( NUM_SAMPLES ); const float INV_NUM_SAMPLES = 1.0 / float( NUM_SAMPLES ); - float getAmbientOcclusion( const in vec3 centerViewPosition ) { + float getAmbientOcclusion( const in vec3 centerViewPosition, in float centerDepth ) { // precompute some variables require in getOcclusion. scaleDividedByCameraFar = scale / cameraFar; minResolutionMultipliedByCameraFar = minResolution * cameraFar; - vec3 centerViewNormal = getViewNormal( centerViewPosition, vUv ); + vec3 centerViewNormal = getViewNormal( centerViewPosition, vUv, centerDepth ); // jsfiddle that shows sample pattern: https://jsfiddle.net/a16ff1p7/ float angle = rand( vUv + randomSeed ) * PI2; vec2 radius = vec2( kernelRadius * INV_NUM_SAMPLES ) / size; @@ -162,7 +209,7 @@ export const speckleSaoFrag = /* glsl */ ` } float centerViewZ = getViewZ( centerDepth ); vec3 viewPosition = getViewPosition( vUv, centerDepth, centerViewZ ); - float ambientOcclusion = getAmbientOcclusion( viewPosition ); + float ambientOcclusion = getAmbientOcclusion( viewPosition, centerDepth ); gl_FragColor = getDefaultColor( vUv ); gl_FragColor.xyz *= 1.0 - ambientOcclusion; // gl_FragColor.xyz = depth_cross(vUv, viewPosition) * 0.5 + 0.5; diff --git a/packages/viewer/src/modules/pipeline/Pipeline.ts b/packages/viewer/src/modules/pipeline/Pipeline.ts index 1c6bcdfbc..e6991a1b4 100644 --- a/packages/viewer/src/modules/pipeline/Pipeline.ts +++ b/packages/viewer/src/modules/pipeline/Pipeline.ts @@ -27,7 +27,7 @@ export const DefaultPipelineOptions: PipelineOptions = { saoBlurDepthCutoff: 0.0007 }, saoScaleOffset: 0, - saoNormalsRendering: NormalsType.IMPROVED + saoNormalsRendering: NormalsType.ACCURATE } export class Pipeline { diff --git a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts index d543bbdcb..4158efec9 100644 --- a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts +++ b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts @@ -22,7 +22,8 @@ import SpeckleNormalMaterial from '../materials/SpeckleNormalMaterial' export enum NormalsType { DEFAULT = 0, - IMPROVED = 1 + IMPROVED = 1, + ACCURATE = 2 } /** @@ -42,6 +43,8 @@ export class SpeckleSAOPass extends SAOPass { this.normalsType === NormalsType.DEFAULT ? 1 : 0 this.saoMaterial.defines['IMPROVED_NORMAL_RECONSTRUCTION'] = this.normalsType === NormalsType.IMPROVED ? 1 : 0 + this.saoMaterial.defines['ACCURATE_NORMAL_RECONSTRUCTION'] = + this.normalsType === NormalsType.ACCURATE ? 1 : 0 this.saoMaterial.needsUpdate = true } From 1f2fb5677f16a776d0469509b7534196f3d903a4 Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Fri, 30 Sep 2022 12:26:02 +0300 Subject: [PATCH 13/15] Fixed an issue where the IBL probe intensity was not being set to 0 when requeted --- packages/viewer/src/modules/SpeckleRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/viewer/src/modules/SpeckleRenderer.ts b/packages/viewer/src/modules/SpeckleRenderer.ts index fda25f9b0..2474ab464 100644 --- a/packages/viewer/src/modules/SpeckleRenderer.ts +++ b/packages/viewer/src/modules/SpeckleRenderer.ts @@ -473,7 +473,7 @@ export default class SpeckleRenderer { public setSunLightConfiguration(config: SunLightConfiguration) { Object.assign(this.sunConfiguration, config) - if (config.indirectLightIntensity) { + if (config.indirectLightIntensity !== undefined) { this.indirectIBLIntensity = config.indirectLightIntensity } this.updateDirectLights() From f96e8c2dd21c8d19105400da94b175897aa87c78 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 3 Oct 2022 04:46:27 -0700 Subject: [PATCH 14/15] Fixed the SAO artifacts on Chromium on MacOS #1065 --- packages/viewer-sandbox/src/Sandbox.ts | 4 ++-- packages/viewer/src/modules/SpeckleRenderer.ts | 18 +++++++++--------- .../src/modules/pipeline/SpeckleSAOPass.ts | 13 +++++++++++-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/packages/viewer-sandbox/src/Sandbox.ts b/packages/viewer-sandbox/src/Sandbox.ts index 4d84de649..d8ef56310 100644 --- a/packages/viewer-sandbox/src/Sandbox.ts +++ b/packages/viewer-sandbox/src/Sandbox.ts @@ -135,7 +135,7 @@ export default class Sandbox { private removeStreamControls(url: string) { this.viewer.unloadObject(url) - ;(this.streams[url][0] as { dispose: () => void }).dispose() + ; (this.streams[url][0] as { dispose: () => void }).dispose() delete this.streams[url] } @@ -228,7 +228,7 @@ export default class Sandbox { }) const dark = localStorage.getItem('dark') === 'dark' if (dark) { - document.getElementById('renderer')?.classList.toggle('background-dark') + document.getElementById('renderer') ?.classList.toggle('background-dark') } darkModeToggle.on('click', () => { diff --git a/packages/viewer/src/modules/SpeckleRenderer.ts b/packages/viewer/src/modules/SpeckleRenderer.ts index 2474ab464..9a2de3f65 100644 --- a/packages/viewer/src/modules/SpeckleRenderer.ts +++ b/packages/viewer/src/modules/SpeckleRenderer.ts @@ -481,12 +481,12 @@ export default class SpeckleRenderer { public updateHelpers() { if (this.SHOW_HELPERS) { - ;(this.scene.getObjectByName('CamHelper') as CameraHelper).update() - // Thank you prettier, this looks so much better - ;(this.scene.getObjectByName('SceneBoxHelper') as Box3Helper).box.copy( - this.sceneBox - ) - ;(this.scene.getObjectByName('DirLightHelper') as DirectionalLightHelper).update() + ; (this.scene.getObjectByName('CamHelper') as CameraHelper).update() + // Thank you prettier, this looks so much better + ; (this.scene.getObjectByName('SceneBoxHelper') as Box3Helper).box.copy( + this.sceneBox + ) + ; (this.scene.getObjectByName('DirLightHelper') as DirectionalLightHelper).update() } } @@ -676,9 +676,9 @@ export default class SpeckleRenderer { this.viewer.cameraHandler.controls.minDistance = distance / 100 this.viewer.cameraHandler.controls.maxDistance = distance * 100 - // this.viewer.cameraHandler.camera.near = distance / 100 - // this.viewer.cameraHandler.camera.far = distance * 100 - // this.viewer.cameraHandler.camera.updateProjectionMatrix() + this.viewer.cameraHandler.camera.near = Math.max(distance / 100, 0.1) + this.viewer.cameraHandler.camera.far = distance * 100 + this.viewer.cameraHandler.camera.updateProjectionMatrix() if (this.viewer.cameraHandler.activeCam.name === 'ortho') { this.viewer.cameraHandler.orthoCamera.far = distance * 100 diff --git a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts index 4158efec9..3a0b3d29c 100644 --- a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts +++ b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts @@ -60,6 +60,15 @@ export class SpeckleSAOPass extends SAOPass { this.batcher = batcher + /** On Chromium, on MacOS the 16 bit depth render buffer appears broken. + * We're not really using a stencil buffer at all, we're just forcing + * three.js to use a 24 bit depth render buffer + */ + this.depthRenderTarget.depthBuffer = true + this.depthRenderTarget.stencilBuffer = true + this.normalRenderTarget.depthBuffer = true + this.normalRenderTarget.stencilBuffer = true + this.depthMaterial = new SpeckleDepthMaterial( { depthPacking: RGBADepthPacking @@ -248,8 +257,8 @@ export class SpeckleSAOPass extends SAOPass { renderer.clear() } - ;(this.fsQuad as FullScreenQuad).material = passMaterial - ;(this.fsQuad as FullScreenQuad).render(renderer) + ; (this.fsQuad as FullScreenQuad).material = passMaterial + ; (this.fsQuad as FullScreenQuad).render(renderer) // restore original state renderer.autoClear = originalAutoClear From ca5fce8c1403bb0ee19e94a2d4faf02fc336c60c Mon Sep 17 00:00:00 2001 From: Dimitrie Stefanescu Date: Mon, 3 Oct 2022 15:52:13 +0100 Subject: [PATCH 15/15] chore(viewer): linting --- packages/viewer-sandbox/src/Sandbox.ts | 4 ++-- packages/viewer/src/modules/SpeckleRenderer.ts | 12 ++++++------ .../viewer/src/modules/pipeline/SpeckleSAOPass.ts | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/viewer-sandbox/src/Sandbox.ts b/packages/viewer-sandbox/src/Sandbox.ts index d8ef56310..4d84de649 100644 --- a/packages/viewer-sandbox/src/Sandbox.ts +++ b/packages/viewer-sandbox/src/Sandbox.ts @@ -135,7 +135,7 @@ export default class Sandbox { private removeStreamControls(url: string) { this.viewer.unloadObject(url) - ; (this.streams[url][0] as { dispose: () => void }).dispose() + ;(this.streams[url][0] as { dispose: () => void }).dispose() delete this.streams[url] } @@ -228,7 +228,7 @@ export default class Sandbox { }) const dark = localStorage.getItem('dark') === 'dark' if (dark) { - document.getElementById('renderer') ?.classList.toggle('background-dark') + document.getElementById('renderer')?.classList.toggle('background-dark') } darkModeToggle.on('click', () => { diff --git a/packages/viewer/src/modules/SpeckleRenderer.ts b/packages/viewer/src/modules/SpeckleRenderer.ts index 9a2de3f65..7b1d581e8 100644 --- a/packages/viewer/src/modules/SpeckleRenderer.ts +++ b/packages/viewer/src/modules/SpeckleRenderer.ts @@ -481,12 +481,12 @@ export default class SpeckleRenderer { public updateHelpers() { if (this.SHOW_HELPERS) { - ; (this.scene.getObjectByName('CamHelper') as CameraHelper).update() - // Thank you prettier, this looks so much better - ; (this.scene.getObjectByName('SceneBoxHelper') as Box3Helper).box.copy( - this.sceneBox - ) - ; (this.scene.getObjectByName('DirLightHelper') as DirectionalLightHelper).update() + ;(this.scene.getObjectByName('CamHelper') as CameraHelper).update() + // Thank you prettier, this looks so much better + ;(this.scene.getObjectByName('SceneBoxHelper') as Box3Helper).box.copy( + this.sceneBox + ) + ;(this.scene.getObjectByName('DirLightHelper') as DirectionalLightHelper).update() } } diff --git a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts index 3a0b3d29c..94866ecc0 100644 --- a/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts +++ b/packages/viewer/src/modules/pipeline/SpeckleSAOPass.ts @@ -257,8 +257,8 @@ export class SpeckleSAOPass extends SAOPass { renderer.clear() } - ; (this.fsQuad as FullScreenQuad).material = passMaterial - ; (this.fsQuad as FullScreenQuad).render(renderer) + ;(this.fsQuad as FullScreenQuad).material = passMaterial + ;(this.fsQuad as FullScreenQuad).render(renderer) // restore original state renderer.autoClear = originalAutoClear