drawElements use - webgl

I create a little application in WebGL, I have two objects which move, a cube and a sphere.
I modify objects with shaders, each object have it shader. So I want update objects on the display at determine time, for this I use drawElements function.
For each object I have a buffer which contains indices of faces in vertices buffer :
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STREAM_DRAW);
indexCount = indices.length;
indices is an array which contains values of indices of each faces. (3 values per face, we work with triangles).
So, after this, for draw triangles of the object I do :
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_SHORT, 0);
But I have nothing on the screen, and I have this warning :
WebGL: INVALID_OPERATION: drawElements: attribs not setup correctly
What's could be my error ?
Thanks

I think you missed codes which link javascript vertex buffer to shader's attributes before drawElements().
e.g:
gl.bindBuffer(gl.ARRAY_BUFFER, meshVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,
meshVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshIndexBuffer);
gl.drawElements(gl.TRIANGLES, meshIndexBuffer.numberOfItems, gl.UNSIGNED_SHORT, 0);

Related

Draw multiple models in WebGL

Problem constraints:
I am not using three.js or similar, but pure WebGL
WebGL 2 is not an option either
I have a couple of models loaded stored as Vertices and Normals arrays (coming from an STL reader).
So far there is no problem when both models are the same size. Whenever I load 2 different models, an error message is shown in the browser:
WebGL: INVALID_OPERATION: drawArrays: attempt to access out of bounds arrays so I suspect I am not manipulating multiple buffers correctly.
The models are loaded using the following typescript method:
public AddModel(model: Model)
{
this.models.push(model);
model.VertexBuffer = this.gl.createBuffer();
model.NormalsBuffer = this.gl.createBuffer();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, model.VertexBuffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, model.Vertices, this.gl.STATIC_DRAW);
model.CoordLocation = this.gl.getAttribLocation(this.shaderProgram, "coordinates");
this.gl.vertexAttribPointer(model.CoordLocation, 3, this.gl.FLOAT, false, 0, 0);
this.gl.enableVertexAttribArray(model.CoordLocation);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, model.NormalsBuffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, model.Normals, this.gl.STATIC_DRAW);
model.NormalLocation = this.gl.getAttribLocation(this.shaderProgram, "vertexNormal");
this.gl.vertexAttribPointer(model.NormalLocation, 3, this.gl.FLOAT, false, 0, 0);
this.gl.enableVertexAttribArray(model.NormalLocation);
}
After loaded, the Render method is called for drawing all loaded models:
public Render(viewMatrix: Matrix4, perspective: Matrix4)
{
this.gl.uniformMatrix4fv(this.viewRef, false, viewMatrix);
this.gl.uniformMatrix4fv(this.perspectiveRef, false, perspective);
this.gl.uniformMatrix4fv(this.normalTransformRef, false, viewMatrix.NormalMatrix());
// Clear the canvas
this.gl.clearColor(0, 0, 0, 0);
this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
// Draw the triangles
if (this.models.length > 0)
{
for (var i = 0; i < this.models.length; i++)
{
var model = this.models[i];
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, model.VertexBuffer);
this.gl.enableVertexAttribArray(model.NormalLocation);
this.gl.enableVertexAttribArray(model.CoordLocation);
this.gl.vertexAttribPointer(model.CoordLocation, 3, this.gl.FLOAT, false, 0, 0);
this.gl.uniformMatrix4fv(this.modelRef, false, model.TransformMatrix);
this.gl.uniform3fv(this.materialdiffuseRef, model.Color.AsVec3());
this.gl.drawArrays(this.gl.TRIANGLES, 0, model.TrianglesCount);
}
}
}
One model works just fine. Two cloned models also work OK. Different models fail with the error mentioned.
What am I missing?
The normal way to use WebGL
At init time
for each shader program
create and compile vertex shader
create and compile fragment shader
create program, attach shaders, link program
for each model
for each type of vertex data (positions, normal, color, texcoord
create a buffer
copy data to buffer
create textures
Then at render time
for each model
use shader program appropriate for model
bind buffers, enable and setup attributes
bind textures and set uniforms
call drawArrays or drawElements
But looking at your code it's binding buffers, and enabling and setting up attributes at init time instead of render time.
Maybe see this article and this one

glDrawArrays is bound to single texture

I have a number of textures loaded using GLKTextureLoader. If I bind any of the loaded textures statically, each texture works as expected.
But I am trying to bind a random texture each glDrawArrays call, but the texture bound is always the same.
GLuint vbo = vboIDs[emitterNum];
GLKMatrix4 projectionMatrix = GLKMatrix4MakeScale(1.0f, aspectRatio, 1.0f);
glUseProgram(emitterShader[emitterNum].program);
glEnable(GL_TEXTURE_2D);
glActiveTexture (GL_TEXTURE0);
//Note: valid texture names are 0-31, but in my code I store texture names returned in an array and use them. Use arc4random here for simplicity
glBindTexture(GL_TEXTURE_2D, arc4random_uniform(31)); //use a random texture name
//glBindTexture(GL_TEXTURE_2D, 2); //If I use this line instead of the line above, it will draw texture 2, or any number I specify
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glUniformMatrix4fv(emitterShader[emitterNum].uProjectionMatrix, 1, 0, projectionMatrix.m);
//I set a number of uniforms such as:
glUniform1f(emitterShader[emitterNum].uTime, timeCurrentFrame);
glUniform1i(emitterShader[emitterNum].uTexture, 0);
//I set a number of vertex arrays such as:
glEnableVertexAttribArray(emitterShader[emitterNum].aShade);
glVertexAttribPointer(emitterShader[emitterNum].aShade, // Set pointer
4, // four components per particle (vec4)
GL_FLOAT, // Data is floating point type
GL_FALSE, // No fixed point scaling
sizeof(Particles), // No gaps in data
(void*)(offsetof(Particles, shade))); // Start from "shade" offset within bound buffer
GLsizei rowsToUse = emitters[emitterNum]->rows;
//Draw the arrays
glDrawArrays(GL_POINTS, 0, rowsToUse );
//Then clean up
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glDisable(GL_TEXTURE_2D);
glBindBuffer(GL_ARRAY_BUFFER, 0);
I have tried putting the texture calls in various places, like where shown and direct before the glDrawArrays command, but no matter what - I can't make it bind to different textures unless done so statically.

OpenGL ES 2.0 iOS - draw a rectangle into stencil buffer and limit drawing only inside it

Do a good deed and help get someone (me) out of their misery, since it's New Year's Eve soon. I'm working on an iOS app, a coloring book for kids and I haven't stumbled upon OpenGL before (more precisely OpenGLES 2.0) so there's a big chance there's stuff I don't actually get in my code.
One of the tasks is to not let the brush spill out of the contour in which the user started drawing.
After reading and understanding some OpenGL basics, I found that using the stencil buffer is the right solution. This is my stencil buffer setup:
glClearStencil(0);
//clear the stencil
glClear(GL_STENCIL_BUFFER_BIT);
//disable writing to color buffer
glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
//disable depth buffer
glDisable(GL_DEPTH_TEST);
//enable writing to stencil buffer
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NEVER, 1, 0xFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
[self drawStencil];
//re-enable color buffer
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
//only draw where there is a 1
glStencilFunc(GL_EQUAL, 1, 1);
//keep the pixels in the stencil buffer
glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
Right now, I'm just trying to draw a square in the stencil buffer and see if I can limit my drawing only to that square. This is the method drawing the square:
- (void)drawStencil
{
// Create a renderbuffer
GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];
// Create a framebuffer
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuffer);
// Clear
glClearColor(1, 1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
// Read vertex shader source
NSString *vertexShaderSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"VertexShader" ofType:#"vsh"] encoding:NSUTF8StringEncoding error:nil];
const char *vertexShaderSourceCString = [vertexShaderSource cStringUsingEncoding:NSUTF8StringEncoding];
// Create and compile vertex shader
GLuint _vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(_vertexShader, 1, &vertexShaderSourceCString, NULL);
glCompileShader(_vertexShader);
// Read fragment shader source
NSString *fragmentShaderSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"FragmentShader" ofType:#"fsh"] encoding:NSUTF8StringEncoding error:nil];
const char *fragmentShaderSourceCString = [fragmentShaderSource cStringUsingEncoding:NSUTF8StringEncoding];
// Create and compile fragment shader
GLuint _fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(_fragmentShader, 1, &fragmentShaderSourceCString, NULL);
glCompileShader(_fragmentShader);
// Create and link program
GLuint program = glCreateProgram();
glAttachShader(program, _vertexShader);
glAttachShader(program, _fragmentShader);
glLinkProgram(program);
// Use program
glUseProgram(program);
// Define geometry
GLfloat square[] = {
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
0.5, 0.5};
//Send geometry to vertex shader
const char *aPositionCString = [#"a_position" cStringUsingEncoding:NSUTF8StringEncoding];
GLuint aPosition = glGetAttribLocation(program, aPositionCString);
glVertexAttribPointer(aPosition, 2, GL_FLOAT, GL_FALSE, 0, square);
glEnableVertexAttribArray(aPosition);
// Draw
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Present renderbuffer
[context presentRenderbuffer:GL_RENDERBUFFER];
}
So much code and nothing happens... I can draw relentlessly wherever I want without a single stencil test stopping me.
What can I do? How do I check if the stencil buffer has something drawn inside it? If there's a missing puzzle for any of you, I will happily share any other parts of the code.
Any help is greatly appreciated! This has been torturing me for a while now. I will be forever in your debt!
UPDATE
I got the contour thing to work but I didn't use the stencil buffer. I created masks for every drawing area and textures for each mask which I loaded in the fragment shader along with the brush texture. When I tap on an area, I iterate through the array of masks and see which one was selected and bind the mask texture. I will make another post on SO with a more appropriate title and explain it there.
The way you allocate the renderbuffer storage looks problematic:
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];
The documentation says about this method:
The width, height, and internal color buffer format are derived from the characteristics of the drawable object.
The way I understand it, since your "drawable object" will normally be a color buffer, this will create a color renderbuffer. But you need a renderbuffer with stencil format in your case. I'm not sure if there's a way to do this with a utility method in the context class (the documentation says something about "overriding the internal color buffer format"), but the easiest way is probably to simply call the corresponding OpenGL function directly:
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
If you want to use your own FBO for this rendering, you will also need to create a color buffer for it, and attach it to the FBO. Otherwise you're not really producing any rendering output.
Instead of creating a new FBO, it might be easier to make sure that the default framebuffer has a stencil buffer, and render to it directly. To do this, you can request a stencil buffer for your GLKView derived view by making this call during setup:
[view setDrawableStencilFormat: GLKViewDrawableStencilFormat8];

How to implement VBO double buffering in WebGL?

I want to increase performance in my WebGL project by setting up VBO double buffering. Although there are plenty of articles on this topic, I failed to find one with a coding example.
I tried the following:
// Initial setup...
var buff1 = gl.createBuffer();
var buff2 = gl.createBuffer();
var buffActive = buff1;
// For each frame, change the verticies data and then ...
gl.bindBuffer(gl.ARRAY_BUFFER, buffActive);
gl.bufferData(gl.ARRAY_BUFFER, vertArray, gl.DYNAMIC_DRAW);
gl.drawArrays(gl.TRIANGLES, start, count);
buffActive = (buffActive === buff2) ? buff1 : buff2;
The calls to gl.bindBuffer and gl.bufferData work fine. However, gl.drawArrays always renders using only the data from buff1. I assumed gl.drawArrays would render using whichever VBO is currently bound to gl.ARRAY_BUFFER, but apparently that's not the case.
Does anyone see what I'm missing?
The issue here is you have bound both buff1 and buff2 to the same buffer, vertArray.
A better thing to do is create two vertex arrays (say, vertArray1 and vertArray2) and
to bind each vertex array to a separate buffer object during the initial setup (and not each frame, because
bufferData() destroys and reinitializes a buffer object's data, which may be an
expensive operation if doing so requires uploading the data to the GPU).
Here's an example:
// Initial setup
var buff1 = gl.createBuffer();
var buff2 = gl.createBuffer();
// Associate the buffer data once during initial setup.
// Note that we use two vertex arrays, vertArray1 and vertArray2,
// rather than one
gl.bindBuffer(gl.ARRAY_BUFFER, buff1);
gl.bufferData(gl.ARRAY_BUFFER, vertArray1, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, buff2);
gl.bufferData(gl.ARRAY_BUFFER, vertArray2, gl.DYNAMIC_DRAW);
var buffActive = buff1;
var vertArrayActive = vertArray1;
// For each frame:
gl.bindBuffer(gl.ARRAY_BUFFER, buffActive);
// No need to re-load the buffer data
gl.drawArrays(gl.TRIANGLES, start, count);
buffActive = (buffActive === buff2) ? Buff1 : buff2;
vertArrayActive = (vertArrayActive === vertArray2) ? VertArray1 : vertArray2;
If you implement VBO double buffering then you need to ensure calls to gl.vertexAttribPointer and gl.enableVertexAttribArray for vertex attribute data (stored as interleaved data in vertArray) are made every time the VBOs switch, rather than just during setup. Otherwise, your vertex attribute pointers will always be referencing data in the first VBO, and that VBO will now only be updated every other frame as a consequence of double buffering. The calls to gl.vertexAttribPointer and gl.enableVertexAttribArray must now be made after switching VBOs and before gl.drawArrays.

OpenGL ES 2.0 - memory management in drawing lines (graphing)

I finally got some functioning code to draw lines (in Xamarin/monotouch)
//init calls
Context = new EAGLContext (EAGLRenderingAPI.OpenGLES2);
DrawableDepthFormat = GLKViewDrawableDepthFormat.Format24;
EAGLContext.SetCurrentContext (Context);
effect = new GLKBaseEffect ();
effect.UseConstantColor = true;
effect.ConstantColor = new Vector4 (1f, 1f, 1f, 1f); //white
GL.ClearColor (0f, 0f, 0f, 1f);//black
public void DrawLine(float[] pts) {
//generate, bind, init
GL.GenBuffers (1, out vertexBuffer);
GL.BindBuffer (BufferTarget.ArrayBuffer, vertexBuffer);
GL.BufferData (BufferTarget.ArrayBuffer, (IntPtr) (pts.Length * sizeof (float)), pts, BufferUsage.DynamicDraw);
// RENDER //
effect.PrepareToDraw ();
//describe what's going to happen
GL.EnableVertexAttribArray ((int) GLKVertexAttrib.Position);
GL.VertexAttribPointer ((int) GLKVertexAttrib.Position, 2, VertexAttribPointerType.Float, false, sizeof(float) * 2, 0);
GL.DrawArrays (BeginMode.LineStrip, 0, pts.Length/2);
}
I have a couple questions.
Is this approach for drawing lines optimal? Are there any suggested improvements (i.e. antialiasing, etc..)
GL.Clear (ClearBufferMask.ColorBufferBit);
effect.ConstantColor = new Vector4 (1f, 1f, 1f, 1f);
DrawLine (line);
effect.ConstantColor = new Vector4 (1f, 0f, 1f, 1f);
DrawLine (line2);
Does all the memory associated with the line disappear when I call GL.Clear()? i.e. do I have to do any memory cleanup, or can I just keep calling GL.Clear() followed by DrawLine() and not worry about memory management?
I'm planning on using these functions for graphing. If the underlying data changes (but I have the same number of lines, is there a subset of functions that I can call to more efficiently update the lines?
GL.GenBuffers (1, out vertexBuffer) creates a buffer on the GPU and has to be deleted after the usage. In most cases you create buffer to push data to GPU which will not be updated frequently and are used to draw those data many times. There is probably a flag to stream the data (instead of DynamicDraw) for constant updating though. You could use that to reuse the same buffer but it would probably be best to just push the data pointer directly from the CPU: Lose all 3 lines concerning the buffer and insert pts into VertexAttribPointer instead of 0 for the last argument.
You say you will be using this for graph drawing. If the graph data will not be modified every frame and you can compute all the points you still might want to benefit from buffers. Instead of trying to push every line to its own buffer try pushing all the lines to a single buffer (even axis can be there). Use GL.DrawArrays (BeginMode.LineStrip, 0, pts.Length/2) to draw specific lines as last 2 arguments control the range in current buffer to draw (to draw 5th line only you would write GL.DrawArrays(BeginMode.LineStrip, 5*2, 2)). So when the graph data should update; delete the current buffer, create a new buffer, push the data to buffer, bind the buffer, set the vertex pointer and then just keep calling the draw method.
GLClear has nothing to do with memory cleanup at all. It will only clear (set values) the buffers attached to your frame buffer, in your case it will set all the pixels in your render buffer to the color you set in ClearColor. Nothing more. Other common cases are to also clear depth buffer, stencil buffer...
As for all the optimization and anti-aliasing it all depends on what you are doing, there is no general answer. Though if your scene gets too edgy try to search around for multisampling.

Resources