UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

127 lines (124 loc) 3.87 kB
import { PIXELFORMAT_R32F, PIXELFORMAT_R16F, pixelFormatInfo, FILTER_LINEAR, FILTER_NEAREST, ADDRESS_CLAMP_TO_EDGE, FUNC_LESS } from '../../platform/graphics/constants.js'; import { RenderTarget } from '../../platform/graphics/render-target.js'; import { Texture } from '../../platform/graphics/texture.js'; import { LIGHTTYPE_OMNI, shadowTypeInfo, SHADOW_VSM_32F, SHADOW_PCSS_32F } from '../constants.js'; class ShadowMap { constructor(texture, targets){ this.texture = texture; this.cached = false; this.renderTargets = targets; } destroy() { if (this.texture) { this.texture.destroy(); this.texture = null; } const targets = this.renderTargets; for(let i = 0; i < targets.length; i++){ targets[i].destroy(); } this.renderTargets.length = 0; } static create(device, light) { let shadowMap = null; if (light._type === LIGHTTYPE_OMNI) { shadowMap = this.createCubemap(device, light._shadowResolution, light._shadowType); } else { shadowMap = this.create2dMap(device, light._shadowResolution, light._shadowType); } return shadowMap; } static createAtlas(device, resolution, shadowType) { const shadowMap = this.create2dMap(device, resolution, shadowType); const targets = shadowMap.renderTargets; const rt = targets[0]; for(let i = 0; i < 5; i++){ targets.push(rt); } return shadowMap; } static create2dMap(device, size, shadowType) { const shadowInfo = shadowTypeInfo.get(shadowType); let format = shadowInfo.format; if (format === PIXELFORMAT_R32F && !device.textureFloatRenderable && device.textureHalfFloatRenderable) { format = PIXELFORMAT_R16F; } const formatName = pixelFormatInfo.get(format)?.name; let filter = FILTER_LINEAR; if (shadowType === SHADOW_VSM_32F) { filter = device.extTextureFloatLinear ? FILTER_LINEAR : FILTER_NEAREST; } if (shadowType === SHADOW_PCSS_32F) { filter = FILTER_NEAREST; } const texture = new Texture(device, { format: format, width: size, height: size, mipmaps: false, minFilter: filter, magFilter: filter, addressU: ADDRESS_CLAMP_TO_EDGE, addressV: ADDRESS_CLAMP_TO_EDGE, name: `ShadowMap2D_${formatName}` }); let target = null; if (shadowInfo?.pcf) { texture.compareOnRead = true; texture.compareFunc = FUNC_LESS; target = new RenderTarget({ depthBuffer: texture }); } else { target = new RenderTarget({ colorBuffer: texture, depth: true }); } if (device.isWebGPU) { target.flipY = true; } return new ShadowMap(texture, [ target ]); } static createCubemap(device, size, shadowType) { const shadowInfo = shadowTypeInfo.get(shadowType); const formatName = pixelFormatInfo.get(shadowInfo.format)?.name; const isPcss = shadowType === SHADOW_PCSS_32F; const filter = isPcss ? FILTER_NEAREST : FILTER_LINEAR; const cubemap = new Texture(device, { format: shadowInfo?.format, width: size, height: size, cubemap: true, mipmaps: false, minFilter: filter, magFilter: filter, addressU: ADDRESS_CLAMP_TO_EDGE, addressV: ADDRESS_CLAMP_TO_EDGE, name: `ShadowMapCube_${formatName}` }); if (!isPcss) { cubemap.compareOnRead = true; cubemap.compareFunc = FUNC_LESS; } const targets = []; for(let i = 0; i < 6; i++){ if (isPcss) { targets.push(new RenderTarget({ colorBuffer: cubemap, face: i, depth: true })); } else { targets.push(new RenderTarget({ depthBuffer: cubemap, face: i })); } } return new ShadowMap(cubemap, targets); } } export { ShadowMap };