playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
135 lines (134 loc) • 4.48 kB
JavaScript
import { Vec4 } from "../../core/math/vec4.js";
import { Mat4 } from "../../core/math/mat4.js";
import { SEMANTIC_POSITION } from "../../platform/graphics/constants.js";
import { LIGHTTYPE_DIRECTIONAL, LIGHTTYPE_OMNI } from "../constants.js";
import { ShaderUtils } from "../shader-lib/shader-utils.js";
import { LightCamera } from "./light-camera.js";
import { QuadRender } from "../graphics/quad-render.js";
import { RenderPass } from "../../platform/graphics/render-pass.js";
const _viewport = new Vec4();
const _invViewProjMatrices = [];
class RenderPassCookieRenderer extends RenderPass {
_quadRenderer2D = null;
_quadRendererCube = null;
_filteredLights = [];
_forceCopy = false;
_evtDeviceRestored = null;
constructor(device, cubeSlotsOffsets) {
super(device);
this._cubeSlotsOffsets = cubeSlotsOffsets;
this.requiresCubemaps = false;
this.blitTextureId = device.scope.resolve("blitTexture");
this.invViewProjId = device.scope.resolve("invViewProj");
this._evtDeviceRestored = device.on("devicerestored", this.onDeviceRestored, this);
}
destroy() {
this._quadRenderer2D?.destroy();
this._quadRenderer2D = null;
this._quadRendererCube?.destroy();
this._quadRendererCube = null;
this._evtDeviceRestored?.off();
this._evtDeviceRestored = null;
}
static create(renderTarget, cubeSlotsOffsets) {
const renderPass = new RenderPassCookieRenderer(renderTarget.device, cubeSlotsOffsets);
renderPass.init(renderTarget);
renderPass.colorOps.clear = false;
renderPass.depthStencilOps.clearDepth = false;
return renderPass;
}
onDeviceRestored() {
this._forceCopy = true;
}
update(lights) {
const filteredLights = this._filteredLights;
this.filter(lights, filteredLights);
this.executeEnabled = filteredLights.length > 0;
}
filter(lights, filteredLights) {
for (let i = 0; i < lights.length; i++) {
const light = lights[i];
if (light._type === LIGHTTYPE_DIRECTIONAL) {
continue;
}
if (!light.atlasViewportAllocated) {
continue;
}
if (!light.atlasSlotUpdated && !this._forceCopy) {
continue;
}
if (light.enabled && light.cookie && light.visibleThisFrame) {
filteredLights.push(light);
}
}
this._forceCopy = false;
}
initInvViewProjMatrices() {
if (!_invViewProjMatrices.length) {
for (let face = 0; face < 6; face++) {
const camera = LightCamera.create(this.device, null, LIGHTTYPE_OMNI, face);
const projMat = camera.projectionMatrix;
const viewMat = camera.node.getLocalTransform().clone().invert();
_invViewProjMatrices[face] = new Mat4().mul2(projMat, viewMat).invert();
}
}
}
get quadRenderer2D() {
if (!this._quadRenderer2D) {
const shader = ShaderUtils.createShader(this.device, {
uniqueName: "cookieRenderer2d",
attributes: { vertex_position: SEMANTIC_POSITION },
vertexChunk: "cookieBlitVS",
fragmentChunk: "cookieBlit2DPS"
});
this._quadRenderer2D = new QuadRender(shader);
}
return this._quadRenderer2D;
}
get quadRendererCube() {
if (!this._quadRendererCube) {
const shader = ShaderUtils.createShader(this.device, {
uniqueName: "cookieRendererCube",
attributes: { vertex_position: SEMANTIC_POSITION },
vertexChunk: "cookieBlitVS",
fragmentChunk: "cookieBlitCubePS"
});
this._quadRendererCube = new QuadRender(shader);
}
return this._quadRendererCube;
}
execute() {
const device = this.device;
device.setDrawStates();
const renderTargetWidth = this.renderTarget.colorBuffer.width;
const cubeSlotsOffsets = this._cubeSlotsOffsets;
const filteredLights = this._filteredLights;
for (let i = 0; i < filteredLights.length; i++) {
const light = filteredLights[i];
const faceCount = light.numShadowFaces;
const quad = faceCount > 1 ? this.quadRendererCube : this.quadRenderer2D;
if (faceCount > 1) {
this.initInvViewProjMatrices();
}
this.blitTextureId.setValue(light.cookie);
for (let face = 0; face < faceCount; face++) {
_viewport.copy(light.atlasViewport);
if (faceCount > 1) {
const smallSize = _viewport.z / 3;
const offset = cubeSlotsOffsets[face];
_viewport.x += smallSize * offset.x;
_viewport.y += smallSize * offset.y;
_viewport.z = smallSize;
_viewport.w = smallSize;
this.invViewProjId.setValue(_invViewProjMatrices[face].data);
}
_viewport.mulScalar(renderTargetWidth);
quad.render(_viewport);
}
}
filteredLights.length = 0;
}
}
export {
RenderPassCookieRenderer
};