@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
203 lines (158 loc) • 5.07 kB
JavaScript
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;
}
}