@@ -16,7 +16,7 @@ export default class Sandbox {
|
||||
private viewsFolder!: FolderApi
|
||||
private streams: { [url: string]: Array<unknown> } = {}
|
||||
private properties: PropertyInfo[]
|
||||
private selectionList: SelectionEvent[] = null
|
||||
private selectionList: SelectionEvent[]
|
||||
|
||||
public static urlParams = {
|
||||
url: 'https://latest.speckle.dev/streams/c43ac05d04/commits/ec724cfbeb'
|
||||
@@ -30,6 +30,23 @@ export default class Sandbox {
|
||||
tonemapping: 4 //'ACESFilmicToneMapping'
|
||||
}
|
||||
|
||||
public static postParams = {
|
||||
saoEnabled: true,
|
||||
saoParams: {
|
||||
saoBias: 0.15,
|
||||
saoIntensity: 1.25,
|
||||
saoScale: 434,
|
||||
saoKernelRadius: 10,
|
||||
saoMinResolution: 0,
|
||||
saoBlur: true,
|
||||
saoBlurRadius: 4,
|
||||
saoBlurStdDev: 4,
|
||||
saoBlurDepthCutoff: 0.0007
|
||||
},
|
||||
saoScaleOffset: 0,
|
||||
saoNormalsRendering: 2
|
||||
}
|
||||
|
||||
public static lightParams: SunLightConfiguration = {
|
||||
enabled: true,
|
||||
castShadow: true,
|
||||
@@ -38,7 +55,7 @@ export default class Sandbox {
|
||||
elevation: 1.33,
|
||||
azimuth: 0.75,
|
||||
radius: 0,
|
||||
indirectLightIntensity: 1.85
|
||||
indirectLightIntensity: 1.2
|
||||
}
|
||||
|
||||
public static filterParams = {
|
||||
@@ -172,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()
|
||||
})
|
||||
@@ -189,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
|
||||
)
|
||||
@@ -321,6 +338,116 @@ 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,
|
||||
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: 100
|
||||
// })
|
||||
// .on('change', () => {
|
||||
// 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, 'saoNormalsRendering', {
|
||||
options: {
|
||||
DEFAULT: 0,
|
||||
ADVANCED: 1,
|
||||
ACCURATE: 2
|
||||
}
|
||||
})
|
||||
.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: 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, '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',
|
||||
|
||||
@@ -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
|
||||
@@ -99,4 +99,14 @@ 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'
|
||||
//Car
|
||||
// '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'
|
||||
// Groups of groups
|
||||
// 'https://speckle.xyz/streams/1ce562e99a/commits/6fa28a5a0f'
|
||||
)
|
||||
|
||||
@@ -45,6 +45,7 @@ import {
|
||||
SunLightConfiguration,
|
||||
ViewerEvent
|
||||
} from '../IViewer'
|
||||
import { DefaultPipelineOptions, Pipeline, PipelineOptions } from './pipeline/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,10 @@ export default class SpeckleRenderer {
|
||||
this._renderer.setSize(container.offsetWidth, container.offsetHeight)
|
||||
container.appendChild(this._renderer.domElement)
|
||||
|
||||
this.pipeline = new Pipeline(this._renderer, this.batcher)
|
||||
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))
|
||||
this.input.on('object-clicked-debug', this.onObjectClickDebug.bind(this))
|
||||
@@ -232,11 +242,46 @@ 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.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)
|
||||
}
|
||||
|
||||
public render(camera: Camera) {
|
||||
this.batcher.render(this.renderer)
|
||||
this.renderer.render(this.scene, camera)
|
||||
this.pipeline.render(this.scene, camera)
|
||||
// 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) {
|
||||
@@ -331,6 +376,7 @@ export default class SpeckleRenderer {
|
||||
}
|
||||
}
|
||||
})
|
||||
this.pipeline.updateClippingPlanes(planes)
|
||||
this.renderer.shadowMap.needsUpdate = true
|
||||
}
|
||||
|
||||
@@ -427,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()
|
||||
@@ -515,7 +561,6 @@ export default class SpeckleRenderer {
|
||||
}
|
||||
})
|
||||
} as SelectionEvent
|
||||
|
||||
this.viewer.emit(ViewerEvent.ObjectClicked, selectionInfo)
|
||||
}
|
||||
|
||||
@@ -631,7 +676,7 @@ 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.near = Math.max(distance / 100, 0.1)
|
||||
this.viewer.cameraHandler.camera.far = distance * 100
|
||||
this.viewer.cameraHandler.camera.updateProjectionMatrix()
|
||||
|
||||
|
||||
@@ -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', () => {
|
||||
@@ -134,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
|
||||
}
|
||||
|
||||
@@ -157,7 +155,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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,114 @@ export default class Batcher {
|
||||
}
|
||||
}
|
||||
|
||||
public saveVisiblity(): Record<string, BatchUpdateRange> {
|
||||
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<string, BatchUpdateRange>) {
|
||||
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<string, BatchUpdateRange> {
|
||||
const visibilityRanges = {}
|
||||
for (const k in this.batches) {
|
||||
const batch: Batch = this.batches[k]
|
||||
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)
|
||||
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) {
|
||||
visibilityRanges[k] = HideAllBatchUpdateRange
|
||||
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 +243,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 +275,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 +342,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)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -15,7 +15,13 @@ 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,
|
||||
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
|
||||
@@ -78,6 +102,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[]
|
||||
|
||||
@@ -118,6 +147,7 @@ export default class LineBatch implements Batch {
|
||||
material: this.batchMaterial
|
||||
})
|
||||
this.mesh.material = this.batchMaterial
|
||||
this.mesh.visible = true
|
||||
}
|
||||
|
||||
public buildBatch() {
|
||||
|
||||
@@ -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)))]
|
||||
@@ -195,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
|
||||
})
|
||||
@@ -207,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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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 <packing>
|
||||
#include <uv_pars_fragment>
|
||||
#include <normal_pars_fragment>
|
||||
#include <bumpmap_pars_fragment>
|
||||
#include <normalmap_pars_fragment>
|
||||
#include <logdepthbuf_pars_fragment>
|
||||
#include <clipping_planes_pars_fragment>
|
||||
void main() {
|
||||
#include <clipping_planes_fragment>
|
||||
#include <logdepthbuf_fragment>
|
||||
#include <normal_fragment_begin>
|
||||
#include <normal_fragment_maps>
|
||||
gl_FragColor = vec4( packNormalToRGB( normal ), opacity );
|
||||
#ifdef OPAQUE
|
||||
gl_FragColor.a = 1.0;
|
||||
#endif
|
||||
}
|
||||
`
|
||||
@@ -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 <common>
|
||||
#include <uv_pars_vertex>
|
||||
#include <displacementmap_pars_vertex>
|
||||
#include <normal_pars_vertex>
|
||||
#include <morphtarget_pars_vertex>
|
||||
#include <skinning_pars_vertex>
|
||||
#include <logdepthbuf_pars_vertex>
|
||||
#include <clipping_planes_pars_vertex>
|
||||
|
||||
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 <uv_vertex>
|
||||
#include <beginnormal_vertex>
|
||||
#include <morphnormal_vertex>
|
||||
#include <skinbase_vertex>
|
||||
#include <skinnormal_vertex>
|
||||
#include <defaultnormal_vertex>
|
||||
#include <normal_vertex>
|
||||
#include <begin_vertex>
|
||||
#include <morphtarget_vertex>
|
||||
#include <skinning_vertex>
|
||||
#include <displacementmap_vertex>
|
||||
//#include <project_vertex> // 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 <logdepthbuf_vertex>
|
||||
#include <clipping_planes_vertex>
|
||||
#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )
|
||||
vViewPosition = - mvPosition.xyz;
|
||||
#endif
|
||||
}
|
||||
`
|
||||
@@ -0,0 +1,216 @@
|
||||
export const speckleSaoFrag = /* glsl */ `
|
||||
#include <common>
|
||||
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 <packing>
|
||||
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;
|
||||
}
|
||||
|
||||
//https://wickedengine.net/2019/09/22/improved-normal-reconstruction-from-depth/
|
||||
vec3 viewNormalImproved(in vec2 uv, in vec3 origin)
|
||||
{
|
||||
highp vec2 dd = abs(vec2(1./size.x, 1./size.y));
|
||||
highp vec2 ddx = vec2(dd.x, 0.);
|
||||
highp vec2 ddy = vec2(0., dd.y);
|
||||
|
||||
float sampleDepth = 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;
|
||||
|
||||
// pick horizontal and vertical diff with the smallest z difference
|
||||
vec3 hDeriv = abs(l.z) < abs(r.z) ? l : r;
|
||||
vec3 vDeriv = abs(d.z) < abs(u.z) ? d : u;
|
||||
|
||||
// get view space normal from the cross product of the two smallest offsets
|
||||
vec3 viewNormal = normalize(cross(hDeriv, vDeriv));
|
||||
|
||||
return viewNormal;
|
||||
}
|
||||
|
||||
vec3 viewNormalAccurate(in vec2 uv, in vec3 origin, in float centerDepth) {
|
||||
highp vec2 dd = abs(vec2(1./size.x, 1./size.y));
|
||||
highp vec2 ddx = vec2(dd.x, 0.);
|
||||
highp vec2 ddy = vec2(0., dd.y);
|
||||
|
||||
float sampleDepth = 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 viewNormalImproved(screenPosition, viewPosition);
|
||||
#elif ACCURATE_NORMAL_RECONSTRUCTION == 1
|
||||
return viewNormalAccurate(screenPosition, viewPosition, centerDepth);
|
||||
#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, in float centerDepth ) {
|
||||
// precompute some variables require in getOcclusion.
|
||||
scaleDividedByCameraFar = scale / cameraFar;
|
||||
minResolutionMultipliedByCameraFar = minResolution * cameraFar;
|
||||
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;
|
||||
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, centerDepth );
|
||||
gl_FragColor = getDefaultColor( vUv );
|
||||
gl_FragColor.xyz *= 1.0 - ambientOcclusion;
|
||||
// gl_FragColor.xyz = depth_cross(vUv, viewPosition) * 0.5 + 0.5;
|
||||
}`
|
||||
@@ -0,0 +1,6 @@
|
||||
export const speckleSaoVert = /* glsl */ `
|
||||
varying vec2 vUv;
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
}`
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
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 { SAOPassParams } from 'three/examples/jsm/postprocessing/SAOPass.js'
|
||||
import Batcher from '../batching/Batcher'
|
||||
import { ApplySAOPass } from './ApplySAOPass'
|
||||
import { NormalsType, SpeckleSAOPass } from './SpeckleSAOPass'
|
||||
|
||||
export interface PipelineOptions {
|
||||
saoEnabled?: boolean
|
||||
saoParams?: Partial<SAOPassParams>
|
||||
saoScaleOffset?: number
|
||||
saoNormalsRendering?: NormalsType
|
||||
}
|
||||
|
||||
export const DefaultPipelineOptions: PipelineOptions = {
|
||||
saoEnabled: true,
|
||||
saoParams: {
|
||||
saoBias: 0.15,
|
||||
saoIntensity: 1.25,
|
||||
saoScale: 434,
|
||||
saoKernelRadius: 10,
|
||||
saoMinResolution: 0,
|
||||
saoBlur: true,
|
||||
saoBlurRadius: 4,
|
||||
saoBlurStdDev: 4,
|
||||
saoBlurDepthCutoff: 0.0007
|
||||
},
|
||||
saoScaleOffset: 0,
|
||||
saoNormalsRendering: NormalsType.ACCURATE
|
||||
}
|
||||
|
||||
export class Pipeline {
|
||||
private _renderer: WebGLRenderer = null
|
||||
private _batcher: Batcher = null
|
||||
private _pipelineOptions: PipelineOptions = {}
|
||||
private composer: EffectComposer = null
|
||||
private renderPass: RenderPass = null
|
||||
private saoPass: SpeckleSAOPass = null
|
||||
private applySaoPass: ApplySAOPass = null
|
||||
private drawingSize: Vector2 = new Vector2()
|
||||
|
||||
public set pipelineOptions(options: PipelineOptions) {
|
||||
Object.assign(this._pipelineOptions, options)
|
||||
if (this.saoPass) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
this._batcher,
|
||||
false,
|
||||
NormalsType.IMPROVED
|
||||
)
|
||||
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 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
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
import {
|
||||
Camera,
|
||||
DoubleSide,
|
||||
NoBlending,
|
||||
OrthographicCamera,
|
||||
PerspectiveCamera,
|
||||
RGBADepthPacking,
|
||||
Scene,
|
||||
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'
|
||||
import Batcher from '../batching/Batcher'
|
||||
import SpeckleDepthMaterial from '../materials/SpeckleDepthMaterial'
|
||||
import SpeckleNormalMaterial from '../materials/SpeckleNormalMaterial'
|
||||
|
||||
export enum NormalsType {
|
||||
DEFAULT = 0,
|
||||
IMPROVED = 1,
|
||||
ACCURATE = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* SAO implementation inspired from bhouston previous SAO work
|
||||
*/
|
||||
|
||||
export class SpeckleSAOPass extends SAOPass {
|
||||
private _oldClearColor
|
||||
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.defines['ACCURATE_NORMAL_RECONSTRUCTION'] =
|
||||
this.normalsType === NormalsType.ACCURATE ? 1 : 0
|
||||
this.saoMaterial.needsUpdate = true
|
||||
}
|
||||
|
||||
constructor(
|
||||
scene: Scene,
|
||||
camera: Camera,
|
||||
batcher: Batcher,
|
||||
useDepthTexture = false,
|
||||
normalsType: NormalsType,
|
||||
resolution = new Vector2(256, 256)
|
||||
) {
|
||||
super(scene, camera, useDepthTexture, true, resolution)
|
||||
|
||||
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
|
||||
},
|
||||
['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: {
|
||||
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.normalsRendering = normalsType
|
||||
this.saoMaterial.extensions.derivatives = true
|
||||
this.saoMaterial.defines['DEPTH_PACKING'] = this.supportsDepthTextureExtension
|
||||
? 0
|
||||
: 1
|
||||
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
|
||||
}
|
||||
|
||||
public render(renderer, writeBuffer, readBuffer) {
|
||||
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['cameraInverseProjectionMatrix'].value.copy(
|
||||
this.camera.projectionMatrixInverse
|
||||
)
|
||||
this.saoMaterial.uniforms['cameraProjectionMatrix'].value =
|
||||
this.camera.projectionMatrix
|
||||
// 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
|
||||
}
|
||||
|
||||
const restoreVisibility = this.batcher.saveVisiblity()
|
||||
const opaque = this.batcher.getOpaque()
|
||||
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
|
||||
this.renderOverride(
|
||||
renderer,
|
||||
this.depthMaterial,
|
||||
this.depthRenderTarget,
|
||||
0x000000,
|
||||
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)
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
public 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)
|
||||
}
|
||||
|
||||
public 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user