Crash in refreshUniformsDistance when light.castShadow is true

In my program (using three@0.157.0) I have a rather complex scene using a MeshPhysicalNodeMaterial.
I can sucessfully add a PointLight to the scene and it renders just fine.

However, as soon as I set the lights’ castShadow = true, my program crashes in refreshUniformsDistance of src\renderers\webgl\WebGLMaterials.js with the following error:

ERROR TypeError: Cannot read properties of undefined (reading 'value')
    at refreshUniformsDistance (d:\Work\platform\web-app\node_modules\three\build\three.module.js:27704:30)
    at Object.refreshMaterialUniforms (d:\Work\platform\web-app\node_modules\three\build\three.module.js:27228:4)
    at setProgram (d:\Work\platform\web-app\node_modules\three\build\three.module.js:29983:15)
    at WebGLRenderer.renderBufferDirect (d:\Work\platform\web-app\node_modules\three\build\three.module.js:28843:20)
    at renderObject (d:\Work\platform\web-app\node_modules\three\build\three.module.js:22278:16)
    at renderObject (d:\Work\platform\web-app\node_modules\three\build\three.module.js:22290:4)
    at renderObject (d:\Work\platform\web-app\node_modules\three\build\three.module.js:22290:4)
    at WebGLShadowMap.render (d:\Work\platform\web-app\node_modules\three\build\three.module.js:22089:5)
    at WebGLRenderer.render (d:\Work\platform\web-app\node_modules\three\build\three.module.js:29160:14)

The function is defined as follows:

function refreshUniformsDistance( uniforms, material ) {

		const light = properties.get( material ).light;

		uniforms.referencePosition.value.setFromMatrixPosition( light.matrixWorld );
		uniforms.nearDistance.value = light.shadow.camera.near;
		uniforms.farDistance.value = light.shadow.camera.far;

	}

Indeed, uniforms contains all sorts of properties but no referencePosition, nearDistance or farDistance, which yields above error consequently. The material that gets passed to this function is an instance of MeshDistanceMaterial that seems to have been created on line 366 of src\renderers\webgl\WebGLShadowMap.js.

I tried to create a minimal example but unfortunately failed to reproduce it there.

My questions would be, is there anything that can be done on my side, that the referencePosition, nearDistance and farDistance uniforms get defined for the MeshDistanceMaterial (which gets created by ThreeJS internally in WebGLShadowMap)?

It would be good if you keep trying since without a test case I fear it will be hard to say something about the runtime error. This is something that shouldn’t happen.

Yes, I understand, I will re-try to create an example. It will be hard to boil it down to a simple example as it is a rather complex project, that was migrated from ThreeJS 0.118.0 to 1.57.0 and the error only shows up there (in 0.118.0 it was working as expected).

Is there maybe any general advice what to look out for. I would also be willing to find the bug within ThreeJS myself. However, I currently have no understanding how and when the uniforms are actually filled (i.e. what mechanism decides which values are defined). Any additional information would be highly appreciated :slight_smile:

Maybe, if it helps, this is the content of the uniforms variable

alphaMap:  {value: null}
alphaMapTransform: {value: Matrix3}
alphaTest: {value: 0}
ambientLightColor: {value: Array(0)}
anisotropyMap: {value: null}
anisotropyMapTransform: {value: Matrix3}
anisotropyVector: {value: Vector2}
aoMap: {value: null}
aoMapIntensity: {value: 1}
aoMapTransform: {value: Matrix3}
attenuationColor: {value: Color}
attenuationDistance: {value: 0}
bumpMap: {value: null}
bumpMapTransform: {value: Matrix3}
bumpScale: {value: 1}
clearcoat: {value: 0}
clearcoatMap: {value: null}
clearcoatMapTransform: {value: Matrix3}
clearcoatNormalMap: {value: null}
clearcoatNormalMapTransform: {value: Matrix3}
clearcoatNormalScale: {value: Vector2}
clearcoatRoughness: {value: 0}
clearcoatRoughnessMap: {value: null}
clearcoatRoughnessMapTransform: {value: Matrix3}
clippingPlanes: {value: null, needsUpdate: false}
diffuse: {value: Color}
directionalLightShadows: {value: Array(0), properties: {…}}
directionalLights: {value: Array(0), properties: {…}}
directionalShadowMap: {value: Array(0)}
directionalShadowMatrix: {value: Array(0)}
displacementBias: {value: 0}
displacementMap: {value: null}
displacementMapTransform: {value: Matrix3}
displacementScale: {value: 1}
emissive: {value: Color}
emissiveMap: {value: null}
emissiveMapTransform: {value: Matrix3}
envMap: {value: null}
envMapIntensity: {value: 1}
flipEnvMap: {value: -1}
fogColor: {value: Color}
fogDensity: {value: 0.00025}
fogFar: {value: 2000}
fogNear: {value: 1}
hemisphereLights: {value: Array(0), properties: {…}}
ior: {value: 1.5}
iridescence: {value: 0}
iridescenceIOR: {value: 1.3}
iridescenceMap: {value: null}
iridescenceMapTransform: {value: Matrix3}
iridescenceThicknessMap: {value: null}
iridescenceThicknessMapTransform: {value: Matrix3}
iridescenceThicknessMaximum: {value: 400}
iridescenceThicknessMinimum: {value: 100}
lightMap: {value: null}
lightMapIntensity: {value: 1}
lightMapTransform: {value: Matrix3}
lightProbe: {value: Array(0)}
ltc_1: {value: null}
ltc_2: {value: null}
map: {value: null}
mapTransform: {value: Matrix3}
metalness: {value: 0}
metalnessMap: {value: null}
metalnessMapTransform: {value: Matrix3}
modelViewMatrix: NodeUniform {isNodeUniform: true, name: 'modelViewMatrix', type: 'mat4', node: UniformNode, needsUpdate: undefined}
nodeUniform0: NodeUniform {isNodeUniform: true, name: 'nodeUniform0', type: 'mat3', node: UniformNode, needsUpdate: undefined}
nodeUniform1: NodeUniform {isNodeUniform: true, name: 'nodeUniform1', type: 'texture', node: TextureNode, needsUpdate: undefined}
nodeUniform2: NodeUniform {isNodeUniform: true, name: 'nodeUniform2', type: 'mat4', node: UniformNode, needsUpdate: undefined}
nodeUniform3: NodeUniform {isNodeUniform: true, name: 'nodeUniform3', type: 'texture', node: TextureNode, needsUpdate: undefined}
nodeUniform4: NodeUniform {isNodeUniform: true, name: 'nodeUniform4', type: 'mat4', node: UniformNode, needsUpdate: undefined}
nodeUniform5: NodeUniform {isNodeUniform: true, name: 'nodeUniform5', type: 'texture', node: TextureNode, needsUpdate: undefined}
nodeUniform6: NodeUniform {isNodeUniform: true, name: 'nodeUniform6', type: 'mat4', node: UniformNode, needsUpdate: undefined}
nodeUniform8: NodeUniform {isNodeUniform: true, name: 'nodeUniform8', type: 'texture', node: TextureNode, needsUpdate: undefined}
nodeUniform9: NodeUniform {isNodeUniform: true, name: 'nodeUniform9', type: 'mat4', node: UniformNode, needsUpdate: undefined}
nodeUniform10: NodeUniform {isNodeUniform: true, name: 'nodeUniform10', type: 'texture', node: TextureNode, needsUpdate: undefined}
nodeUniform11: NodeUniform {isNodeUniform: true, name: 'nodeUniform11', type: 'texture', node: TextureNode, needsUpdate: undefined}
nodeUniform12: NodeUniform {isNodeUniform: true, name: 'nodeUniform12', type: 'mat4', node: UniformNode, needsUpdate: undefined}
normalMap: {value: null}
normalMapTransform: {value: Matrix3}
normalScale: {value: Vector2}
opacity: {value: 1}
pointLightShadows: 
{value: Array(0), properties: {…}}
pointLights: {value: Array(0), properties: {…}}
pointShadowMap: {value: Array(0)}
pointShadowMatrix: {value: Array(0)}
rectAreaLights: {value: Array(0), properties: {…}}
reflectivity: {value: 1}
refractionRatio: {value: 0.98}
roughness: {value: 1}
roughnessMap: {value: null}
roughnessMapTransform: {value: Matrix3}
sheen: {value: 0}
sheenColor: {value: Color}
sheenColorMap: {value: null}
sheenColorMapTransform: {value: Matrix3}
sheenRoughness: {value: 1}
sheenRoughnessMap: {value: null}
sheenRoughnessMapTransform: {value: Matrix3}
specularColor: {value: Color}
specularColorMap: {value: null}
specularColorMapTransform: {value: Matrix3}
specularIntensity: {value: 1}
specularIntensityMap: {value: null}
specularIntensityMapTransform: {value: Matrix3}
spotLightMap: {value: Array(0)}
spotLightMatrix: {value: Array(0)}
spotLightShadows: {value: Array(0), properties: {…}}
spotLights: {value: Array(0), properties: {…}}
spotShadowMap: {value: Array(0)}
thickness: {value: 0}
thicknessMap: {value: null}
thicknessMapTransform: {value: Matrix3}
transmission: {value: 0}
transmissionMap: {value: null}
transmissionMapTransform: {value: Matrix3}
transmissionSamplerMap: {value: null}
transmissionSamplerSize: {value: Vector2}
[[Prototype]]: Object

This is the material variable:

alphaHash: false
alphaMap: null
alphaToCoverage: false
blendDst: 205
blendDstAlpha: null
blendEquation: 100
blendEquationAlpha: null
blendSrc: 204
blendSrcAlpha: null
blending: 1
clipIntersection: false
clipShadows: false
clippingPlanes: null
colorWrite: true
depthFunc: 3
depthTest: true
depthWrite: true
displacementBias: 0
displacementMap: null
displacementScale: 1
dithering: false
forceSinglePass: false
isMaterial: true
isMeshDistanceMaterial: true
linewidth: 1
map: null
name: ""
opacity: 1
polygonOffset: false
polygonOffsetFactor: 0
polygonOffsetUnits: 0
precision: null
premultipliedAlpha: false
shadowSide: null
side: 1
stencilFail: 7680
stencilFunc: 519
stencilFuncMask: 255
stencilRef: 0
stencilWrite: false
stencilWriteMask: 255
stencilZFail: 7680
stencilZPass: 7680
toneMapped: true
transparent: false
type: "MeshDistanceMaterial"
userData: {}
uuid: "fa3e8378-8a92-449e-8ae9-2943e677e52e"
version: 0
vertexColors: false
visible: true
wireframe: false
wireframeLinewidth: 1
_alphaTest: 0
_listeners: {dispose: Array(1)}
id: 131
alphaTest: (...)
[[Prototype]]: Material

Maybe you can try to migrate like so:

0.118.0 > 0.128.0 > 0.138.0 > 0.148.0 > 0.157.0

This will make it easier to perform the necessary migration tasks and to pinpoint the release when the runtime error occurs for the first time.

1 Like
Original Post

After having stepped through the program lots of times it seems that it is somehow related to:

  • my scene has several objects, some receive shadows, some not
  • all generated MeshDistanceMaterials for the rendered objects seem to be the same / shared
  • uniforms seem to be cached in programCache with the object as key (in this case MeshDistanceMaterial)

So it might be that it is reading the wrong cached values? :thinking:

EDIT: Actually, this was misleading, the problem seems to be somewhere else (see below)

This is in src\renderers\WebGLRenderer.js line 1530

parameters.uniforms from the program cache still contain the correct values and include the desired uniforms referencePosition, nearDistance and farDistance

image

However, after material.onBuild( object, parameters, _this ); is executed the uniforms get extended / overwritten and referencePosition, nearDistance, farDistance are missing afterwards.

This happens in examples\jsm\renderers\webgl-legacy\nodes\WebGLNodeBuilder.js line 92:

I guess the already existing shader.uniforms should also be merged and not omitted?

Does that mean you are using the node material? Do you mind giving some more context how your setup looks like?

Sure,
we have built a scene graph editor where we can add objects and lights and maintain our own scene graph.

During rendering, we convert our internal representation to a ThreeJS scene.
In my case, ThreeJS crashes when I have a basic scene consisting of a plane with a MeshStandardNodeMaterial as soon as I add a light with castShadow = true to the scene graph (castShadow = false works).

More specifically my scene consists of the following objects
children:
0: TransformControls
1: Group with a single child of a plane having a MeshStandardNodeMaterial

Once the following child is added to the scene the renderer throws said exception:
2: Group with a single child of a point light with castShadow = true

The exception is thrown in the line 1098 of the WebGLRenderer
shadowMap.render( shadowsArray, scene, camera );

Strangely we also have some scenes where the point lights work, it might be the structure of the scene (i.e. the order or the stacking into different groups that yields the error)

Meanwhile I have found out, that in my case the error occurs if the first object in the scene that casts a shadow happens to have a node material. If the first object has a standard material everything is working, and also the objects with the node material are correctly rendered. So it just depends on the order of the child items in the scene.

I could bypass this, by always adding a dummy object first to the scene having a standard material before the other objects are added:

const threeObject = new THREE.Mesh(new THREE.PlaneGeometry(0, 0), new THREE.MeshStandardMaterial())
threeObject.frustumCulled = false
threeObject.castShadow = true
threeScene.add(threeObject)

However, this indicates that there is something wrong internally in ThreeJS. Unfortunately I’m still not able to create a minimal example that reproduces this.

I have another issue with node materials in another context, where also the uniforms are not set correctly, depending on the order of the objects. I believe that this could be related to above issue, i.e. having the same underlying cause of error. Luckily, I was able to create a minimal example here: Crash if using OutlinePass in a scene with node materials and standard materials · Issue #27095 · mrdoob/three.js · GitHub
Might make sense to check this out.

(In this other case case we have multiple objects in the scene, some with node materials and some with classic materials.

Objects can be selected by the user, selected objects are rendered with the OutlinePass.
Selection works if the first object selected is an object with classic material, afterwards also objects with node materials can be selected. If the first selected object happens to have a node material, it crashes.

If setting outlinePass.prepareMaskMaterial.lights = true it behaves vice-versa: I.e. it works if the first object selected has a node material, afterwards also objects with classical materials can be selected. But if the first object has a classical material it again crashes.)

This issue should be fixed with the next release r159. Related PR:

1 Like