UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

221 lines (162 loc) • 6.38 kB
import { Color, LinearFilter, NearestFilter, RGBAFormat, Scene, ShaderMaterial, Vector2, VSMShadowMap, WebGLRenderTarget } from "three"; import Signal from "../../../core/events/signal/Signal.js"; import { fragment as vsm_frag, vertex as vsm_vert } from "./vsm.glsl.js"; import { renderScreenSpace } from "../render/utils/renderScreenSpace.js"; const shadowMaterialVertical = new ShaderMaterial({ uniforms: { shadow_pass: { value: null }, resolution: { value: new Vector2() }, radius: { value: 4.0 }, samples: { value: 8.0 } }, defines: { VSM_SAMPLES: 8, // in 'defines' since r134 }, vertexShader: vsm_vert, fragmentShader: vsm_frag }); const shadowMaterialHorizontal = shadowMaterialVertical.clone(); shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; const scratch_color = new Color(); export class ShadowMapRenderer { constructor() { /** * * @type {WebGLRenderer|null} * @private */ this.__renderer = null; /** * * @type {Signal} */ this.onBeforeRender = new Signal(); } /** * * @param {THREE.WebGLRenderer} renderer * @param {LightShadow} shadow * @param {Scene} scene * @param {Light} light */ update(renderer, shadow, scene, light) { // const shadow_camera = shadow.camera; // this.__scene = scene; this.__renderer = renderer; this.__ensure_map(shadow, renderer); // draw scene from perspective of shadow camera into the render texture const __rt = renderer.getRenderTarget(); const __acf = renderer.getActiveCubeFace(); const __aml = renderer.getActiveMipmapLevel(); const __scissor = renderer.getScissorTest(); renderer.getClearColor(scratch_color); const __clear_alpha = renderer.getClearAlpha(); const __autoClearColor = renderer.autoClearColor; const __autoClearDepth = renderer.autoClearDepth; const __autoClearStencil = renderer.autoClearStencil; const __sortObjects = renderer.sortObjects; const target = shadow.map; renderer.setRenderTarget(target); renderer.setClearColor(0xFFFFFF, 1); renderer.setScissorTest(false); renderer.autoClearColor = false; renderer.autoClearDepth = false; renderer.autoClearStencil = false; // disable sorting, unnecessary since fragment cost is very low and sorting is expensive for large number of objects renderer.sortObjects = false; renderer.clear(true, true, false); shadow.updateMatrices(light, 0); this.onBeforeRender.send3(renderer, shadow_camera, scene); if (target.width <= 0 || target.height <= 0) { // no point in rendering } else { // do the draw renderer.render(scene, shadow_camera); if (renderer.shadowMap.type === VSMShadowMap) { this.__do_vsm_pass(shadow); } } // restore state renderer.setRenderTarget(__rt, __acf, __aml); renderer.setScissorTest(__scissor); renderer.setClearColor(scratch_color, __clear_alpha); renderer.autoClearColor = __autoClearColor; renderer.autoClearDepth = __autoClearDepth; renderer.autoClearStencil = __autoClearStencil; renderer.sortObjects = __sortObjects; // increment frame index to signal to the renderer that geometries might need to be updated renderer.info.render.frame++; } /** * * @param {LightShadow} shadow * @private */ __do_vsm_pass(shadow) { const _renderer = this.__renderer; if (shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples) { shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialVertical.needsUpdate = true; shadowMaterialHorizontal.needsUpdate = true; } // vertical pass const vertical = shadowMaterialVertical.uniforms; vertical.shadow_pass.value = shadow.map.texture; vertical.resolution.value = shadow.mapSize; vertical.radius.value = shadow.radius; vertical.samples.value = shadow.blurSamples; _renderer.setRenderTarget(shadow.mapPass); _renderer.clear(); renderScreenSpace(_renderer, shadowMaterialVertical); // horizontal pass const horizontal = shadowMaterialHorizontal.uniforms; horizontal.shadow_pass.value = shadow.mapPass.texture; horizontal.resolution.value = shadow.mapSize; horizontal.radius.value = shadow.radius; horizontal.samples.value = shadow.blurSamples; _renderer.setRenderTarget(shadow.map); _renderer.clear(); renderScreenSpace(_renderer, shadowMaterialHorizontal); } /** * * @param {LightShadow} shadow * @param {THREE.WebGLRenderer} renderer * @private */ __ensure_map(shadow, renderer) { const _shadowMapSize = shadow.mapSize; if (shadow.map === null) { const pars = { format: RGBAFormat, scissorTest: false, stencilBuffer: false }; if (!shadow.isPointLightShadow && renderer.shadowMap.type === VSMShadowMap) { pars.minFilter = LinearFilter; pars.magFilter = LinearFilter; shadow.mapPass = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, Object.assign({ depthBuffer: false }, pars)); } else { pars.minFilter = NearestFilter; pars.magFilter = NearestFilter; } shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.map.texture.name = '.shadowMap'; shadow.camera.updateProjectionMatrix(); } } }