I am coding to OpenGL ES 2.0 (Webgl). I am using VBOs to draw primitives. I have vertex array, color array and array of indices. I have looked at sample codes, books and tutorial, but one thing I don't get - if color is defined per vertex how does it affect the polygonal surfaces adjacent to those vertices? (I am a newbie to OpenGL(ES))
I will explain with an example. I have a cube to draw. From what I read in OpenGLES book, the color is defined as an vertex attribute. In that case, if I want to draw 6 faces of the cube with 6 different colors how should I define the colors. The source of my confusion is: each vertex is common to 3 faces, then how will it help defining a color per vertex? (Or should the color be defined per index?). The fact that we need to subdivide these faces into triangles, makes it harder for me to understand how this relationship works. The same confusion goes for edges. Instead of drawing triangles, let's say I want to draw edges using LINES primitives. Each edge of different color. How am I supposed to define color attributes in that case?
I have seen few working examples. Specifically this tutorial: http://learningwebgl.com/blog/?p=370
I see how color array is defined in the above example to draw a cube with 6 different colored faces, but I don't understand why is defined that way. (Why is each color copied 4 times into unpackedColors for instance?)
Can someone explain how color attributes work in VBO?
[The link above seems inaccessible, so I will post the relevant code here]
cubeVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
vertices = [
// Front face
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
// Back face
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
// Top face
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
// Bottom face
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
// Right face
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
// Left face
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(vertices), gl.STATIC_DRAW);
cubeVertexPositionBuffer.itemSize = 3;
cubeVertexPositionBuffer.numItems = 24;
cubeVertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer);
var colors = [
[1.0, 0.0, 0.0, 1.0], // Front face
[1.0, 1.0, 0.0, 1.0], // Back face
[0.0, 1.0, 0.0, 1.0], // Top face
[1.0, 0.5, 0.5, 1.0], // Bottom face
[1.0, 0.0, 1.0, 1.0], // Right face
[0.0, 0.0, 1.0, 1.0], // Left face
];
var unpackedColors = []
for (var i in colors) {
var color = colors[i];
for (var j=0; j < 4; j++) {
unpackedColors = unpackedColors.concat(color);
}
}
gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(unpackedColors), gl.STATIC_DRAW);
cubeVertexColorBuffer.itemSize = 4;
cubeVertexColorBuffer.numItems = 24;
cubeVertexIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
var cubeVertexIndices = [
0, 1, 2, 0, 2, 3, // Front face
4, 5, 6, 4, 6, 7, // Back face
8, 9, 10, 8, 10, 11, // Top face
12, 13, 14, 12, 14, 15, // Bottom face
16, 17, 18, 16, 18, 19, // Right face
20, 21, 22, 20, 22, 23 // Left face
]
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new WebGLUnsignedShortArray(cubeVertexIndices), gl.STATIC_DRAW);
cubeVertexIndexBuffer.itemSize = 1;
cubeVertexIndexBuffer.numItems = 36;
The way I like to look at it is that each vertex is not a point in space but rather a bundle of attributes. These generally (but not always) include its location and may include its colour, texture coordinates, etc., etc., etc. A triangle (or line, or other primitive) is defined by specifying a set of vertices, then generating values for each attribute at each pixel by linearly interpolating the per-vertex values.
As Liam says, and as you've realised in your comment, this means that if you want to have a point in space that is used by a vertex for multiple primitives -- for example, the corner of a cube -- with other non-location attributes varying on a per-primitive basis, you need a separate vertex for each combination of attributes.
This is wasteful of memory to some degree -- but the complexity involved in doing it any other way would make things worse, and would require the graphics hardware to do a lot more work unpacking and repacking data. To me, it feels like the waste is comparable to the waste we get by using 32-bit RGBA values for each pixel in our video memory instead of keeping a "palette" lookup table of every colour we want to use and then just storing an index into that per-pixel (which is, of course, what we used to do when RAM was more expensive).
If the color of a set of polygons is the same then a vertex shared by all of the polygons, along with its color, can be defined once and shared by the polygons (using an index).
If the color of the polygons is different then even though the position of a vertex may be common, the color is not, and therefore the vertex as a whole cannot be shared. You will need to define the vertex for each polygon.
Related
So I'm writing some WebGL, no THREE.JS. I'm trying to render a cube, with a single texture mapped to every face of the cube. In my code where I set up my attributes I have something like:
var vertices = new Float32Array([
// x, y, z u, v
1.0, 1.0, 1.0, /* v0 right top front */ 1.0, 1.0,
-1.0, 1.0, 1.0, /* v1 left top front */ 0.0, 1.0,
-1.0, -1.0, 1.0, /* v2 left bottom front */ 0.0, 0.0,
1.0, -1.0, 1.0, /* v3 right bottom front */ 1.0, 0.0,
// u's switch for back faces
1.0, -1.0, -1.0, /* v4 right bottom back */ 0.0, 0.0,
1.0, 1.0, -1.0, /* v5 right top back */ 0.0, 1.0,
-1.0, 1.0, -1.0, /* v6 left top back */ 1.0, 1.0,
-1.0, -1.0, -1.0, /* v7 left bottom back */ 1.0, 0.0
]);
// the pairs of vertex triples
// 3 vertices = 1 triangle
// 2 triangles = 1 quad = 1 face
var indices = new Uint8Array([
0, 1, 2, 0, 2, 3, // front
0, 3, 4, 0, 4, 5, // right
//0, 5, 6, 0, 6, 1, // top
1, 6, 7, 1, 7, 2, // left
//7, 4, 3, 7, 3, 2, // bottom
4, 7, 6, 4, 6, 5 // back
]);
I wind up with a cube with the texture reflected for the right and left faces, which is fine. For the top and the bottom, I have no faces because of the two commented out lines. When I comment them in, the faces don't have the texture sampled as I expected. Sure enough, if you look at the indices for the top face for instance, and the UV coordinates that they would have:
index | u | v
0 | 1.0 | 1.0
1 | 0.0 | 1.0
5 | 0.0 | 1.0
6 | 1.0 | 1.0
So we can see that index 1 and 5 (also, 0 and 6) have the same UV coordinates, so of course it wont look right on a quad.
I've been trying to draw out on paper, but I can't change the UV's without messing up another face's coordinates. What I'm wondering is: is it possible to use ELEMENT_ARRAY_BUFFERs to map UV coordinates on a cube, or do I need to use more data and draw using an ARRAY_BUFFER?
== EDIT ==
Looks like a dupe: OpenGL ES - texture map all faces of an 8 vertex cube?
Hate to answer my own question, but based on the hint here, I was able to get it to work by using 24 vertices instead of 8. I can use 24 instead of 36 because I'm repeating indices in my ELEMENT_ARRAY_BUFFER (something I wouldn't be able to do with just an ARRAY_BUFFER).
var vertices = new Float32Array([
// x, y, z, u, v
// front face (z: +1)
1.0, 1.0, 1.0, 1.0, 1.0, // top right
-1.0, 1.0, 1.0, 0.0, 1.0, // top left
-1.0, -1.0, 1.0, 0.0, 0.0, // bottom left
1.0, -1.0, 1.0, 1.0, 0.0, // bottom right
// right face (x: +1)
1.0, 1.0, -1.0, 1.0, 1.0, // top right
1.0, 1.0, 1.0, 0.0, 1.0, // top left
1.0, -1.0, 1.0, 0.0, 0.0, // bottom left
1.0, -1.0, -1.0, 1.0, 0.0, // bottom right
// top face (y: +1)
1.0, 1.0, -1.0, 1.0, 1.0, // top right
-1.0, 1.0, -1.0, 0.0, 1.0, // top left
-1.0, 1.0, 1.0, 0.0, 0.0, // bottom left
1.0, 1.0, 1.0, 1.0, 0.0, // bottom right
// left face (x: -1)
-1.0, 1.0, 1.0, 1.0, 1.0, // top right
-1.0, 1.0, -1.0, 0.0, 1.0, // top left
-1.0, -1.0, -1.0, 0.0, 0.0, // bottom left
-1.0, -1.0, 1.0, 1.0, 0.0, // bottom right
// bottom face (y: -1)
1.0, -1.0, 1.0, 1.0, 1.0, // top right
-1.0, -1.0, 1.0, 0.0, 1.0, // top left
-1.0, -1.0, -1.0, 0.0, 0.0, // bottom left
1.0, -1.0, -1.0, 1.0, 0.0, // bottom right
// back face (x: -1)
-1.0, 1.0, -1.0, 1.0, 1.0, // top right
1.0, 1.0, -1.0, 0.0, 1.0, // top left
1.0, -1.0, -1.0, 0.0, 0.0, // bottom left
-1.0, -1.0, -1.0, 1.0, 0.0 // bottom right
]);
// the pairs of vertex triples
// 3 vertices = 1 triangle
// 2 triangles = 1 quad = 1 face
var indices = new Uint8Array([
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23
]);
Scroll above code example ^
The number of vertices can be further reduced, because some indices share the same XYZ and UV coordinates, though if I add normals to my interleaved buffer later (or any other attribute) I may need the repeated values.
I've been looking into OpenGL and diving into the 3D world. This question relates to OpenGL in general but I've been using WebGL (which is OpenGL ES if I believe). I've had a problem understanding how cubes are drawn. I know that to draw a quad, you create two triangles in (default) CCW order like
1 0
|‾ ‾ ‾ ‾|
| | Indices = 0,1,2, 0,2,3 (2 triangles one face)
| |
|_ _ _ _|
2 3
However, I'm having a bit of trouble understanding the best way to draw a cube. Is there a specific way I should be drawing the cube? For example, is drawing the faces in front->right->bottom->left->top->back the best order or is it some other way. Do I draw the cube faces counter-clockwise too or something? I just need to understand the different ways I could represent a model/cube and why.
It can be done with 2 triangle strips, but strips are usually replaced with indexed triangles for more complex geometry because they become more efficient when you need multiple strips. I think it can also be done with a single strip if you use a degenerate triangle to connect the two, but indexed is usually better for larger geometry. Quads just get converted into triangles.
The winding direction is important only when backface culling is enabled. Also, things get more complicated when you add texture coordinates and surface normals for lighting. The normals can be used to make the cube look faceted or smooth shaded, which is really for larger models, like spheres. Here is a tutorial I wrote years ago for OpenGL ES 2.0:
/******************************************************************************
Function DrawCubeSmooth
Return None
Description Draw a cube using Vertex and NormalsPerVertex Arrays and
glDrawArrays with two triangle strips. Because normals are
supplied per vertex, all the triangles will be smooth shaded.
Triangle strips are used instead of an index array. The first
strip is texture mapped but the second strip is not.
******************************************************************************/
void Cube2::DrawCubeSmooth(void)
{
static GLfloat Vertices[16][3] =
{ // x y z
{-1.0, -1.0, 1.0}, // 1 left First Strip
{-1.0, 1.0, 1.0}, // 3
{-1.0, -1.0, -1.0}, // 0
{-1.0, 1.0, -1.0}, // 2
{ 1.0, -1.0, -1.0}, // 4 back
{ 1.0, 1.0, -1.0}, // 6
{ 1.0, -1.0, 1.0}, // 5 right
{ 1.0, 1.0, 1.0}, // 7
{ 1.0, 1.0, -1.0}, // 6 top Second Strip
{-1.0, 1.0, -1.0}, // 2
{ 1.0, 1.0, 1.0}, // 7
{-1.0, 1.0, 1.0}, // 3
{ 1.0, -1.0, 1.0}, // 5 front
{-1.0, -1.0, 1.0}, // 1
{ 1.0, -1.0, -1.0}, // 4 bottom
{-1.0, -1.0, -1.0} // 0
};
static GLfloat NormalsPerVertex[16][3] = // One normal per vertex.
{ // x y z
{-0.5, -0.5, 0.5}, // 1 left First Strip
{-0.5, 0.5, 0.5}, // 3
{-0.5, -0.5, -0.5}, // 0
{-0.5, 0.5, -0.5}, // 2
{ 0.5, -0.5, -0.5}, // 4 back
{ 0.5, 0.5, -0.5}, // 6
{ 0.5, -0.5, 0.5}, // 5 right
{ 0.5, 0.5, 0.5}, // 7
{ 0.5, 0.5, -0.5}, // 6 top Second Strip
{-0.5, 0.5, -0.5}, // 2
{ 0.5, 0.5, 0.5}, // 7
{-0.5, 0.5, 0.5}, // 3
{ 0.5, -0.5, 0.5}, // 5 front
{-0.5, -0.5, 0.5}, // 1
{ 0.5, -0.5, -0.5}, // 4 bottom
{-0.5, -0.5, -0.5} // 0
};
static GLfloat TexCoords[8][2] =
{ // x y
{0.0, 1.0}, // 1 left First Strip
{1.0, 1.0}, // 3
{0.0, 0.0}, // 0
{1.0, 0.0}, // 2
{0.0, 1.0}, // 4 back
{1.0, 1.0}, // 6
{0.0, 0.0}, // 5 right
{1.0, 0.0} // 7
};
glEnableVertexAttribArray(VERTEX_ARRAY);
glEnableVertexAttribArray(NORMAL_ARRAY);
glEnableVertexAttribArray(TEXCOORD_ARRAY);
// Set pointers to the arrays
glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, Vertices);
glVertexAttribPointer(NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, NormalsPerVertex);
glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, 0, TexCoords);
// Draw first triangle strip with texture map
glDrawArrays(GL_TRIANGLE_STRIP, 0, 8);
// Draw second triangle strip without texture map
glDisableVertexAttribArray(TEXCOORD_ARRAY);
glDrawArrays(GL_TRIANGLE_STRIP, 8, 8);
glDisableVertexAttribArray(VERTEX_ARRAY);
glDisableVertexAttribArray(NORMAL_ARRAY);
};
I hope this helps.
I am trying to create a VBO for a simple rectangle. The GL is set up to use the core profile (GL: 3.2, GLSL: 1.5, inside an NSView in Cocoa).
I spent hours trying to figure out how to draw a simple rectangle in OpenGL. It seems really hard to find any decent tutorials on the core profile. The best I could find was this tutorial.
I adapted it to my needs and came up with the following code:
GLfloat vertices[] = { 1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
-1.0, -1.0, -1.0 };
glGenBuffers(1, &vertexVBO);
glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*3*4, vertices, GL_STATIC_DRAW);
glVertexAttribPointer(VERTEX_POS, 3, GL_FLOAT, GL_FALSE, 0, 0); // VERTEX_POS = 0
glEnableVertexAttribArray(VERTEX_POS); // fails
However, this is throwing an error when calling glEnableVertexAttribArray: INVALID_OPERATION. The documentation suggests that this error is produced if the aforementioned call is made between a glBegin and glEnd. This is not the case. I mean, as far as I know, glBegin and glEnd are not even supported in the core profile.
Thus, I am at a loss. How can I draw this stupid rectangle (or at least initialize it)?
You need to bind a VAO before setting attribute pointers.
GLuint vao_name;
glGenVertexArrays(1, &vao_name);
glBindVertexArray(vao_name);
// ...
glVertexAttribPointer(...);
glEnableVertexAttribArray(...);
Also, the documentation you're linking to is outdated — use this one instead.
Imagine you're standing on the ground looking up at a cube in the sky. As you tilt your head, the cube moves. I'm trying to replicate this using OpenGL ES on the iPhone by manipulating the tilt of the camera while looking at a simple 3D cube drawn around the origin. I'm using the gluLookAt() function from Cocos2d which is supposed to emulate the OpenGL version and it seems that when I try to tinker with any of the values, my cube disappears.
My question is: Can you provide a gluLookAt() usage here that will get me started manipulating the camera so I can figure out how this works? I'm really just interesting in learning how to tilt the camera along the Y axis.
Here is my current code:
Viewport Configuration
glBindFramebufferOES(GL_FRAMEBUFFER_OES, _viewFramebuffer);
glViewport(0, 0, _backingWidth, _backingHeight);
Projection Matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Maybe this should be a perspective projection?? If so,
// can you provide an example using gluPerspective()?
glOrthof(-_backingWidth, _backingWidth,-_backingHeight, _backingHeight, -1, 1);
ModelView Matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt() // What goes here?
Drawing Code
static const GLfloat cubeVertices[] = {
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
};
static const GLushort cubeIndices[] = {
0, 1, 2, 3, 7, 1, 5, 4, 7, 6, 2, 4, 0, 1
};
static const GLubyte cubeColors[] = {
255, 255, 0, 255,
0, 255, 255, 255,
0, 0, 0, 0,
255, 0, 255, 255,
255, 255, 0, 255,
0, 255, 255, 255,
0, 0, 0, 0,
255, 0, 255, 255
};
glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
glEnableClientState(GL_VERTEX_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, cubeColors);
glEnableClientState(GL_COLOR_ARRAY);
glDrawElements(GL_TRIANGLE_STRIP, 14, GL_UNSIGNED_SHORT, cubeIndices);
I'm not completely sure what exactly you want, but here some explanations:
gluLookAt expects 3 vectors (each as 3 doubles): first the position of the camera (eye point), then the position to where you look (center point) and finally an up-vector that specifies an up-direction (this need not be the perfect orthogonal upward direction, as it is reorthogonalized anyway).
So if you stand at (0,0,5) and look at your cube (that is at the center) and want the y-axis to be the up-direction, you would call gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0) to see your cube in full beauty.
If you want to tilt your head to the side, you just need to change the up-vector and rotate it to the side a bit. Or if you want to look up, but without tilting the head to the side, you still use the y-axis as up-vector, but you just look at another point, so you change the center point to a point above and in front of you (maybe rotated about the eye position). But this won't work if you want to look straight up, in this case you need to change the up-vector to something orthogonal to the y-axis (in addition to setting the center point to a point straight above you, of course).
But I think you want a perspective projection. Your current ortho is at least quite inappropriate for your coordinates, as it specifies a coordinate system in which coordinates are in the size of pixel, so your [-1,1]-cube is about the size of a pixel on the screen. Try gluPerspective(60.0, ((double)_backingWidth)/_backingHeight, 0.1, 100.0). If you really want an orthographic projection without any realistic perspective distortion, you can use glOrtho, but in this case you should keep the size proprtions of the glOrtho parameters and your model's coordinates roughly in-sync (therefore not specify a screen-sized ortho and using coordinates in the [-1,1] range).
I'm doing my first steps with OpenGL ES 2.0 trying things on my ipod touch. I was wondering how to solve this coordinates issue..
To explain better, I was trying to draw a quad and rotate/translate it using a vertex shader (also because from what I've read it seems the only way to do it).
Since I'm working with a ipod I have a 1.5 : 1 ratio and a viewport set by
glViewport(0, 0, backingWidth, backingHeight);
So 0,0 is the center and bounds for clipping should be at -1.0, -1.0, -1.0, 1.0, etc (right?)
To draw a square I had to use different values for x and y coordinates because of the aspect ratio:
static const GLfloat lineV[] = {
-0.5f, 0.33f, 0.5f, 0.33f,
0.5f, 0.33f, 0.5f,-0.33f,
0.5f,-0.33f, -0.5f,-0.33f,
-0.5f,-0.33f, -0.5f, 0.33f,
-0.5f, 0.33f, 0.5f,-0.33f,
0.5f, 0.33f, -0.5f,-0.33f,
};
It's a square with both diagonals (I know that using indexes would be more efficient but that's not the point)..
Then I tried writing a vertex shader to rotate the object while moving it:
void main()
{
m = mat4( cos(rotation), sin(rotation), 0.0, 0.0,
-sin(rotation), cos(rotation), 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
m2 = mat4(1.0);
m2[1][3] = sin(rotation)*0.8;
gl_Position = position*(m*m2);
}
It works but since coordinates are not the same the quad is distorted while it rotates. How should I prevent that? I thought if it was possible to change the view frustum to have different bounds (not -1.0 to 1.0 on both axis so that enlarging on y-axis would fix the problem).
In addition is there a better way to use matrixes? I mean, I was used to use glRotatef without having to specify the whole matrix.. does convenience functions/constructors exist to accomplish this task?
The first arguments to glViewport() is not the center, it's the bottom left corner's coordinates.
You should probably set up a projection that takes your aspect into account, typically using gluPerspective() (if GLU is available in ES).
No glut or support functions are provided from what I've seen. Basically I solved it by using equal coordinates when building vertices and using a vertex shader to scale on y axis by the right aspect ratio.