@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
199 lines (183 loc) • 5.69 kB
text/typescript
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);
}
}