Spaces:
Running
Running
| import { | |
| FrontSide, | |
| BackSide, | |
| DoubleSide, | |
| RGBAFormat, | |
| NearestFilter, | |
| LinearFilter, | |
| PCFShadowMap, | |
| VSMShadowMap, | |
| RGBADepthPacking, | |
| NoBlending, | |
| } from '../../constants.js'; | |
| import { WebGLRenderTarget } from '../WebGLRenderTarget.js'; | |
| import { MeshDepthMaterial } from '../../materials/MeshDepthMaterial.js'; | |
| import { MeshDistanceMaterial } from '../../materials/MeshDistanceMaterial.js'; | |
| import { ShaderMaterial } from '../../materials/ShaderMaterial.js'; | |
| import { BufferAttribute } from '../../core/BufferAttribute.js'; | |
| import { BufferGeometry } from '../../core/BufferGeometry.js'; | |
| import { Mesh } from '../../objects/Mesh.js'; | |
| import { Vector4 } from '../../math/Vector4.js'; | |
| import { Vector2 } from '../../math/Vector2.js'; | |
| import { Frustum } from '../../math/Frustum.js'; | |
| import * as vsm from '../shaders/ShaderLib/vsm.glsl.js'; | |
| function WebGLShadowMap(_renderer, _objects, _capabilities) { | |
| let _frustum = new Frustum(); | |
| const _shadowMapSize = new Vector2(), | |
| _viewportSize = new Vector2(), | |
| _viewport = new Vector4(), | |
| _depthMaterial = new MeshDepthMaterial({ depthPacking: RGBADepthPacking }), | |
| _distanceMaterial = new MeshDistanceMaterial(), | |
| _materialCache = {}, | |
| _maxTextureSize = _capabilities.maxTextureSize; | |
| const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; | |
| const shadowMaterialVertical = new ShaderMaterial({ | |
| defines: { | |
| VSM_SAMPLES: 8, | |
| }, | |
| uniforms: { | |
| shadow_pass: { value: null }, | |
| resolution: { value: new Vector2() }, | |
| radius: { value: 4.0 }, | |
| }, | |
| vertexShader: vsm.vertex, | |
| fragmentShader: vsm.fragment, | |
| }); | |
| const shadowMaterialHorizontal = shadowMaterialVertical.clone(); | |
| shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; | |
| const fullScreenTri = new BufferGeometry(); | |
| fullScreenTri.setAttribute('position', new BufferAttribute(new Float32Array([-1, -1, 0.5, 3, -1, 0.5, -1, 3, 0.5]), 3)); | |
| const fullScreenMesh = new Mesh(fullScreenTri, shadowMaterialVertical); | |
| const scope = this; | |
| this.enabled = false; | |
| this.autoUpdate = true; | |
| this.needsUpdate = false; | |
| this.type = PCFShadowMap; | |
| this.render = function (lights, scene, camera) { | |
| if (scope.enabled === false) return; | |
| if (scope.autoUpdate === false && scope.needsUpdate === false) return; | |
| if (lights.length === 0) return; | |
| const currentRenderTarget = _renderer.getRenderTarget(); | |
| const activeCubeFace = _renderer.getActiveCubeFace(); | |
| const activeMipmapLevel = _renderer.getActiveMipmapLevel(); | |
| const _state = _renderer.state; | |
| // Set GL state for depth map. | |
| _state.setBlending(NoBlending); | |
| _state.buffers.color.setClear(1, 1, 1, 1); | |
| _state.buffers.depth.setTest(true); | |
| _state.setScissorTest(false); | |
| // render depth map | |
| for (let i = 0, il = lights.length; i < il; i++) { | |
| const light = lights[i]; | |
| const shadow = light.shadow; | |
| if (shadow === undefined) { | |
| console.warn('THREE.WebGLShadowMap:', light, 'has no shadow.'); | |
| continue; | |
| } | |
| if (shadow.autoUpdate === false && shadow.needsUpdate === false) continue; | |
| _shadowMapSize.copy(shadow.mapSize); | |
| const shadowFrameExtents = shadow.getFrameExtents(); | |
| _shadowMapSize.multiply(shadowFrameExtents); | |
| _viewportSize.copy(shadow.mapSize); | |
| if (_shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize) { | |
| if (_shadowMapSize.x > _maxTextureSize) { | |
| _viewportSize.x = Math.floor(_maxTextureSize / shadowFrameExtents.x); | |
| _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; | |
| shadow.mapSize.x = _viewportSize.x; | |
| } | |
| if (_shadowMapSize.y > _maxTextureSize) { | |
| _viewportSize.y = Math.floor(_maxTextureSize / shadowFrameExtents.y); | |
| _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; | |
| shadow.mapSize.y = _viewportSize.y; | |
| } | |
| } | |
| if (shadow.map === null && !shadow.isPointLightShadow && this.type === VSMShadowMap) { | |
| const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; | |
| shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); | |
| shadow.map.texture.name = light.name + '.shadowMap'; | |
| shadow.mapPass = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); | |
| shadow.camera.updateProjectionMatrix(); | |
| } | |
| if (shadow.map === null) { | |
| const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; | |
| shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); | |
| shadow.map.texture.name = light.name + '.shadowMap'; | |
| shadow.camera.updateProjectionMatrix(); | |
| } | |
| _renderer.setRenderTarget(shadow.map); | |
| _renderer.clear(); | |
| const viewportCount = shadow.getViewportCount(); | |
| for (let vp = 0; vp < viewportCount; vp++) { | |
| const viewport = shadow.getViewport(vp); | |
| _viewport.set(_viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w); | |
| _state.viewport(_viewport); | |
| shadow.updateMatrices(light, vp); | |
| _frustum = shadow.getFrustum(); | |
| renderObject(scene, camera, shadow.camera, light, this.type); | |
| } | |
| // do blur pass for VSM | |
| if (!shadow.isPointLightShadow && this.type === VSMShadowMap) { | |
| VSMPass(shadow, camera); | |
| } | |
| shadow.needsUpdate = false; | |
| } | |
| scope.needsUpdate = false; | |
| _renderer.setRenderTarget(currentRenderTarget, activeCubeFace, activeMipmapLevel); | |
| }; | |
| function VSMPass(shadow, camera) { | |
| const geometry = _objects.update(fullScreenMesh); | |
| 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 | |
| shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; | |
| shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; | |
| shadowMaterialVertical.uniforms.radius.value = shadow.radius; | |
| _renderer.setRenderTarget(shadow.mapPass); | |
| _renderer.clear(); | |
| _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null); | |
| // horizontal pass | |
| shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; | |
| shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; | |
| shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; | |
| _renderer.setRenderTarget(shadow.map); | |
| _renderer.clear(); | |
| _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null); | |
| } | |
| function getDepthMaterial(object, geometry, material, light, shadowCameraNear, shadowCameraFar, type) { | |
| let result = null; | |
| const customMaterial = light.isPointLight === true ? object.customDistanceMaterial : object.customDepthMaterial; | |
| if (customMaterial !== undefined) { | |
| result = customMaterial; | |
| } else { | |
| result = light.isPointLight === true ? _distanceMaterial : _depthMaterial; | |
| } | |
| if ( | |
| (_renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0) || | |
| (material.displacementMap && material.displacementScale !== 0) || | |
| (material.alphaMap && material.alphaTest > 0) | |
| ) { | |
| // in this case we need a unique material instance reflecting the | |
| // appropriate state | |
| const keyA = result.uuid, | |
| keyB = material.uuid; | |
| let materialsForVariant = _materialCache[keyA]; | |
| if (materialsForVariant === undefined) { | |
| materialsForVariant = {}; | |
| _materialCache[keyA] = materialsForVariant; | |
| } | |
| let cachedMaterial = materialsForVariant[keyB]; | |
| if (cachedMaterial === undefined) { | |
| cachedMaterial = result.clone(); | |
| materialsForVariant[keyB] = cachedMaterial; | |
| } | |
| result = cachedMaterial; | |
| } | |
| result.visible = material.visible; | |
| result.wireframe = material.wireframe; | |
| if (type === VSMShadowMap) { | |
| result.side = material.shadowSide !== null ? material.shadowSide : material.side; | |
| } else { | |
| result.side = material.shadowSide !== null ? material.shadowSide : shadowSide[material.side]; | |
| } | |
| result.alphaMap = material.alphaMap; | |
| result.alphaTest = material.alphaTest; | |
| result.clipShadows = material.clipShadows; | |
| result.clippingPlanes = material.clippingPlanes; | |
| result.clipIntersection = material.clipIntersection; | |
| result.displacementMap = material.displacementMap; | |
| result.displacementScale = material.displacementScale; | |
| result.displacementBias = material.displacementBias; | |
| result.wireframeLinewidth = material.wireframeLinewidth; | |
| result.linewidth = material.linewidth; | |
| if (light.isPointLight === true && result.isMeshDistanceMaterial === true) { | |
| result.referencePosition.setFromMatrixPosition(light.matrixWorld); | |
| result.nearDistance = shadowCameraNear; | |
| result.farDistance = shadowCameraFar; | |
| } | |
| return result; | |
| } | |
| function renderObject(object, camera, shadowCamera, light, type) { | |
| if (object.visible === false) return; | |
| const visible = object.layers.test(camera.layers); | |
| if (visible && (object.isMesh || object.isLine || object.isPoints)) { | |
| if ((object.castShadow || (object.receiveShadow && type === VSMShadowMap)) && (!object.frustumCulled || _frustum.intersectsObject(object))) { | |
| object.modelViewMatrix.multiplyMatrices(shadowCamera.matrixWorldInverse, object.matrixWorld); | |
| const geometry = _objects.update(object); | |
| const material = object.material; | |
| if (Array.isArray(material)) { | |
| const groups = geometry.groups; | |
| for (let k = 0, kl = groups.length; k < kl; k++) { | |
| const group = groups[k]; | |
| const groupMaterial = material[group.materialIndex]; | |
| if (groupMaterial && groupMaterial.visible) { | |
| const depthMaterial = getDepthMaterial(object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type); | |
| _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, group); | |
| } | |
| } | |
| } else if (material.visible) { | |
| const depthMaterial = getDepthMaterial(object, geometry, material, light, shadowCamera.near, shadowCamera.far, type); | |
| _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, null); | |
| } | |
| } | |
| } | |
| const children = object.children; | |
| for (let i = 0, l = children.length; i < l; i++) { | |
| renderObject(children[i], camera, shadowCamera, light, type); | |
| } | |
| } | |
| } | |
| export { WebGLShadowMap }; | |