UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

203 lines (158 loc) 5.07 kB
import { max2 } from "../../../../../../core/math/max2.js"; import { ceilPowerOfTwo } from "../../../../../../core/binary/operations/ceilPowerOfTwo.js"; import { ClampToEdgeWrapping, FloatType, NearestFilter, RedFormat, WebGLRenderTarget } from "three"; import { renderScreenSpace } from "../../../utils/renderScreenSpace.js"; import { makeReductionShader } from "./ReductionShader.js"; import Vector2 from "../../../../../../core/geom/Vector2.js"; export class HierarchicalZBuffer { constructor() { /** * * @type {THREE.Texture|null} * @private */ this.__input_buffer = null; /** * * @type {THREE.WebGLRenderer|null} * @private */ this.__renderer = null; /** * * @type {THREE.WebGLRenderTarget[]} */ this.mips = []; /** * * @type {ShaderMaterial} * @private */ this.__material = makeReductionShader(); /** * * @type {Vector2} * @private */ this.__resolution = new Vector2(0, 0); /** * * @type {Vector2} * @private */ this.__limits = new Vector2(1, 1); } /** * * @return {Vector2} */ get mip_limits() { return this.__limits; } /** * @param {THREE.WebGLRenderer} v */ set renderer(v) { this.__renderer = v; } /** * @param {THREE.Texture} v */ set input(v) { this.__input_buffer = v; } build_mip_chain() { const size_max = ceilPowerOfTwo(max2(this.__resolution.x, this.__resolution.y)); const mip_depth = Math.log2(size_max); const mips = this.mips; const existing_mip_depth = mips.length; if (existing_mip_depth > mip_depth) { for (let i = mip_depth; i < existing_mip_depth; i++) { mips[i].dispose(); } mips.splice(mip_depth, existing_mip_depth - mip_depth); } else { for (let i = existing_mip_depth; i < mip_depth; i++) { const renderTarget = new WebGLRenderTarget(1, 1, { minFilter: NearestFilter, magFilter: NearestFilter, format: RedFormat, type: FloatType, internalFormat: 'R32F', depthBuffer: false, stencilBuffer: false, wrapS: ClampToEdgeWrapping, wrapT: ClampToEdgeWrapping, generateMipmaps: false }); renderTarget.texture.name = `Hierarchical Z buffer MIP ${i}`; mips[i] = renderTarget; } } } /** * * @param {number} x * @param {number} y */ set_resolution(x, y) { this.__resolution.set(x, y); const mips = this.mips; const mip_count = mips.length; let _x = ceilPowerOfTwo(x); let _y = ceilPowerOfTwo(y); for (let i = 0; i < mip_count; i++) { const mip = mips[i]; const _nx = max2(1, _x >> 1); const _ny = max2(1, _y >> 1); if (mip.width !== _nx || mip.height !== _ny) { mip.setSize(_nx, _ny); } if (_nx === 1 && _x !== _nx) { this.__limits.x = i; } if (_ny === 1 && _y !== _ny) { this.__limits.y = i; } _x = _nx; _y = _ny; } } build_mips() { const mips = this.mips; let t0 = null; let t1 = { texture: this.__input_buffer }; for (let i = 0; i < mips.length; i++) { t0 = t1; t1 = mips[i]; this.reduce_mip(t0.texture, t1); } this.__input_buffer.generateMipmaps = false; this.__input_buffer.mips = mips.map(m => m.texture); } /** * @private * @param {THREE.Texture} input * @param {THREE.WebGLRenderTarget} output */ reduce_mip(input, output) { const material = this.__material; material.uniforms.source_texture.value = input; material.uniforms.destination_resolution.value.set(output.width, output.height); material.uniforms.source_resolution.value.set(input.image.width, input.image.height); const renderer = this.__renderer; const __rt = renderer.getRenderTarget(); renderer.setRenderTarget(output); renderScreenSpace(renderer, material); renderer.setRenderTarget(__rt); } /** * Depth texture with mips * @returns {THREE.Texture} */ getTexture() { return this.__input_buffer; } }