Passing arrays of off-length to gl.uniform3fv - javascript

It seems that when using WebGL uniform3fv you MUST pass an array of length equal to 3 if your shader uniform is vec3 OR an array having length equal to multiple of 3 if your shader uniform is an array of vec3's.
Doing this:
var data = new Float32Array([ 1, 2, 3, 4 ]);
gl.uniform3fv( uniformLocation, data );
when your uniform is declared as:
uniform vec3 some_uniform;
will result in some_uniform getting (0,0,0) value.
I searched the web and SO and MDN and forums and stuff (one of the stuff being WebGL specification) and I can't find a requirement (or mention) for this limitation.
My question is: is this required by WebGL specification (and if yes, can you please point me to it) or is it just some undocumented behaviour you are supposed to know about?
If it is required, we'll change code to support it as requirement, if it is undocumented quirk, we'll change code to support that quirk with an option to disable/remove the support once the quirk is gone.

From the WebGL spec section 5.14.10
If the array passed to any of the vector forms (those ending in v) has an invalid length, an INVALID_VALUE error will be generated. The length is invalid if it is too short for or is not an integer multiple of the assigned type.
Did you check your JavaScript console? When I tried it I clearly saw the INVALID_VALUE error in the console.
The code below
const gl = document.querySelector('canvas').getContext('webgl');
const vs = `
attribute vec4 position;
void main() {
gl_Position = position;
}
`;
const fs = `
precision mediump float;
uniform vec3 color;
void main() {
gl_FragColor = vec4(color, 1);
}
`;
const program = twgl.createProgram(gl, [vs, fs]);
const loc = gl.getUniformLocation(program, 'color');
gl.useProgram(program);
gl.uniform3fv(loc, [1, 2, 3, 4]); // generates INVALID_VALUE
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>
prints
in the JavaScript console
In other words, your uniform was not set to 0,0,0. Instead your gl.uniform3fv function failed to execute since it got an error and so the uniform was left at whatever value it already was. Uniforms default to 0 so that's where the 0s came from
If you want to catch this kind of error during debugging consider using this helper library. Generally I find just looking at the JavaScript console is enough for me to figure out where the issue though.

Related

WebGL not displaying content

i am mainly developing in C++ and decided to compile one of my projects to WebAssembly and build a website upon it. Since I have written some 3D-Engine in C++ before, I decided to use WebGL on the website. I have translated my shader template class. I have reduced the problem to a two-dimensional problem.
Task
First, I will describe what I am trying to do.
I am going to render a 2D FEM-grid which consists of FEM-elements which is any type of polygon. The nodes of those polygons contain values which I am trying to display. I already wrote code to break the polygons down to triangles. Initially I am just trying to render two dimensional triangles with their node values being interpolated.
Shader Code
I wrote some template shader class in WebGL which handles the construction and compilation of the shaders and has been copied from my C++ 3D-Engine. Since the code itself is somewhat long, I am not going to post it here but eventually show a list of executed OpenGL commands.
The shaders I am currently using to debug my problem are the following:
Vertex-Shader:
precision mediump float;
attribute vec2 position;
varying vec2 fragPos;
void main(){
gl_Position = vec4(position,0,1.0);
fragPos = position;
}
---------------------------------------------------------------
Fragment-Shader:
precision mediump float;
varying vec2 fragPos;
uniform sampler2D elementValues;
uniform int elementValuesDimension;
void main(){
gl_FragColor = vec4(0.8,0.2,0.0,1.0);
}
As you can see, I am not going for any interpolation in this debug case but just trying to some red-ish color within my fragment shader.
Executed Commands
I went ahead and used webgl-debug.js to show all the operations done. For this case, you can see the vertices and indices matching a simple quad spanning [0,0.6]x[0,0.6] which should be well within the clip space.
This can be broken into a few parts:
Enable uint32 as indexing
gl.getExtension(OES_element_index_uint)
Create two buffers for vertex coordinates and indices
gl.createBuffer()
gl.createBuffer()
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, [object WebGLBuffer])
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 0,1,2,0,2,3, gl.STATIC_DRAW)
gl.bindBuffer(gl.ARRAY_BUFFER, [object WebGLBuffer])
gl.bufferData(gl.ARRAY_BUFFER, 0,0,0,0.6000000238418579,0.6000000238418579,0.6000000238418579,0.6000000238418579,0, gl.STATIC_DRAW)
Create and compile the shaders
gl.createShader(gl.VERTEX_SHADER)
gl.shaderSource(vertex_shader_src);
gl.compileShader([object WebGLShader]);
gl.getShaderParameter([object WebGLShader], gl.COMPILE_STATUS);
gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragment_shader_src);
gl.compileShader([object WebGLShader]);
gl.getShaderParameter([object WebGLShader], gl.COMPILE_STATUS);
gl.createProgram()
gl.attachShader([object WebGLProgram], [object WebGLShader])
gl.attachShader([object WebGLProgram], [object WebGLShader])
gl.linkProgram([object WebGLProgram])
gl.validateProgram([object WebGLProgram])
Get Locations of the uniforms (which are unused) as well as the location of the attribute
gl.getUniformLocation([object WebGLProgram], elementValues)
gl.getUniformLocation([object WebGLProgram], elementValuesDimension)
gl.getAttribLocation([object WebGLProgram], position)
Verify that linking has worked
gl.getProgramParameter([object WebGLProgram], gl.LINK_STATUS)
Main Rendering Loop. Initially clear the displayed stuff
gl.clearColor(0.5, 0.5, 0.5, 1)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gl.disable(gl.CULL_FACE)
gl.viewport(0, 0, 700, 600)
Activate the shader and bind the buffers
gl.useProgram([object WebGLProgram])
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, [object WebGLBuffer])
gl.bindBuffer(gl.ARRAY_BUFFER, [object WebGLBuffer])
Connect the vertices to the first attribute
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0)
Render Call.
gl.drawElements(gl.TRIANGLES, 4, gl.UNSIGNED_INT, 0)
Sadly when the commands above are executed, I am unable to see anything except a gray screen:
I am very happy for any help!
Greetings
Finn
Thanks to teddybeard and a lot more time on this problem spent than I was hoping, two issues have shown up:
l.bufferData(gl.ELEMENT_ARRAY_BUFFER, 0,1,2,0,2,3, gl.STATIC_DRAW) contains 6 vertices but the drawElements command only took 4 values. This is obviously a bug but should still have rendered atleast one of the two triangles.
Secondly, I forgot to enable the vertex attribute in the shader. gl.enableVertexAttribArray(1); solved the problem.

webgl getUniformLocation returns 0

Put succinctly, I've hit the problem where calls for different uniforms via getUniformLocation all seem to return the same value.
It's hard to validate this since the returned Glint is opaque to the javascript console. However when looking at someone else's code sample I noticed you can pass standard javascript numbers in it's place hence this trial code:
const infoA = gl.getActiveUniform(program, gl.getUniformLocation(program, 'uSampler'));
const infoB = gl.getActiveUniform(program, gl.getUniformLocation(program, 'uRotationMatrix'));
const infoC = gl.getActiveUniform(program, 0);
const infoD = gl.getActiveUniform(program, 1);
infoA and infoB both equal the WebGLActiveInfo object for the 'uRotationMatrix' uniform, as does infoC, but infoDreturns the info data for 'uSampler'.
The closest I can find to similar questions is about optimization removing unused uniforms resulting in getUniformLocation always returning -1. I don't believe that is the case here, since both uniforms are used and using the webgl-inspector chrome extension by Ben Vanik https://github.com/benvanik/WebGL-Inspector , I see both uniforms listed in the Program panel with idx values 0 and 1. However I did note that providing an invalid uniform name produced no error and resulted in a 'default' return value of an info object for 'uRotationMatrix' (infoE); using just -1resulted in an error (infoF).
const infoE = gl.getActiveUniform(program, gl.getUniformLocation(program, 'INVALID_NAME');
const infoF = gl.getActiveUniform(program, -1); // null
Interestingly the results in Safari are reversed, that is the majority of calls return the info object for 'uSampler' while only explicitly using a javascript number, returns the info object for 'uRotationMatrix'
The shaders are below and pretty simple, both they and the program I linked them in returned success when the relevant paramters were inspected. i.e.
gl.getShaderParameter(shader, gl.COMPILE_STATUS);
gl.getProgramParameter(program, gl.LINK_STATUS);
Vertex.
precision mediump float;
attribute vec2 aPosition;
attribute vec2 aTexCoord;
uniform mat4 uRotationMatrix;
varying vec2 fragTexCoord;
void main() {
fragTexCoord = aTexCoord;
gl_Position = uRotationMatrix * vec4(aPosition, 0.0, 1.0);
}
Fragment.
precision mediump float;
varying vec2 fragTexCoord;
uniform sampler2D uSampler;
void main() {
vec4 sample = texture2D(uSampler, fragTexCoord);
gl_FragColor = vec4(sample.rgb, 1.0);
}
Does anyone have any pointers for where I should be looking to track down the problem?
Edit:
In reference to making sure the parameter type and return value types are compatible, via the MDN documentation for the pertinent functions.
"location: A GLuint specifying the index of the uniform attribute to get. This value is returned by getUniformLocation()." Link: getActiveUniform
and
"Return value: A WebGLUniformLocation value indicating the location of the named variable, ... The WebGLUniformLocation type is compatible with the GLint type when specifying the index or location of a uniform attribute." Link: getUniformLocation
gl.getUniformLocation does not return -1 for non-existent uniforms. It returns null
This code makes no sense
const infoA = gl.getActiveUniform(program, gl.getUniformLocation(program, 'uSampler'));
const infoB = gl.getActiveUniform(program, gl.getUniformLocation(program, 'uRotationMatrix'));
gl.getActiveUniform requires an integer. gl.getUniformLocation returns WebGLUniformLocation object, not an integer and it cannot be converted into a integer. At best it's getting converted into NaN and NaN is getting converted into 0.
gl.getActiveUniform does not take uniform locations, it takes a number from 0 to N - 1 where N is returned from gl.getProgramParameter(prg, gl.ACTIVE_UNIFORMS). It's purpose is to allow you to query the uniforms without first knowing their names.
const numUniforms = gl.getProgramParameter(prg, gl.ACTIVE_UNIFORMS);
for (let i = 0; i < numUniforms; ++i) {
// get the name, type, and size of a uniform
const info = gl.getActiveUniform(prg, i);
// get the location of that uinform
const loc = gl.getUniformLocation(prg, info.name);
}
Note that the reason WebGL choose to have gl.getUniformLocation return a WebGLUniformLocation object instead of an int is because it's an common error to guess those ints or to assume they are consecutive. OpenGL makes no such guarantees.
Two programs with the same uniforms might have different locations for each uniform.
Those locations are not 0 and 1, they could be anything, 5323 and 23424. It's up to the driver. Various drivers return different numbers.
Similarly for uniforms arrays like uniform float foo[2], if the location of foo[0] is 37 that does not mean the location of foo[1] is 38.
For all these reasons WebGL chose to wrap the location. That way many of those mistakes can be avoided and/or checked for. You can't do math on a WebGLUniformLocation so the guessing a location error disappears (your guess might work locally but you're making a webpage that has to run on other GPUs). The erroneous uniform array math error is avoided. Similarly you can't use a WebGLUniformLocation from one program with a different program meaning the error of assuming 2 programs with the same uniforms will have the same int locations for those uniforms is avoided.
While we're on the topic of gl.getActiveUniform you should be aware it can return info for things that aren't uniforms. Example: https://jsfiddle.net/greggman/n6mzz6jv/

WebGL fragment-shader for multiple light sources?

I want to be able to append multiple light sources to each node of my scene graph, but I have no clue how to do it!
From the tutorials on learningwebgl.com I learned to either use directional or position lighting, but I couldn't find a good explanation of how to implement multiple light sources.
So, the objective should be, to have the option to append an arbitrary number of light sources to each node, which type can be either directional or position lighting, and if possible and advisable, this should be realized by using only one shader-program (if this isn't the only possibility to do it anyway), because I automatically create the program for each node depending on their specific needs (unless there is already a program on the stack with equal settings).
Based on the tutorials on learningwebgl.com, my fragment-shader source for a node object using lighting without preset binding to one of the lighting-types could look like this...
precision highp float;
uniform bool uUsePositionLighting;
uniform bool uUseDirectionalLighting;
uniform vec3 uLightPosition;
uniform vec3 uLightDirection;
uniform vec3 uAmbientColor;
uniform vec3 uDirectionalColor;
uniform float uAlpha;
varying vec4 vPosition;
varying vec3 vTransformedNormal;
varying vec3 vColor;
void main (void) {
float directionalLightWeighting;
if (uUseDirectionalLighting) {
directionalLightWeighting = max(dot(vTransformedNormal, uLightDirection), 0.0);
else if (uUsePositionLighting) {
vec3 lightDirection = normalize(uLightPosition, vPosition.xyz);
directionalLightWeighting = max(dot(normalize(vTransformedNormal, lightDirection), 0.0);
}
vec3 lightWeighting = uAmbientColor + uDirectionalColor * directionalLightWeighting;
gl_FragColor = vec4(vColor * lightWeighting, uAlpha);
}
...so, that's basically my poor state of knowledge concerning this subject.
I also ask myself, how adding more light sources would affect the lighting-colors:
I mean, do uAmbientColor and uDirectionalColor have to sum up to 1.0? In this case (and particularly when using more than one light source) it surely would be good to precalculate these values before passing them to the shader, wouldn't it?
Put your lights into an array and loop over them for each fragment. Start with a fixed array of light sources, unbounded arrays are not supported until OpenGL 4.3 and are more complicated to work with.
Something along the lines of:
uniform vec3 uLightPosition[16];
uniform vec3 uLightColor[16];
uniform vec3 uLightDirection[16];
uniform bool uLightIsDirectional[16];
....
void main(void) {
vec3 reflectedLightColor;
// Calculate incoming light for all light sources
for(int i = 0; i < 16; i++) {
vec3 lightDirection = normalize(uLightPosition[i], vPosition.xyz);
if (lightIsDirectional[i]) {
reflectedLightColor += max(dot(vTransformedNormal, uLightDirection[i]), 0.0) * uLightColor[i];
}
else {
reflectedLightColor += max(dot(normalize(vTransformedNormal, lightDirection), 0.0) * uLightColor[i];
}
}
glFragColor = vec4(uAmbientColor + reflectedLightColor * vColor, uAlpha);
}
Then you can enable/disable the light sources by setting uLightColor to (0,0,0) for the entries you don't use.
Ambient and directional don't have to sum up to 1, actually the light source can have a strength much stronger than 1.0, but then you will need to do tonemapping to get back to a range of values that can be displayed on a screen, I would suggest playing around to get a feel for what is happening (e.g. what happens when a light source have negative colors, or colors above 1.0?).
uAmbientColor is just a (poor) way to simulate light that has bounced several times in the scene. Otherwise things in shadow becomes completely black Which looks unrealistic.
Reflectance should typically be between 0 and 1 (in this example it would be the parts returned by the 'max' computations), otherwise a lightsource will get stronger when looked at via the material.
#ErikMan's answer is great, but may involve a lot of extra work on the part of the GPU since you're checking every light per fragment, which isn't strictly necessary.
Rather than an array, I'd suggest building a clip-space quadtree. (You can do this in a compute shader if it's supported by your target platform / GL version.)
A node might have a structure such as (pseudocode as my JS is rusty):
typedef struct
{
uint LightMask; /// bitmask - each light has a bit indicating whether it is active for this node. uint will allow for 32 lights.
bool IsLeaf;
} Node;
const uint maxLevels = 4;
const uint maxLeafCount = pow(4,maxLevels);
const uint maxNodeCount = (4 * maLeafCount - 1) / 3;
/// linear quadtree - node offset = 4 * parentIndex + childIndex;
Node tree[maxNodeCount];
When building the tree, just check each light's clip-space bounding box against the implicit node bounds. (Root goes from (-1,-1) to (+1,+1). Each child is half that size on each dimension. So, you don't really need to store node bounds.)
If the light touches the node, set a bit in Node.LightMask corresponding to the light. If the light completely contains the node, stop recursing. If it intersects the node, subdivide and continue.
In your fragment shader, find which leaf node contains your fragment, and apply all lights whose bit is set in the leaf node's mask.
You could also store your tree in a mipmap pyramid if you expect it to be dense.
Keep your tiles to a size that is a multiple of 32, preferably square.
vec2 minNodeSize = vec2(2.f / 32);
Now, if you have a small number of lights, this may be overkill. You would probably have to have a lot of lights to see any real performance benefit. Also, a normal loop may help reduce data divergence in your shader, and makes it easier to eliminate branching.
This is one way to implement a simple tiled renderer, and opens the door to having hundreds of lights.

How to cut an object using WebGL shaders?

I would like to cut an object (a box) in WebGL (fragment shaders / vertex shaders) without using Boolean operations (union, difference, etc..).
I want to use shaders to hide some part of the object (so it is therefore not really a "real cuts" since it simply hides the object).
EDIT
First, make sure that the vertex shader passes through to the fragment shader the position in world space (or rather, whichever coordinate space you wish the clipping to be fixed relative to). Example (written from memory, not tested):
varying vec3 positionForClip;
...
void main(void) {
...
vec4 worldPos = modelMatrix * vertexPosition;
positionForClip = worldPos.xyz / worldPos.w; // don't need homogeneous coordinates, so do the divide early
gl_Position = viewMatrix * worldPos;
}
And in your fragment shader, you can then discard based on an arbitrary plane, or any other kind of test you want:
varying vec3 positionForClip;
uniform vec3 planeNormal;
uniform float planeDistance;
...
void main(void) {
if (dot(positionForClip, planeNormal) > planeDistance) {
// or if (positionForClip.x > 10.0), or whatever
discard;
}
...
gl_FragColor = ...;
}
Note that using discard may cause a performance reduction as the GPU cannot optimize based on knowing that all fragments will be written.
Disclaimer: I haven't researched this myself, and only just wrote down a possible way to do it based on the 'obvious solution'. There may be better ways I haven't heard of.
Regarding your question about multiple objects: There are many different ways to handle this — it's all custom code in the end. But you certainly can use a different shader for different objects in your scene, as long as they're in different vertex arrays.
gl.useProgram(programWhichCuts);
gl.drawArrays();
gl.useProgram(programWhichDoesNotCut);
gl.drawArrays();
If you're new to using multiple programs, it's pretty much just like using one program except that you do all the setup (compile, attach, link) once. The main thing to watch out for is each program has its own uniforms, so you have to initialize your uniforms for each program separately.

WebGL Weird Performance Drop in Chrome

I have been trying to learn WebGL so I have made a simple model loading script. I thought it was working all fine and dandy until I pulled up Chrome's devtools timeline and saw that I was getting ~20 FPS when rendering 100 models. The weird part was, the CPU was idle most of the time on each frame.
My first thought was that I must be GPU bound, which seemed unlikely because my shaders were short and not very complex. I tested with 10 models and again with only one, and that did increase performance, but I was still getting less than 40 FPS. I pulled up chrome://tracing, and saw that both my GPU wasn't doing much for each frame either. The majority of each frame in CrGpuMain was filled with "Processing Swap", and CrBrowserMain spent nearly all of the time on glFinish (called by "CompositingIOSurfaceMac::DrawIOSurface").
Processing Swap appears to be the major problem, not glFinish.
So my question is, what is causing the browser to spend so much time on glFinish? Any ideas on how to fix this problem?
Each model has 40608 elements and 20805 vertices,
Update - Corrected numbers of simplified mesh:
3140 elements
6091 vertices
The models are pseudo-instanced so data is only passed to the GPU once per frame. Is the large number of vertices the problem?
The demo can be found here.
Rendering Code:
Thing.prototype.renderInstances = function(){
if (this.loaded){
var instance;
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexbuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this.normalbuffer);
gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, 3, gl.FLOAT, true, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexbuffer);
for(var i in this.instances){
instance = this.instances[i];
setMatrixUniforms(instance.matrix);
gl.drawElements(gl.TRIANGLES, this.numItems, gl.UNSIGNED_SHORT,0);
}
}
};
Vertex Shader Code:
attribute vec3 aVertexPosition;
attribute vec3 aVertexNormal;
uniform mat4 uMMatrix;
uniform mat4 uVMatrix;
uniform mat4 uPMatrix;
uniform vec3 uLightDirection;
varying float vLight;
void main(void) {
mat4 MVMatrix = uVMatrix*uMMatrix;
//Toji's manual transpose
mat3 normalMatrix = mat3(MVMatrix[0][0], MVMatrix[1][0], MVMatrix[2][0], MVMatrix[0][1], MVMatrix[1][1], MVMatrix[2][1], MVMatrix[0][2], MVMatrix[1][2], MVMatrix[2][2]);
gl_Position = uPMatrix*uVMatrix*uMMatrix*vec4(aVertexPosition, 1.0);
vec3 lightDirection = uLightDirection*normalMatrix;
vec3 normal = normalize(aVertexNormal*normalMatrix);
vLight = max(dot(aVertexNormal,uLightDirection),0.0);
}

Categories