2
\$\begingroup\$

Let's say, that I have 3 programs, and in each of those programs there is a view matrix uniform, which should be the same in all those programs.

Right now, when my camera moves, I need to re-upload the modified matrix to every program separately. Is it possible to create some kind of global uniforms which are constant for all programs linked to it, so I could just upload the matrix once?

I tried creating a globalUniforms object which looked kinda like this:

var globalUniforms = {
    program: {},
    // (...)
    vMatrixUniform: null,
    // (...)
    initialize: function() {
        vMatrixUniform = gl.getUniformLocation(this.program, 'uVMatrix');
    }
};

So I could just link it to proper programs like this: program.vMatrixUniform = globalUniforms.vMatrixUniform;, and then pass the matrix like this:

if (camera.isDirty.viewMatrix !== false) { camera.isDirty.viewMatrix = false;
    gl.uniformMatrix4fv(globalUniforms.vMatrixUniform, false, camera.viewMatrix.element);
}

but unfortunately it throws an error:

Uncaught exception: gl.INVALID_VALUE was caused by call to: getUniformLocation called from line 272, column 2 in () in mysite/js/mesh.js: vMatrixUniform = gl.getUniformLocation(this.program, 'uVMatrix');

Summing up: is there a more efficient way of managing shaders which follows my logic?

\$\endgroup\$
2
  • 2
    \$\begingroup\$ I was going to tell you that this is the absolute best use-case scenario for using a Uniform Buffer Object, but then I realized the question was about WebGL :-\. In desktop GL, Uniform Buffers were created for multiple reasons: to allow storage of larger number of uniform components, to allow you to share uniform data between different programs and to make uniform storage general purpose. The 2nd of those 3 things would really help you here, you could bind a UBO to a certain global location and have each of your programs reference that binding location. \$\endgroup\$ Commented Jun 6, 2014 at 22:32
  • \$\begingroup\$ That said, the only reason GL_INVALID_VALUE would pop up is if this.program was invalid. \$\endgroup\$ Commented Jun 6, 2014 at 22:34

2 Answers 2

2
\$\begingroup\$

You can make an analogy of a GL shader program to a program running on a desktop PC. It is natural to expect that it should have its own private "memory space" for variables. This is necessary for several reasons, such as avoiding name collisions and accidental/erroneous memory reads/writes. So you will not be able to share uniform variables between programs.

You can, of course, keep a single CPU-side copy of your uVMatrix, but each program will have to declare a uVMatrix uniform and you will have to update all of then when the CPU-side variable changes.

One exception to this would be, like Andon M. Coleman commented, to use a Uniform Buffer Object (UBO). UBOs provide a very nice way of sharing memory between shader programs. They use the same framework of Vertex Buffers and the like, but unfortunately they are not available for WebGL yet. So we are stuck with the duplicate uniform variables until further updates of WebGL start to provide support for UBOs.

\$\endgroup\$
2
  • \$\begingroup\$ Could you introduce me to CPU-side variables a little bit more? Cause this is probably the first time I consciously hear about it and I cannot find any good source of information which mainly refers to this phenomena. \$\endgroup\$
    – Winged
    Commented Jun 8, 2014 at 7:35
  • \$\begingroup\$ Oh, I'm sorry if I caused a misunderstanding. By "CPU-side" I meant a variable that resides in RAM and is accessed by the CPU, e.g. a var in your Javascript code. While a uniform variable resides, most likely, in GPU memory and is accessed by the graphics processor, making it fair to call it a "GPU-side" variable. \$\endgroup\$
    – glampert
    Commented Jun 8, 2014 at 18:31
1
\$\begingroup\$

You can hack around this by abusing vertex attributes.

Say you have a matrix declared as follows (I'm not using JS here but you should be able to follow):

float myMatrix[4][4];

Instead of sending it as a uniform to each program object, you send it as vertex attributes like so:

glVertexAttrib4fv (13, myMatrix[0]);
glVertexAttrib4fv (14, myMatrix[1]);
glVertexAttrib4fv (15, myMatrix[2]);
glVertexAttrib4fv (16, myMatrix[3]);

(Per https://www.khronos.org/registry/webgl/specs/1.0/ this usage is supported in WebGL).

Then you define these input attributes in your GLSL, and rebuild the matrix from them. Now you can change program as often as you wish and - so long as you're consistent in that you use the same attribute slot numbers (via glBindAttribLocation) in each program - the matrix will be available to all of them.

A downside of this approach is that it uses up attribute slots, of which there are a limited number (typically 16), so you most definitely should not use it as a general-case workaround. But where you absolutely must share uniforms, this is one way of working around this limitation.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .