Hello everyone,
Background: I need to override the way three.js renders different types of lights and their shadowmaps by making several optimizations and gaining more flexibility to finalize my hybrid rasterized/pathtracing rendering pipeline.
So I simply “copy/pasted” snippets of code from the WebGLLights and WebGLShadowMap classes, adapting them to my needs (for now, my approach is pretty naive; I’d like it to work already!).
Everything is working fine in my custom rendering shader with the uniforms defined in the updateLight() method (var shadowsData). I get ISO illumination with the vanilla three.js renderer. It’s also fine for applying the shadowmaps of the different lights, except for the PointLights.
Problem: This is the whole problem: the PointLights don’t seem to be generating their shadowMap correctly. If I inspect my shadow texture’s data buffer, I only get RGBA values of 1. Everything is white. Yet, I don’t see any difference between my code and the vanilla code regarding the rendering of the PointLights’ shadows.
I must be missing something; my doubts are directed towards the distanceMaterial and its rendering.
Rendering a DirectionalLight:
Rendering a PointLight:
renderLight(renderer, entity, camera) {
const shadow = entity.shadow;
const renderTarget = shadow.map;
if (!renderTarget || entity.entities.length === 0) return false;
renderer.getViewport(_oldViewPort);
const ogRenderTarget = renderer.getRenderTarget();
const ogRenderType = renderer.renderType;
const activeCubeFace = renderer.getActiveCubeFace();
renderer.getClearColor(ogClearColor);
const oldScissorTest = renderer.getScissorTest();
const activeMipmapLevel = renderer.getActiveMipmapLevel();
renderer.setRenderTarget(renderTarget);
renderer.renderType = 'DepthShadowPass';
renderer.setClearColor(clearColor, 1);
renderer.setScissorTest( false );
renderer.clear();
const viewportCount = shadow.getViewportCount();
renderTarget.setSize(this.shadowsData.lights[entity.indexInArray].shadowMapSize.x, this.shadowsData.lights[entity.indexInArray].shadowMapSize.y);
_viewportSize.copy(this.shadowsData.lights[entity.indexInArray].viewportSize);
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
);
renderer.setViewport( _viewport );
shadow.updateMatrices(entity, vp);
entity.renderEntities(renderer, camera, shadow.camera);
}
entity._needShadowsUpdate = false;
this._shadowsTextures[entity.indexInArray] = shadow.map.texture;
this._shadowsTextures[entity.indexInArray].version++;
renderer.setRenderTarget( ogRenderTarget, activeCubeFace, activeMipmapLevel );
renderer.renderType = ogRenderType;
renderer.setScissorTest( oldScissorTest );
renderer.setClearColor(ogClearColor);
renderer.setViewport( _oldViewPort );
if (renderTarget._debugContext) {
renderer.readRenderTargetPixels( renderTarget, 0, 0, renderTarget.width, renderTarget.height, renderTarget._debugBuffer );
const d = renderTarget._debugImageData.data;
for ( let i = 0, l = renderTarget.width * renderTarget.height; i < l; i ++ ) {
// Rgba to depth
const r = renderTarget._debugBuffer[ i * 4 + 0 ];
const g = renderTarget._debugBuffer[ i * 4 + 1 ];
const b = renderTarget._debugBuffer[ i * 4 + 2 ];
const a = renderTarget._debugBuffer[ i * 4 + 3 ];
d[ i * 4 + 0 ] = ( r * 255 ) | 0;
d[ i * 4 + 1 ] = ( g * 255 ) | 0;
d[ i * 4 + 2 ] = ( b * 255 ) | 0;
d[ i * 4 + 3 ] = 255;
}
renderTarget._debugContext.putImageData( renderTarget._debugImageData, 0, 0 );
console.log('renderTarget._debugImageData', renderTarget._debugImageData);
}
return true;
}
updateLight(entity, camera) {
if (!entity) return;
const light = entity.light;
const shadow = entity.shadow;
_shadowMapSize.copy( shadow.mapSize ).multiplyScalar( this.resolution );
_shadowMapSize.x = Math.floor( _shadowMapSize.x * 0.5 );
const mapSize = _shadowMapSize.clone();
const shadowFrameExtents = shadow.getFrameExtents();
_shadowMapSize.multiply( shadowFrameExtents );
_viewportSize.copy( mapSize );
const _maxTextureSize = this._maxTextureSize;
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;
}
}
const coneCos = entity.angle ? Math.cos(entity.angle || 0) : 0;
const penumbraCos = entity.angle ? Math.cos((entity.angle || 0) * (1 - (entity.penumbra || 0))) : 0;
if (entity.target) {
entity.target.updateMatrixWorld();
_tempVector3.setFromMatrixPosition(entity.target.matrixWorld);
} else {
_tempVector3.set(0, 0, 0);
}
shadow.updateMatrices(entity);
this.shadowsData.lights[entity.indexInArray] = {
uuid: entity.uuid,
type: light.isDirectionalLight ? 0 : light.isSpotLight ? 1 : 2, // 0: Directional, 1: Spot, 2: Point
shadowMapSize: mapSize,
viewportSize: _viewportSize.clone(),
shadowRadius: shadow.radius || 1.0,
shadowBias: shadow.bias || 0.0,
shadowIntensity: shadow.intensity || 1.0,
shadowNormalBias: shadow.normalBias || 0.02,
color: entity.color.clone().multiplyScalar(entity.intensity),
distance: entity.distance || 1.0,
decay: entity.decay || 1.0,
coneCos: coneCos,
penumbraCos: penumbraCos,
near: shadow.camera.near || 0.001,
far: shadow.camera.far || 1000.0,
inFrustum: entity.inFrustum || false,
index: entity.indexInArray,
originalLight: light,
originalEntity: entity,
shadowMatrix: shadow.matrix.clone(),
};
entity._shadowData = this.shadowsData.lights[entity.indexInArray];
const viewMatrix = camera.matrixWorldInverse;
if (!this.shadowsData.lights[entity.indexInArray].position) {
this.shadowsData.lights[entity.indexInArray].position = new Vector3();
}
this.shadowsData.lights[entity.indexInArray].position.setFromMatrixPosition(entity.matrixWorld).applyMatrix4(viewMatrix);
if (!this.shadowsData.lights[entity.indexInArray].direction) {
this.shadowsData.lights[entity.indexInArray].direction = new Vector3();
}
this.shadowsData.lights[entity.indexInArray].direction.setFromMatrixPosition(entity.matrixWorld).sub(_tempVector3).transformDirection(viewMatrix);
}
renderEntities(renderer, camera, shadowCamera) {
this.scene.transformEntitiesToInstanced(this.entities, this._instancedMeshes, this._instancedMeshesGroup);
this._instancedMeshesGroup.children.forEach(entity => {
if (!entity.depthMaterial) {
const depthMaterial = this.getDepthMaterial( renderer, entity.material );
entity.depthMaterial = depthMaterial;
}
entity._material = entity.material;
entity.material = entity.depthMaterial;
});
renderer.render( this._instancedMeshesGroup, shadowCamera );
this._instancedMeshesGroup.children.forEach(entity => {
entity.material = entity._material;
delete entity._material;
});
}
getDepthMaterial( renderer, material ) {
const light = this.light;
let result = ( light.isPointLight === true ) ? _distanceMaterial.clone() : _depthMaterial.clone()
if ( ( renderer.localClippingEnabled && material.clipShadows === true && Array.isArray( material.clippingPlanes ) && material.clippingPlanes.length !== 0 ) ||
( material.displacementMap && material.displacementScale !== 0 ) ||
( material.alphaMap && material.alphaTest > 0 ) ||
( material.map && material.alphaTest > 0 ) ||
( material.alphaToCoverage === true ) ) {
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;
material.addEventListener( 'dispose', this.onMaterialDispose );
}
result = cachedMaterial;
}
result.visible = material.visible;
result.wireframe = material.wireframe;
result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ];
result.alphaMap = material.alphaMap;
result.alphaTest = ( material.alphaToCoverage === true ) ? 0.5 : material.alphaTest; // approximate alphaToCoverage by using a fixed alphaTest value
result.map = material.map;
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 ) {
const materialProperties = renderer.properties.get( result );
materialProperties.light = this;
}
return result;
}

