Say we have an object and we want to create multiple objects and move them independently based some algorithm.
Here is what is the process I am using:
Create a structure with the geometry of the object
Create an array of vertice buffers using the geometry of the object
Now in the rendering routine, I need to go through each one of those objects and alter their position based on a specific algorithm.
To accomplish this I need to get the current location of the object to compute the new position.
How can I get the current location of a vertice buffer? Clearly, I do not want to store outside the program all locations of the object since they are inside the vertice buffer.
EDIT: This is the code I am using to store and retrieve data from the model matrix of each object
// Set up Code
- (void)setupGL
{
[EAGLContext setCurrentContext:self.context];
[self loadShaders];
glEnable(GL_DEPTH_TEST);
for( int i; i<num_objects; i++) {
glGenVertexArraysOES(1, &_objectArray[i]);
glBindVertexArrayOES(_objectArray[i]);
glGenBuffers(1, &_objectBuffer[i]);
glBindBuffer(GL_ARRAY_BUFFER, _objectBuffer[i]);
glBufferData(GL_ARRAY_BUFFER, sizeof(objectData), objectData, GL_STATIC_DRAW);
glEnableVertexAttribArray(....);
glVertexAttribPointer(......, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0));
}
glBindVertexArrayOES(0);
}
//********************************************************
// Rendering Code
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glUseProgram(_program);
for(int i=0; i<num_objects; i++) {
glBindVertexArrayOES(_objectArray[i]);
// Get Previous data
GLint uMatrix = glGetUniformLocation(_program, "modelMatrix");
glGetUniformfv(_program, uMatrix, dataOfCurrentObject);
// Get Previous data
... transform dataOfCurrentObject based on an algorithm and create newDataOfCurrentObject
// Update object with new data and draw
glUniformMatrix4fv(uMatrix, 1, 0, newDataOfCurrentObject);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
}
The problem I have now is that the dataOfCurrentObject for object 'i' is identical to the newDataOfCurrentObject for object 'i-1'. In other words it appears that the code keeps track of only one model matrix for all objects, or it does not read correctly the model matrix of a specific object. Any ideas?
The simplest method that gets used is to set the object's position in the Model Matrix, and upload that object's appropriate Model Matrix as a Uniform each time you draw a new object. The [pseudo-]code would look like this:
for(game_object object : game_object_list) {
glUniformMatrix4f(modelMatrixUniformLocation, 1, false, object->model_matrix);
object->draw();
}
And when you need to update the object's position:
for(game_object object : game_object_list) {
object->model_matrix = Matrix.identity();
/*Any transformations that need to take place here*/
object->model_matrix = object->model_matrix.transpose(/*x*/, /*y*/);
/*Any other transformations that need to take place*/
}
Exactly what you put in there will vary depending on your needs. If you're programming a 2D game, you probably don't need a 4x4 model matrix. But the basic logic should be identical to what you eventually use.
You don't have to read anything back from your vertex buffers. You just need to keep in your application the tranform you need and pass it as uniform for every object.
For example if you need to translate the object, keep the modelViewMatrix in and every frame apply trasformation to the previous matrix :
GLKMatrix4 modelViewMatrix;
...
modelViewMatrix = GLKMatrix4Identity;
...
-(void) render {
modelViewMatrix = GLKMatrix4Translate(modelViewMatrix, deltaX, deltaY, deltaZ);
glUniformMatrix4fv(_modelViewUniform, 1,0, modelViewMatrix.m);
}
So you just need to keep reference to your objects modelView matrices and apply your transformations accordingly.
If you want to read data back from your vertex buffers (gpu) to your application you have to use Transform Feedback which is a bit more advanced technique and it is used when you modify your vertexes in the vertex shader and you need the results back. (It is not needed for your case).
Related
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
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.
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.
First of all, I am new to XNA and the way GPU works and how it cooperates with the XNA (or DirectX) API.
I have a polygon to draw using the SpriteBatch. I'm triangulating the polygon, and creating a VertexPositionTexture array to hold the vertices. I set the vertices (and just for simplicity, set the texture offset vector to zero), and try to draw the primitives, but I get this error:
The current vertex declaration does not include all the elements required by the current vertex shader. Color0 is missing.
Here is my code, I've double checked my vectors from triangulation, they are fine:
VertexPositionTexture[] vertices = new VertexPositionTexture[triangulationResult.Count * 3];
int ctr = 0;
foreach (var item in triangulationResult)
{
foreach (var point in item.Vertices)
{
vertices[ctr++] = new VertexPositionTexture(new Vector3(point.X, point.Y, 0), Vector2.Zero);
}
}
sb.GraphicsDevice.DrawUserPrimitives<VertexPositionTexture>(PrimitiveType.TriangleList, vertices, 0, triangulationResult.Count);
What am I possibly doing wrong here?
Your shader is expecting a Color in the vertex stream.... so you have to use VertexPositionColorTexture or change your shader.
It seems that you are not using any shader. If the active shader is the one used with spritebatch you won't be able to draw it right.
VertexPositionColorTexture[] vertices = new VertexPositionColorTexture[triangulationResult.Count * 3];
int ctr = 0;
foreach (var item in triangulationResult)
{
foreach (var point in item.Vertices)
{
vertices[ctr++] = new VertexPositionColorTexture(new Vector3(point.X, point.Y, 0), Color.White, Vector2.Zero);
}
}
sb.GraphicsDevice.DrawUserPrimitives<VertexPositionColorTexture>(PrimitiveType.TriangleList, vertices, 0, triangulationResult.Count);
Use BasicEffect if you are drawing polygons (MSDN tutorial). You should only use SpriteBatch for sprite drawing (ie: using its Draw methods).
The vertex element type that BasicEffect requires will depend on what settings you apply to it.
To use a vertex element type without a colour component (like VertexPositionTexture), set BasicEffect.VertexColorEnabled to false.
Or alternately, use a vertex element type that supplies a colour, such as VertexPositionColorTexture.
If you want to create a BasicEffect that has the same coordinate system as SpriteBatch, see this answer or this blog post.
I am having trouble understanding how to use VAO's in OpenGL ES2 (on iOS) and getting them to work.
My current rendering setup looks like this (in pseudocode):
Initialization:
foreach VBO:
glGenBuffers();
Rendering a frame:
// Render VBO 1
glClear(color | depth);
glBindBuffer(GL_ARRAY_BUFFER, arrayVBO1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO1);
foreach vertex attribute:
glVertexAttribPointer();
glEnableVertexAttribArray();
glBufferData(GL_ARRAY_BUFFER, ...);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ...);
glDrawElements(GL_TRIANGLE_STRIP, ...);
// Render VBO 2
glClear(color | depth);
glBindBuffer(GL_ARRAY_BUFFER, arrayVBO2);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO2);
foreach vertex attribute:
glVertexAttribPointer();
glEnableVertexAttribArray();
glBufferData(GL_ARRAY_BUFFER, ...);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ...);
glDrawElements(GL_TRIANGLE_STRIP, ...);
This works fine, however, both VBO's have exactly the same interleaved vertex attribute struct, and as you can see I'm setting up and enabling each attribute every frame for every VBO. Instruments complains about redundant calls to glVertexAttribPointer() and glEnableVertexAttribArray(), but when I move one of them or both to the initialization phase I either get a EXC_BAD_ACCESS when calling glDrawElements or nothing is drawn.
My question is whether I need to do this every frame, why it doesn't work if I don't, and how I would use VAO's to solve this.
Sorry for the dredge, but I'm procrastinating and you keep topping my google search. I'm sure you've solved it by now...
The correct way is to only update the buffers when the data changes, not every frame. Ideally, you would only update the part of the buffer that changed. Also, Attribute Pointers are offsets into the buffer if a buffer is bound.
Initialisation:
glGenBuffers()
foreach VBO:
glBufferData()
Updates / Animation, etc:
glMapBuffer() //or something like this
buffer->vertex = vec3(1,2,3) // etc
glUnmapBuffer()
And Render:
glBindFBO()
glClear(color | depth);
glBindBuffer(GL_ARRAY_BUFFER, arrayVBO1)
glVertexAttribPointer(GL_VERTEX,..., 0) // Buffer Offset = 0
glVertexAttribPointer(GL_TEXCOORD,..., sizeof(vertex)) // Buffer Offset = size of vertex
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO1);
glEnableVertexAttribArray(..., 0) // Buffer Offset = 0
glBindBuffer(0); // Unbind buffers, you don't need them here
glDrawElements(GL_TRIANGLE_STRIP, ...);
Hope that helps.