Three.js: Better Documentation for Structured Uniforms

Created on 8 Feb 2017  路  16Comments  路  Source: mrdoob/three.js

Description of the problem

By structured uniform, I mean a uniform of the form:

struct parameters {
    float scale, height, width, offset, baseline;
};
uniform parameters transition_model;

Currently, the uniform documentation only states that

Uniform structures and arrays are also supported.

but doesn't go into further detail on how to set such uniforms using three.js. I had to figure out the proper syntax by stepping through the source code. For future reference, the syntax is:

material.uniforms = {
    transition_model: { value: {
        scale: pScale,
        height: pHeight,
        width: pWidth,
        offset: pOffset,
        baseline: pBaseline
    }
}

This old issue thread is the only reference I could find on structured uniforms in three.js. Moreover, said issue doesn't conclude on a single syntax (nor is the correct syntax even mentioned).

It would be nice to have this topic covered in the official documentation. Just like there already is a nice example of regular (non-structured) uniform usage.

Three.js version
  • [ ] Dev
  • [x] r84
  • [ ] ...
Browser
  • [x] All of them
  • [ ] Chrome
  • [ ] Firefox
  • [ ] Internet Explorer
OS
  • [x] All of them
  • [ ] Windows
  • [ ] Linux
  • [ ] Android
  • [ ] IOS
Hardware Requirements (graphics card, VR Device, ...)
Documentation Enhancement

All 16 comments

/cc @looeee

Yeah, improving the docs for ShaderMaterial and RawShaderMaterial is on my list! It'll probably be a couple of weeks before I have time to do it though. In the meantime @frederikaalund if you want to add this yourself that would be great!

@looeee
Would you be able to provide a small example of how to pass an array of structured uniforms? It seems that it is still not documented, and I couldn't find its example for the latest version (r84).

@hayatoikoma - read the first comment in this thread by @frederikaalund. He describes how to do it there.

Sure. I understood how we can pass a set of uniforms as a struct to a shader from the first comment, but I don't know how I can pass an array of a struct. For example, I would like to pass an array of transition_model to a shader. It would be similar to how multiple light sources are passed to a shader, but I still haven't been able to figure out how I can achieve it.

InUniformsLib.js, such array of structured uniforms are defined like

    directionalLights: { value: [], properties: {
        direction: {},
        color: {},

        shadow: {},
        shadowBias: {},
        shadowRadius: {},
        shadowMapSize: {}
    } },

Would you provide an example of how I can pass actual values to directionalLights.value? Should I provide any values to directionalLights.properties?

Ah I see. I'm afraid I don't know the answer to that, but if you ask over on the forum, discourse.threejs.org, or on stackoverflow someone might help you.

I was finally able to find a way by myself. For example,

uniforms.directionalLights = { value: [], properties: {
    direction: {},
    color: {},
} };

var dLight0;
dLight0.direction = new THREE.Vector3( 0, 1, 0 );
dLight0.color = new THREE.Color();
uniforms.directionalLights.value.push( dLight0 );

var dLight1;
dLight1.direction = new THREE.Vector3( 1, 0, 0 );
dLight1.color = new THREE.Color();
uniforms.directionalLights.value.push( dLight1 );

worked to parse an array of structured uniforms to a shader. I can add it to the documentation if it is appropriate.

I am also thinking that it would be great to have this functionality to Uniform.js. If somebody can confirm that my understanding is correct and it is worth doing, I would be happy to extend Uniform.js to support structured uniforms and its array.

worked to parse an array of structured uniforms to a shader. I can add it to the documentation if it is appropriate.

That'd be great!

I am also thinking that it would be great to have this functionality to Uniform.js. If somebody can confirm that my understanding is correct and it is worth doing, I would be happy to extend Uniform.js to support structured uniforms and its array.

/ping @bhouston

Yikes. While I wrote the original version of structured uniforms it was refactor by @tschw and I'd have to read through the code to figure it out. Sorry, I am not any more help that you guys reading through it as well.

@bhouston I see. Thank you.

Would anyone let me know which file I should read through? Are there any unit tests to check this functionality?

@hayatoikoma I think it would be sufficient to add an example of passing a struct, and an example of passing an array of structs, to https://threejs.org/docs/?q=unifor#Reference/Core/Uniform.

Thanks for the ping, Ben!

Actually it was more than just a refactor, but in fact a completion. It handles any GLSL uniform you can declare, as long as you resemble the same structure in JavaScript.

The docs on structured uniforms (including multidimensional arrays) read:

Uniform structures and arrays are also supported. GLSL arrays of primitive type must either be
specified as an array of the corresponding THREE objects or as a flat array containing the data
of all the objects. In other words; GLSL primitives in arrays must not be represented by arrays.
This rule does not apply transitively.

An array of vec2 arrays, each with a length of five vectors, must be an array of arrays, of
either five Vector2 objects or ten numbers.

And some types are marked with (*) which means:

(*) Same for an (innermost) array (dimension) of the same GLSL type, containing the components
of all vectors or matrices in the array.

This is rather detailed.

Put bluntly, you either have to go "completely high level" or "flat" for the innermost array (where a one-dimensional array is always "innermost" - the "flat" way is a direct pass-through to WebGL / OpenGL ES2, see specs for details).

The rest is just like you'd expect: Structure members are mapped to object properties (which is pretty much the JS equivalent) and arrays to arrays. Wouldn't hurt to state that explicitly, I guess, but since there are no restrictions except those imposed by GLSL, there's really not much more to say about it.

For the .properties vs. .value story, I honestly have no clue. I never had anything to do with .properties.

I remember .value being kind-of historical leftover, because we didn't want to break existing code, and also because there's the optional .needsUpdate...

@tschw
Thank you for the detailed explanation. It actually worked without .properties. I was expecting that .properties was used for checking if all elements in a JS array have the same properties.

It seems that .properties was introduced in https://github.com/mrdoob/three.js/commit/7985c8b0abf91dc81eb528756f6692abb43cbee3?diff=split by @bhouston and became unnecessary at https://github.com/mrdoob/three.js/commit/494e016deacab56a9edc9b4eca48697c4b9afd4d?diff=split by @tschw .

I understood that the description describes every types supported in Three.js, but I guess it would be more informative to write some examples as I couldn't figure it out easily. As a matter of fact, the only example I found for an array of structured uniforms in UniformsLib.js seems the old way.

So, is it safe to provide example like the following code?

uniforms.directionalLights = { value: [] };

var dLight0;
dLight0.direction = new THREE.Vector3( 0, 1, 0 );
dLight0.color = new THREE.Color();
uniforms.directionalLights.value.push( dLight0 );

var dLight1;
dLight1.direction = new THREE.Vector3( 1, 0, 0 );
dLight1.color = new THREE.Color();
uniforms.directionalLights.value.push( dLight1 );

What would happen if some of the elements does not have the same properties? As I still have questions on the behaviors and don't understand the implementation details, I may not be qualified to write more examples...

So, is it safe to provide example like the following code?

Yes, that's basically how it works.

What would happen if some of the elements does not have the same properties?

I don't remember - it's not a good idea on the JS end (heterogeneous arrays poison JIT compilation) and diversity of that kind lets suspect a chaotic programming style, so I wouldn't promote it in the docs and, due to the performance critical nature of uniform upload with WebGL (version 1), it's probably best left undefined behavior.

Actually, I never indented this interface to be the final thing, since uniforms do not belong onto a scene graph. Similarly, materials are not the same as shaders. Instead the data that goes into a uniform is either the property of an object or its associated material, or something managed by the renderer (e.g. camera or lights that can reach the object). A custom shader would declare those bindings, but not the associated data. Multiple materials can share the same shader, while the renderer would manage its multiple instances that need to be compiled for everything to just work.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

konijn picture konijn  路  3Comments

boyravikumar picture boyravikumar  路  3Comments

clawconduce picture clawconduce  路  3Comments

jack-jun picture jack-jun  路  3Comments

donmccurdy picture donmccurdy  路  3Comments