[3d] performance improvement by using threejs setViewport (#4079)

This commit is contained in:
Terry Jia
2025-06-06 20:35:16 -04:00
committed by GitHub
parent 65289b1927
commit 1ca71caf45
4 changed files with 145 additions and 126 deletions

View File

@@ -160,22 +160,48 @@ class Load3d {
this.viewHelperManager.update(delta) this.viewHelperManager.update(delta)
this.controlsManager.update() this.controlsManager.update()
this.renderMainScene()
if (this.previewManager.showPreview) {
this.renderPreviewScene()
}
this.resetViewport()
if (this.viewHelperManager.viewHelper.render) {
this.viewHelperManager.viewHelper.render(this.renderer)
}
this.INITIAL_RENDER_DONE = true
}
renderMainScene(): void {
const width = this.renderer.domElement.clientWidth
const height = this.renderer.domElement.clientHeight
this.renderer.setViewport(0, 0, width, height)
this.renderer.setScissor(0, 0, width, height)
this.renderer.setScissorTest(true)
this.renderer.clear() this.renderer.clear()
this.sceneManager.renderBackground() this.sceneManager.renderBackground()
this.renderer.render( this.renderer.render(
this.sceneManager.scene, this.sceneManager.scene,
this.cameraManager.activeCamera this.cameraManager.activeCamera
) )
}
if (this.viewHelperManager.viewHelper.render) { renderPreviewScene(): void {
this.viewHelperManager.viewHelper.render(this.renderer) this.previewManager.renderPreview()
} }
if (this.previewManager.showPreview) { resetViewport(): void {
this.previewManager.updatePreviewRender() const width = this.renderer.domElement.clientWidth
} const height = this.renderer.domElement.clientHeight
this.INITIAL_RENDER_DONE = true this.renderer.setViewport(0, 0, width, height)
this.renderer.setScissor(0, 0, width, height)
this.renderer.setScissorTest(false)
} }
private getActiveCamera(): THREE.Camera { private getActiveCamera(): THREE.Camera {
@@ -198,20 +224,17 @@ class Load3d {
return return
} }
if (this.previewManager.showPreview) {
this.previewManager.updatePreviewRender()
}
const delta = this.clock.getDelta() const delta = this.clock.getDelta()
this.viewHelperManager.update(delta) this.viewHelperManager.update(delta)
this.controlsManager.update() this.controlsManager.update()
this.renderer.clear() this.renderMainScene()
this.sceneManager.renderBackground()
this.renderer.render( if (this.previewManager.showPreview) {
this.sceneManager.scene, this.renderPreviewScene()
this.cameraManager.activeCamera }
)
this.resetViewport()
if (this.viewHelperManager.viewHelper.render) { if (this.viewHelperManager.viewHelper.render) {
this.viewHelperManager.viewHelper.render(this.renderer) this.viewHelperManager.viewHelper.render(this.renderer)
@@ -304,11 +327,9 @@ class Load3d {
async setBackgroundImage(uploadPath: string): Promise<void> { async setBackgroundImage(uploadPath: string): Promise<void> {
await this.sceneManager.setBackgroundImage(uploadPath) await this.sceneManager.setBackgroundImage(uploadPath)
if (this.previewManager.previewRenderer) { this.previewManager.updateBackgroundTexture(
this.previewManager.updateBackgroundTexture( this.sceneManager.backgroundTexture
this.sceneManager.backgroundTexture )
)
}
this.forceRender() this.forceRender()
} }
@@ -316,10 +337,7 @@ class Load3d {
removeBackgroundImage(): void { removeBackgroundImage(): void {
this.sceneManager.removeBackgroundImage() this.sceneManager.removeBackgroundImage()
if ( if (this.previewManager.previewCamera) {
this.previewManager.previewRenderer &&
this.previewManager.previewCamera
) {
this.previewManager.updateBackgroundTexture(null) this.previewManager.updateBackgroundTexture(null)
} }

View File

@@ -42,10 +42,6 @@ class Load3dAnimation extends Load3d {
return return
} }
if (this.previewManager.showPreview) {
this.previewManager.updatePreviewRender()
}
const delta = this.clock.getDelta() const delta = this.clock.getDelta()
this.animationManager.update(delta) this.animationManager.update(delta)
@@ -54,12 +50,13 @@ class Load3dAnimation extends Load3d {
this.controlsManager.update() this.controlsManager.update()
this.renderer.clear() this.renderMainScene()
this.sceneManager.renderBackground()
this.renderer.render( if (this.previewManager.showPreview) {
this.sceneManager.scene, this.renderPreviewScene()
this.cameraManager.activeCamera }
)
this.resetViewport()
if (this.viewHelperManager.viewHelper.render) { if (this.viewHelperManager.viewHelper.render) {
this.viewHelperManager.viewHelper.render(this.renderer) this.viewHelperManager.viewHelper.render(this.renderer)

View File

@@ -4,7 +4,6 @@ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { EventManagerInterface, PreviewManagerInterface } from './interfaces' import { EventManagerInterface, PreviewManagerInterface } from './interfaces'
export class PreviewManager implements PreviewManagerInterface { export class PreviewManager implements PreviewManagerInterface {
previewRenderer: THREE.WebGLRenderer | null = null
previewCamera: THREE.Camera previewCamera: THREE.Camera
previewContainer: HTMLDivElement = {} as HTMLDivElement previewContainer: HTMLDivElement = {} as HTMLDivElement
showPreview: boolean = true showPreview: boolean = true
@@ -17,7 +16,6 @@ export class PreviewManager implements PreviewManagerInterface {
private getControls: () => OrbitControls private getControls: () => OrbitControls
private eventManager: EventManagerInterface private eventManager: EventManagerInterface
// @ts-expect-error unused variable
private getRenderer: () => THREE.WebGLRenderer private getRenderer: () => THREE.WebGLRenderer
private previewBackgroundScene: THREE.Scene private previewBackgroundScene: THREE.Scene
@@ -25,6 +23,8 @@ export class PreviewManager implements PreviewManagerInterface {
private previewBackgroundMesh: THREE.Mesh | null = null private previewBackgroundMesh: THREE.Mesh | null = null
private previewBackgroundTexture: THREE.Texture | null = null private previewBackgroundTexture: THREE.Texture | null = null
private previewBackgroundColor: THREE.Color = new THREE.Color(0x282828)
constructor( constructor(
scene: THREE.Scene, scene: THREE.Scene,
getActiveCamera: () => THREE.Camera, getActiveCamera: () => THREE.Camera,
@@ -61,18 +61,6 @@ export class PreviewManager implements PreviewManagerInterface {
init(): void {} init(): void {}
dispose(): void { dispose(): void {
if (this.previewRenderer) {
this.previewRenderer.forceContextLoss()
const canvas = this.previewRenderer.domElement
const event = new Event('webglcontextlost', {
bubbles: true,
cancelable: true
})
canvas.dispatchEvent(event)
this.previewRenderer.dispose()
}
if (this.previewBackgroundTexture) { if (this.previewBackgroundTexture) {
this.previewBackgroundTexture.dispose() this.previewBackgroundTexture.dispose()
} }
@@ -84,17 +72,6 @@ export class PreviewManager implements PreviewManagerInterface {
} }
createCapturePreview(container: Element | HTMLElement): void { createCapturePreview(container: Element | HTMLElement): void {
this.previewRenderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true,
preserveDrawingBuffer: true
})
this.previewRenderer.setSize(this.targetWidth, this.targetHeight)
this.previewRenderer.setClearColor(0x282828)
this.previewRenderer.autoClear = false
this.previewRenderer.outputColorSpace = THREE.SRGBColorSpace
this.previewContainer = document.createElement('div') this.previewContainer = document.createElement('div')
this.previewContainer.style.cssText = ` this.previewContainer.style.cssText = `
position: absolute; position: absolute;
@@ -104,7 +81,6 @@ export class PreviewManager implements PreviewManagerInterface {
display: block; display: block;
transition: border-color 0.1s ease; transition: border-color 0.1s ease;
` `
this.previewContainer.appendChild(this.previewRenderer.domElement)
const MIN_PREVIEW_WIDTH = 120 const MIN_PREVIEW_WIDTH = 120
const MAX_PREVIEW_WIDTH = 240 const MAX_PREVIEW_WIDTH = 240
@@ -131,7 +107,6 @@ export class PreviewManager implements PreviewManagerInterface {
} }
this.updatePreviewSize() this.updatePreviewSize()
this.updatePreviewRender()
}) })
this.previewContainer.style.display = this.showPreview ? 'block' : 'none' this.previewContainer.style.display = this.showPreview ? 'block' : 'none'
@@ -159,13 +134,48 @@ export class PreviewManager implements PreviewManagerInterface {
const previewHeight = const previewHeight =
(this.previewWidth * this.targetHeight) / this.targetWidth (this.previewWidth * this.targetHeight) / this.targetWidth
this.previewRenderer?.setSize(this.previewWidth, previewHeight, false)
this.previewContainer.style.width = `${this.previewWidth}px`
this.previewContainer.style.height = `${previewHeight}px`
}
getPreviewViewport(): {
left: number
bottom: number
width: number
height: number
} | null {
if (!this.showPreview || !this.previewContainer) {
return null
}
const renderer = this.getRenderer()
const canvas = renderer.domElement
const containerRect = this.previewContainer.getBoundingClientRect()
const canvasRect = canvas.getBoundingClientRect()
if (
containerRect.bottom < canvasRect.top ||
containerRect.top > canvasRect.bottom ||
containerRect.right < canvasRect.left ||
containerRect.left > canvasRect.right
) {
return null
}
const width = parseFloat(this.previewContainer.style.width)
const height = parseFloat(this.previewContainer.style.height)
const left = this.getRenderer().domElement.clientWidth - width
const bottom = 0
return { left, bottom, width, height }
} }
syncWithMainCamera(): void { syncWithMainCamera(): void {
if (!this.previewRenderer || !this.previewContainer || !this.showPreview) { if (!this.showPreview) return
return
}
this.previewCamera = this.getActiveCamera().clone() this.previewCamera = this.getActiveCamera().clone()
@@ -203,85 +213,73 @@ export class PreviewManager implements PreviewManagerInterface {
} }
this.previewCamera.lookAt(this.getControls().target) this.previewCamera.lookAt(this.getControls().target)
this.updatePreviewRender()
} }
updatePreviewRender(): void { renderPreview(): void {
if (!this.previewRenderer || !this.previewContainer || !this.showPreview) const viewport = this.getPreviewViewport()
return if (!viewport) return
if ( const renderer = this.getRenderer()
!this.previewCamera ||
(this.getActiveCamera() instanceof THREE.PerspectiveCamera &&
!(this.previewCamera instanceof THREE.PerspectiveCamera)) ||
(this.getActiveCamera() instanceof THREE.OrthographicCamera &&
!(this.previewCamera instanceof THREE.OrthographicCamera))
) {
this.previewCamera = this.getActiveCamera().clone()
}
this.previewCamera.position.copy(this.getActiveCamera().position) const originalClearColor = renderer.getClearColor(new THREE.Color())
this.previewCamera.rotation.copy(this.getActiveCamera().rotation) const originalClearAlpha = renderer.getClearAlpha()
const aspect = this.targetWidth / this.targetHeight this.syncWithMainCamera()
if (this.getActiveCamera() instanceof THREE.OrthographicCamera) { renderer.setViewport(
const activeOrtho = this.getActiveCamera() as THREE.OrthographicCamera viewport.left,
const previewOrtho = this.previewCamera as THREE.OrthographicCamera viewport.bottom,
viewport.width,
viewport.height
)
renderer.setScissor(
viewport.left,
viewport.bottom,
viewport.width,
viewport.height
)
const frustumHeight = renderer.setClearColor(this.previewBackgroundColor, 1.0)
(activeOrtho.top - activeOrtho.bottom) / activeOrtho.zoom renderer.clear()
const frustumWidth = frustumHeight * aspect
previewOrtho.top = frustumHeight / 2
previewOrtho.left = -frustumWidth / 2
previewOrtho.right = frustumWidth / 2
previewOrtho.bottom = -frustumHeight / 2
previewOrtho.zoom = 1
previewOrtho.updateProjectionMatrix()
} else {
;(this.previewCamera as THREE.PerspectiveCamera).aspect = aspect
;(this.previewCamera as THREE.PerspectiveCamera).fov = (
this.getActiveCamera() as THREE.PerspectiveCamera
).fov
;(this.previewCamera as THREE.PerspectiveCamera).updateProjectionMatrix()
}
this.previewCamera.lookAt(this.getControls().target)
const previewHeight =
(this.previewWidth * this.targetHeight) / this.targetWidth
this.previewRenderer.setSize(this.previewWidth, previewHeight, false)
this.previewRenderer.outputColorSpace = THREE.SRGBColorSpace
this.previewRenderer.clear()
if (this.previewBackgroundMesh && this.previewBackgroundTexture) { if (this.previewBackgroundMesh && this.previewBackgroundTexture) {
const material = this.previewBackgroundMesh const material = this.previewBackgroundMesh
.material as THREE.MeshBasicMaterial .material as THREE.MeshBasicMaterial
if (material.map) { if (material.map) {
const currentToneMapping = this.previewRenderer.toneMapping const currentToneMapping = renderer.toneMapping
const currentExposure = this.previewRenderer.toneMappingExposure const currentExposure = renderer.toneMappingExposure
this.previewRenderer.toneMapping = THREE.NoToneMapping renderer.toneMapping = THREE.NoToneMapping
this.previewRenderer.render( renderer.render(
this.previewBackgroundScene, this.previewBackgroundScene,
this.previewBackgroundCamera this.previewBackgroundCamera
) )
this.previewRenderer.toneMapping = currentToneMapping renderer.toneMapping = currentToneMapping
this.previewRenderer.toneMappingExposure = currentExposure renderer.toneMappingExposure = currentExposure
} }
} }
this.previewRenderer.render(this.scene, this.previewCamera) renderer.render(this.scene, this.previewCamera)
renderer.setClearColor(originalClearColor, originalClearAlpha)
}
setPreviewBackgroundColor(color: string | number): void {
this.previewBackgroundColor.set(color)
}
getPreviewBackgroundColor(): THREE.Color {
return this.previewBackgroundColor.clone()
}
updatePreviewRender(): void {
this.syncWithMainCamera()
} }
togglePreview(showPreview: boolean): void { togglePreview(showPreview: boolean): void {
if (this.previewRenderer) { this.showPreview = showPreview
this.showPreview = showPreview if (this.previewContainer) {
this.previewContainer.style.display = this.showPreview ? 'block' : 'none' this.previewContainer.style.display = this.showPreview ? 'block' : 'none'
} }
@@ -306,7 +304,7 @@ export class PreviewManager implements PreviewManagerInterface {
) )
} }
if (this.previewRenderer && this.previewCamera) { if (this.previewCamera) {
if (this.previewCamera instanceof THREE.PerspectiveCamera) { if (this.previewCamera instanceof THREE.PerspectiveCamera) {
this.previewCamera.aspect = width / height this.previewCamera.aspect = width / height
this.previewCamera.updateProjectionMatrix() this.previewCamera.updateProjectionMatrix()
@@ -322,7 +320,6 @@ export class PreviewManager implements PreviewManagerInterface {
handleResize(): void { handleResize(): void {
this.updatePreviewSize() this.updatePreviewSize()
this.updatePreviewRender()
} }
updateBackgroundTexture(texture: THREE.Texture | null): void { updateBackgroundTexture(texture: THREE.Texture | null): void {

View File

@@ -100,7 +100,6 @@ export interface ViewHelperManagerInterface extends BaseManager {
} }
export interface PreviewManagerInterface extends BaseManager { export interface PreviewManagerInterface extends BaseManager {
previewRenderer: THREE.WebGLRenderer | null
previewCamera: THREE.Camera previewCamera: THREE.Camera
previewContainer: HTMLDivElement previewContainer: HTMLDivElement
showPreview: boolean showPreview: boolean
@@ -112,6 +111,14 @@ export interface PreviewManagerInterface extends BaseManager {
setTargetSize(width: number, height: number): void setTargetSize(width: number, height: number): void
handleResize(): void handleResize(): void
updateBackgroundTexture(texture: THREE.Texture | null): void updateBackgroundTexture(texture: THREE.Texture | null): void
getPreviewViewport(): {
left: number
bottom: number
width: number
height: number
} | null
renderPreview(): void
syncWithMainCamera(): void
} }
export interface EventManagerInterface { export interface EventManagerInterface {