UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

199 lines (183 loc) 5.69 kB
import { Object3D, Material, Vector2, Vector4, Camera, WebGLRenderTarget, NearestFilter, RGBAFormat, FloatType, WebGLRenderer, Scene, NoToneMapping, NoColorSpace, Color, ColorSpace, ToneMapping, DepthTexture, UnsignedInt248Type, } from 'three'; import {coreGetDefaultCamera} from './CoreGetDefautCamera'; import {PolyScene} from '../../../../src/engine/scene/PolyScene'; import {setupDepthReadScene, updateDepthSetup} from './DepthRead'; export function coreCursorToUv(cursor: Vector2, target: Vector2) { target.x = 0.5 * (cursor.x + 1); target.y = 0.5 * (1 - cursor.y); } interface Object3DRestoreContext { parent: Object3D | null; } interface RendererRestoreContext { toneMapping: ToneMapping; outputColorSpace: ColorSpace; } interface RestoreContext { object: Object3DRestoreContext; renderer: RendererRestoreContext; } function _createDepthTexture() { const texture = new DepthTexture(1, 1); texture.type = UnsignedInt248Type; return texture; } export class RenderPixelController { // Note for this to work on iOS: // The materials used for picking should have their transparency OFF. // This could potentially be done automatically by traversing the scene first. private _colorWriteRenderTarget: WebGLRenderTarget = new WebGLRenderTarget(1, 1, { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat, type: FloatType, colorSpace: NoColorSpace, depthTexture: _createDepthTexture(), }); private _depthReadRenderTarget: WebGLRenderTarget = new WebGLRenderTarget(1, 1, { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat, type: FloatType, colorSpace: NoColorSpace, }); private _renderScene = new Scene(); private _depthReadSetup = setupDepthReadScene(); private _restoreContext: RestoreContext = { object: { parent: null, }, // scene: { // overrideMaterial: null, // }, renderer: { toneMapping: NoToneMapping, outputColorSpace: NoColorSpace, }, }; private _read = new Float32Array(4); renderColor( scene: PolyScene, object3D: Object3D, material: Material | null, camera: Camera, backgroundColor: Color | null, uv: Vector2, target: Vector4 ): Vector4 { this._doRender(scene, object3D, camera, material, backgroundColor, uv, target, false); return target; } renderDepth( scene: PolyScene, object3D: Object3D, camera: Camera, backgroundColor: Color | null, uv: Vector2, target: Vector4 ): Vector4 { this._doRender(scene, object3D, camera, null, backgroundColor, uv, target, true); return target; } private _doRender( scene: PolyScene, object3D: Object3D, camera: Camera, material: Material | null, backgroundColor: Color | null, uv: Vector2, target: Vector4, renderDepth: boolean ) { const renderer = scene.renderersRegister.lastRegisteredRenderer(); if (!renderer) { return target; } if (!(renderer instanceof WebGLRenderer)) { console.log('renderPixel: renderer found is not WebGLRenderer'); return target; } if (camera == null) { camera = coreGetDefaultCamera(scene); } this._prepare(object3D, material, backgroundColor, renderer); this._render(uv, camera, renderer, target, renderDepth); this._restore(object3D, renderer); return target; } private _prepare( object3D: Object3D, material: Material | null, backgroundColor: Color | null, renderer: WebGLRenderer ) { // save context this._restoreContext.renderer.outputColorSpace = renderer.outputColorSpace; this._restoreContext.renderer.toneMapping = renderer.toneMapping; this._restoreContext.object.parent = object3D.parent; // set context this._renderScene.background = backgroundColor; this._renderScene.overrideMaterial = material || null; this._renderScene.attach(object3D); renderer.toneMapping = NoToneMapping; renderer.outputColorSpace = NoColorSpace; } private _render(uv: Vector2, camera: Camera, renderer: WebGLRenderer, target: Vector4, readDepth: boolean) { (camera as any).setViewOffset( renderer.domElement.width, renderer.domElement.height, uv.x * renderer.domElement.width, uv.y * renderer.domElement.height, 1, 1 ); renderer.setRenderTarget(this._colorWriteRenderTarget); renderer.clear(); renderer.render(this._renderScene, camera); if (readDepth) { // read depth updateDepthSetup(this._depthReadSetup, camera, this._colorWriteRenderTarget); renderer.setRenderTarget(this._depthReadRenderTarget); renderer.render(this._depthReadSetup.scene, this._depthReadSetup.camera); renderer.readRenderTargetPixels(this._depthReadRenderTarget, 0, 0, 1, 1, this._read); } else { // There are some cases where .readRenderTargetPixels is slow, // and this seems to be due to the calls to _gl.getParameters. // Here we are bypassing it. // Note: this attempt to bypass needs "properties", which is internal to WebGLRenderer. // const context = renderer.getContext(); // const textureFormat = context.RGBA; // RGBAFormat see three/WebGLUtils.js // const textureType = context.FLOAT; // FloatType see three/WebGLUtils.js // context.readPixels(0, 0, 1, 1, textureFormat, textureType, this._read); renderer.readRenderTargetPixels(this._colorWriteRenderTarget, 0, 0, 1, 1, this._read); } renderer.setRenderTarget(null); (camera as any).clearViewOffset(); // read buffer into target vector target.fromArray(this._read); } private _restore(object3D: Object3D, renderer: WebGLRenderer) { renderer.outputColorSpace = this._restoreContext.renderer.outputColorSpace; renderer.toneMapping = this._restoreContext.renderer.toneMapping; this._restoreContext.object.parent?.attach(object3D); } }