Exception thrown in StructuredUniform.setValue()

I’ve been struggling for the last 6 months or so with a number of lighting bugs. The most serious of these is an exception thrown in StructuredUniform.setValue() with the following stack trace:

at StructuredUniform.setValue (three.module.js:18641:20)
    at StructuredUniform.setValue (three.module.js:18641:6)
    at WebGLUniforms.upload (three.module.js:18770:7)
    at setProgram (three.module.js:29423:19)
    at WebGLRenderer.renderBufferDirect (three.module.js:28302:20)
    at renderObject (three.module.js:28973:11)
    at renderObjects (three.module.js:28942:6)
    at renderScene (three.module.js:28830:36)
    at WebGLRenderer.render (three.module.js:28644:5)
    at Engine.render (Engine.ts:235:19)

From what I can tell in the debugger, it’s attempting to set a uniform which is a zero-length array, and accessing element 0, which produces an undefined - and then tries to dereference that undefined.

The bug only happens when I use two renderers - one to render character portraits to an offscreen render target, followed by a render of the main window. The bug does not manifest if I render only one or the other. It appears that somehow the render to the offscreen render target is messing up the uniforms for the main view.

The bug also only happens with a specific shader, however I have checked and rechecked the shader code many times and I don’t see anything in it which would cause this to happen. And as mentioned, shader works fine if I’m only rendering to a single target.

I know of at least two other lighting bugs related to shadow maps and rendering multiple scenes, specifically with portals. However, those are harder to set up a repro case for, and I’m hoping that if this bug gets solved it may fix the other bugs as well.

Reproduction steps

The bug is 100% reproducible in my game engine, but very difficult to reproduce in a small program. So what I have done is spent the last couple days whittling down my game engine from 70K lines of code to just under a hundred source files, with all the game assets hard-coded. I know a hundred seems like a lot, but there’s only 3 files that need to be looked at, the rest can be ignored.

The source can be found in this github repo: GitHub - viridia/three-lighting-bug

To reproduce the bug, git clone the repo, and then:

npm install
npm run build
cd packages/los-editor
npm start

Then navigate to http://localhost:3000/. The code has been set up to trigger the bug after 1 second - tht is, it renders to a single target for one second, and then renders to a second target, which causes the crash.

As mentioned, most of the sources can be ignored. The only ones you should need to look at are:

  • packages/engine/src/world/actors/SkinnedModel.ts
  • packages/engine/src/materials/HairMaterial.ts
  • packages/engine/src/Engine.ts

In the Engine.ts file, you will find a section called ‘fixLightsHack’ - this is a workaround that avoids the crash, but causes a loss of framerate. Basically what the workaround does is globally disable shadows, do an extra render pass, and then re-enable shadows and then render again. Somehow this causes the uniform data to go back into sync.

(I’m posting this here because when I opened a github ticket I was asked to post on the forum instead.)

I’m continuing to investigate this, still haven’t figured out what is causing the crash.

One thing I have learned is that adding dummy point lights into the scene, so that the main view and the offscreen render target have the same number of point lights, will prevent the crash in the sample program. Unfortunately, this doesn’t prevent the crash in the full game.

Disabling lights in the custom shader (by setting lights: false in the constructor parameter to ShaderMaterial) also prevents the crash.

A summary of what I know so far:

  • The crash occurs inside the three.js code for uploading uniforms.
  • The specific error is attempting to dereference a uniform value which is undefined.
  • The specific uniform being uploaded varies, but is always something related to lighting - such as directionalShadowMatrix or pointLights.
  • The crash only occurs when lighting is enabled.
  • The crash only occurs when rendering multiple scenes. For the sample, I’m using a two separate renderers.
  • I’m not sharing materials between rendering contexts (I’m not sure whether there’s a prohibition against this or not, but I am erring on the safe side.)
  • Doing a dummy render pass with shadows disabled between the two renders prevents the bug from occurring.

I don’t understand the internals of three.js well enough to form a coherent theory, but my best guess is that there’s some shared state between the two renderers, which is not getting reset, and thus causing the uniform values to get messed up somehow.

I have managed to strip down the reproducing example to just 2 source files, here’s a stackblitz link which shows the problem: Three.js lighting bug - StackBlitz (see the console output for the exception stack trace).

I don’t do ts, but probably because your HairMaterial uses UniformsLib.lights and that contains a structure for pointLights, so if you use HairMaterial, you have to have point lights in your scene.

In other words, you compile a shader program that actively uses a structure for point lights but you do not provide any.

I’ve considered this. However, there are three reasons why I don’t think this is the case.

  1. this is exactly what the builtin phong material does, and it doesn’t appear to have this problem. HairMaterial really isn’t much different from the builtin phong, especially since most of the guts of it have been removed in the interest of simplifying the example.

  2. I don’t get a crash if I am only using a single renderer instances, regardless of whether I have point lights or not. HairMaterial was one of the first shaders I wrote, 3 years ago, and this was long before I started using either point lights or multiple renderers, both of which are relatively recent developments.

  3. if you examine the ShaderChunks source files in the three.js source code, you will see that all of the code dealing with lights has #if guards to prevent the code from being included if there are no point lights:

#if NUM_POINT_LIGHTS > 0

	struct PointLight {
		vec3 position;
		vec3 color;
		float distance;
		float decay;
	};

I’d go for the throat - insert debugger statement in THREE render loop, that will allow you to see rendering of what object causes the error, you can also see what web program is in use with the geometry. The console error gives you the call stack, so you can see where to put debuggers.

That will give you the clear answer to you question, then you can trace it back to a higher level code and figure out why it is happening.

1 Like