I'd like to use Vertex Buffer Objects (VBOs) to improved my rendering of somewhat complicated models in my Open GL ES 1.1 game for iPhone. After reading several posts on SO and this (http://playcontrol.net/ewing/jibberjabber/opengl_vertex_buffer_object.html) tutorial, I'm still having trouble understanding VBOs and how to implement them given my Cheetah 3D export model format. Could someone please give me an example of implementing a VBO and using it to draw my vertices with the given data structure and explain the syntax? I greatly appreciate any help!
#define body_vertexcount 434
#define body_polygoncount 780
// The vertex data is saved in the following format:
// u0,v0,normalx0,normaly0,normalz0,x0,y0,z0
float body_vertex[body_vertexcount][8]={
{0.03333, 0.00000, -0.68652, -0.51763, 0.51063, 0.40972, -0.25028, -1.31418},
{...},
{...}
}
GLushort body_index[body_polygoncount][3]={
{0, 1, 2},
{2, 3, 0}
}
I've written the following code with the help of Chapter 9 from Pro OpenGL ES (Appress). I'm getting EXC_BAD_ACCESS with the DrawElements command and I'm not sure why. Could someone please shed some light? Thanks -
// First thing we do is create / setup the index buffer
glGenBuffers(1, &bodyIBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bodyIBO);
// For constrast, instead of glBufferSubData and glMapBuffer,
// we can directly supply the data in one-shot
glBufferData(GL_ELEMENT_ARRAY_BUFFER, body_polygoncount*sizeof(GLubyte), body_index, GL_STATIC_DRAW);
// Define our data structure
int numXYZElements = 3;
int numNormalElements = 3;
int numTextureCoordElements = 2;
long totalXYZBytes;
long totalNormalBytes;
long totalTexCoordinateBytes;
int numBytesPerVertex;
// Allocate a new buffer
glGenBuffers(1, &bodyVBO);
// Bind the buffer object to use
glBindBuffer(GL_ARRAY_BUFFER, bodyVBO);
// Tally up the size of the data components
numBytesPerVertex = numXYZElements;
numBytesPerVertex += numNormalElements;
numBytesPerVertex += numTextureCoordElements;
numBytesPerVertex *= sizeof(GLfloat);
// Actually allocate memory on the GPU ( Data is static here )
glBufferData(GL_ARRAY_BUFFER, numBytesPerVertex * body_vertexcount, 0, GL_STATIC_DRAW);
// Upload data to the cache ( memory mapping )
GLubyte *vboBuffer = (GLubyte *)glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
// Caclulate the total number of bytes for each data type
totalXYZBytes = numXYZElements * body_vertexcount * sizeof(GLfloat);
totalNormalBytes = numNormalElements * body_vertexcount * sizeof(GLfloat);
totalTexCoordinateBytes = numTextureCoordElements * body_vertexcount * sizeof(GLfloat);
// Set the total bytes property for the body
self.bodyTotalBytes = totalXYZBytes + totalNormalBytes + totalTexCoordinateBytes;
// Setup the copy of the buffer(s) using memcpy()
memcpy(vboBuffer, body_vertex, self.bodyTotalBytes);
// Perform the actual copy
glUnmapBufferOES(GL_ARRAY_BUFFER);
Here are the drawing commands where I'm getting the exception:
// Activate the VBOs to draw
glBindBuffer(GL_ARRAY_BUFFER, bodyVBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bodyIBO);
// Setup drawing
glMatrixMode(GL_MODELVIEW);
glEnable(GL_TEXTURE_2D);
glClientActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,lightGreyInt);
// Setup pointers
glVertexPointer(3, GL_FLOAT, sizeof(vertexStruct), (char *)NULL + 0 );
glTexCoordPointer(2, GL_FLOAT, sizeof(vertexStruct), (char *)NULL + 12 );
glNormalPointer(GL_FLOAT, sizeof(vertexStruct), (char *)NULL + 24 );
// Now draw the body
glDrawElements(GL_TRIANGLES, body_polygoncount,GL_UNSIGNED_SHORT, (GLvoid*)((char*)NULL));
//glDrawElements(GL_TRIANGLES, body_polygoncount, GL_UNSIGNED_SHORT, nil);
//glDrawElements(GL_TRIANGLES,body_polygoncount*3,GL_UNSIGNED_SHORT,body_index);
Well, first of all your index buffer is too small, you don't just have body_polygoncount indices but body_polygoncount * 3. You also messed up the type, since they're shorts, you need GLushort and not GLubyte, so it should be
glBufferData(GL_ELEMENT_ARRAY_BUFFER, body_polygoncount*3*sizeof(GLushort),
body_index, GL_STATIC_DRAW);
And then, you messed up the offsets of your attributes, since your data contains first the texture coords, then the normal and then the position for each vertex, it should be
glVertexPointer(3, GL_FLOAT, sizeof(vertexStruct), (char *)NULL + 20 ); //3rd, after 5*4 byte
glTexCoordPointer(2, GL_FLOAT, sizeof(vertexStruct), (char *)NULL + 0 ); //1st
glNormalPointer(GL_FLOAT, sizeof(vertexStruct), (char *)NULL + 8 ); //2nd, after 2*4 bytes
And finally, in a glDrawElements call you don't give the number of triangles, but the number of elements (indices), so it should be
glDrawElements(GL_TRIANGLES, body_polygoncount*3,
GL_UNSIGNED_SHORT, (GLvoid*)((char*)NULL));
Otherwise your code looks reasonable (of course the mapping was senseless and you could have just used glBufferData again, but I guess you did it for learning) and if you understood everything it does, there is nothing more to it.
But I wonder that all these errors would also have occurred if you had just used client side vertex arrays without VBOs and I thought OpenGL ES 1.1 doesn't have immediate mode glBegin/glEnd. So I wonder why your game worked previously without VBOs if you're not aware of these errors.
Related
I am trying to port an application to an embedded system that I am trying to design. The embedded system is Raspberry Pi Zero W - based, and uses a custom Yocto build.
The application to be ported is written with SDL / OpenGLES to my understanding. I have a hard time understanding how to make a connection similar to the following depiction:
SDL APP -----> XServer ($DISPLAY) -------> Framebuffer /dev/fb1 ($FRAMEBUFFER)
System has two displays: One HDMI on /dev/fb0 and One TFT on /dev/fb1. I am trying to run the SDL application on TFT. The following are the steps I do:
First, start an XServer on DISPLAY=:1 that is connected to /dev/fb1:
FRAMEBUFFER=/dev/fb1 xinit /etc/X11/Xsession -- /usr/bin/Xorg :1 -br -pn -nolisten tcp -dpi 100
The first step seems like it's working. I can see LXDE booting up on my TFT screen. Checking the display, I get the correct display resolution:
~/projects# DISPLAY=:1 xrandr -q
xrandr: Failed to get size of gamma for output default
Screen 0: minimum 320 x 240, current 320 x 240, maximum 320 x 240
default connected 320x240+0+0 0mm x 0mm
320x240 0.00*
Second, I would like to start SDL-written application using x11. I am thinking that should work in seeing the application on the TFT. In order to do so, I try:
SDL_VIDEODRIVER=x11 SDL_WINDOWID=1 DISPLAY=:1 ./SDL_App
No matter which display number I choose, it starts on my HDMI display and not on the TFT. So now I am thinking the person who wrote the application hardcoded somethings in the application code:
void init_ogl(void)
{
int32_t success = 0;
EGLBoolean result;
EGLint num_config;
static EGL_DISPMANX_WINDOW_T nativewindow;
DISPMANX_ELEMENT_HANDLE_T dispman_element;
DISPMANX_DISPLAY_HANDLE_T dispman_display;
DISPMANX_UPDATE_HANDLE_T dispman_update;
VC_DISPMANX_ALPHA_T alpha;
VC_RECT_T dst_rect;
VC_RECT_T src_rect;
static const EGLint attribute_list[] =
{
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE
};
EGLConfig config;
// Get an EGL display connection
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
assert(display!=EGL_NO_DISPLAY);
// Initialize the EGL display connection
result = eglInitialize(display, NULL, NULL);
assert(EGL_FALSE != result);
// Get an appropriate EGL frame buffer configuration
result = eglChooseConfig(display, attribute_list, &config, 1, &num_config);
assert(EGL_FALSE != result);
// Create an EGL rendering context
context = eglCreateContext(display, config, EGL_NO_CONTEXT, NULL);
assert(context!=EGL_NO_CONTEXT);
// Create an EGL window surface
success = graphics_get_display_size( 0 /* LCD */ , &screen_width, &screen_height);
printf ("Screen width= %d\n", screen_width);
printf ("Screen height= %d\n", screen_height);
assert( success >= 0 );
int32_t zoom = screen_width / GAMEBOY_WIDTH;
int32_t zoom2 = screen_height / GAMEBOY_HEIGHT;
if (zoom2 < zoom)
zoom = zoom2;
int32_t display_width = GAMEBOY_WIDTH * zoom;
int32_t display_height = GAMEBOY_HEIGHT * zoom;
int32_t display_offset_x = (screen_width / 2) - (display_width / 2);
int32_t display_offset_y = (screen_height / 2) - (display_height / 2);
dst_rect.x = 0;
dst_rect.y = 0;
dst_rect.width = screen_width;
dst_rect.height = screen_height;
src_rect.x = 0;
src_rect.y = 0;
src_rect.width = screen_width << 16;
src_rect.height = screen_height << 16;
dispman_display = vc_dispmanx_display_open( 0 /* LCD */ );
dispman_update = vc_dispmanx_update_start( 0 );
alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS;
alpha.opacity = 255;
alpha.mask = 0;
dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display,
0/*layer*/, &dst_rect, 0/*src*/,
&src_rect, DISPMANX_PROTECTION_NONE, &alpha, 0/*clamp*/, DISPMANX_NO_ROTATE/*transform*/);
nativewindow.element = dispman_element;
nativewindow.width = screen_width;
nativewindow.height = screen_height;
vc_dispmanx_update_submit_sync( dispman_update );
surface = eglCreateWindowSurface( display, config, &nativewindow, NULL );
assert(surface != EGL_NO_SURFACE);
// Connect the context to the surface
result = eglMakeCurrent(display, surface, surface, context);
assert(EGL_FALSE != result);
eglSwapInterval(display, 1);
glGenTextures(1, &theGBTexture);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, theGBTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*) NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(0.0f, screen_width, screen_height, 0.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glViewport(0.0f, 0.0f, screen_width, screen_height);
quadVerts[0] = display_offset_x;
quadVerts[1] = display_offset_y;
quadVerts[2] = display_offset_x + display_width;
quadVerts[3] = display_offset_y;
quadVerts[4] = display_offset_x + display_width;
quadVerts[5] = display_offset_y + display_height;
quadVerts[6] = display_offset_x;
quadVerts[7] = display_offset_y + display_height;
glVertexPointer(2, GL_SHORT, 0, quadVerts);
glEnableClientState(GL_VERTEX_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, kQuadTex);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClear(GL_COLOR_BUFFER_BIT);
}
void init_sdl(void)
{
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0)
{
Log("SDL Error Init: %s", SDL_GetError());
}
theWindow = SDL_CreateWindow("Gearboy", 0, 0, 0, 0, 0);
if (theWindow == NULL)
{
Log("SDL Error Video: %s", SDL_GetError());
}
...
}
At first glance, I discovered two lines: vc_dispmanx_display_open( 0 /* LCD */ ); and graphics_get_display_size( 0 /* LCD */ , &screen_width, &screen_height);. I tried changing the display parameter to 1, thinking that it refers to DISPLAY=:1, but it did not do anything. I added logs for screen resolution, and I get 1920x1080, which is the resolution of the HDMI display. I think there must be something with the EGL portion of the code that I'm missing. What should I do right now? Is my logic fair enough or am I missing something?
Any requirements, please let me know. Any guidance regarding the issue is much appreciated.
EDIT: I saw that some people use the following, but raspberry pi zero can not find EGL/eglvivante.h for fb functions so I am unable to compile it:
int fbnum = 1; // fbnum is an integer for /dev/fb1 fbnum = 1
EGLNativeDisplayType native_display = fbGetDisplayByIndex(fbnum);
EGLNativeWindowType native_window = fbCreateWindow(native_display, 0, 0, 0, 0);
display = eglGetDisplay(native_display);
I've been trying to switch from two distinct VBOs to just one with interleaved attributes. I can do it in C++, but in Scala it proves quite difficult.
Here is my implementation:
class Mesh(positions: Array[Float], textureCoordinates: Array[Float], indices: Array[Int])
{
// Create VAO, VBO and a buffer for the indices
val vao: Int = glGenVertexArrays
val vbo: Int = glGenBuffers
val ibo: Int = glGenBuffers
setup
private def setup(): Unit =
{
val interleavedBuffer: FloatBuffer = prepareFloatBuffer(positions ++ textureCoordinates)
val indicesBuffer: IntBuffer = prepareIntBuffer(indices)
// One VAO to bind them all!
glBindVertexArray(vao)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo)
// Fill buffers with data
glBufferData(GL_ARRAY_BUFFER, interleavedBuffer, GL_STATIC_DRAW)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL_STATIC_DRAW)
// Set vertex attribute pointers
glVertexAttribPointer(0, 3, GL_FLOAT, false, 4*5, 0) // 0 = Position = Vector3(x,y,z) -> 3 (coordinates) * 4 (byte-size of float)
glVertexAttribPointer(1, 2, GL_FLOAT, false, 4*5, 4*3) // 1 = Texture Coordinates = Vector2(x,y) -> 2 (coordinates) * 4 (byte-size of float) => stride = 3 (coordinates) + 2 (texture coordinates) = 5 * 4 (byte-size of float); offset = 3 (coordinates) * 4 (byte-size of float)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
}
private def prepareIntBuffer(data: Array[Int]): IntBuffer =
{
val buffer: IntBuffer = BufferUtils.createIntBuffer(data.length)
buffer.put(data)
buffer.flip // Make the buffer readable
buffer
}
private def prepareFloatBuffer(data: Array[Float]): FloatBuffer =
{
val buffer: FloatBuffer = BufferUtils.createFloatBuffer(data.length)
buffer.put(data)
buffer.flip // Make the buffer readable
buffer
}
def render(): Unit =
{
glBindVertexArray(vao)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo)
glEnableVertexAttribArray(0) // Vertices are in zero
glEnableVertexAttribArray(1) // Texture Coords are in one
glDrawElements(GL_TRIANGLES, this.indices.length, GL_UNSIGNED_INT, 0)
glDisableVertexAttribArray(1)
glDisableVertexAttribArray(0)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
}
}
The data (positions, textureCoordinates) is the same I used before, with two distinct VBOs for them.
now:
glVertexAttribPointer(0, 3, GL_FLOAT, false, 4*5, 0)
glVertexAttribPointer(1, 2, GL_FLOAT, false, 4*5, 4*3)
How do I calculate these strides and offsets you ask?
Well, position is a Vector3(x, y, z) so 3 floats. Texture coordinates are two floats.
3 + 2 = 5
The size of a float is... well, I thought it was 4 bytes. (according to http://wiki.lwjgl.org/wiki/The_Quad_interleaved it is in Java)
That would give 20, or 4*5
The offset for the texture coordinates would be calculated the same (3 * 4) for each coordinates of the position
Now, the outcome doesn't look too good...
can you guess what it actually should be? (Spoiler: A cube)
So, I figure that either my maths is totally broken or that a Float maybe has a different size in Scala?
In Java I could do Float.size, but Scala doesn't have anything the like it seems.
In C++ I'd define a struct and do:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, textureCoordinates));
Your problem is not sizeof float, but the data layout in your buffer. The statement
val interleavedBuffer: FloatBuffer = prepareFloatBuffer(positions ++ textureCoordinates)
Creates a buffer of the layout
xyz[0],xyz[1],…,xyz[n],st[0],st[1],…,st[m]
However what you configure OpenGL to expect is
xyz[0],st[0],xyz[1],st[1],…,xyz[n],st[n]
You can either properly interleave the attributes in the buffer, your you tell OpenGL that each attribute's elements are contiguous (0 stride, or the size of exactly one element of that attribute, i.e. 3*4 for xyz and 2*4 for st) and pass offsets to where each subbuffer start.
I have been starting to dive into OpenGL ES 2.0 the last couple days, but I still get really faulty results. One thing I do not quite understand, is how I am supposed to set up my buffers correctly.
I would like to create a shape like this: A kind of tent, if you like, without the left and right side.
3_______________________2
|\ /|
| \_ _ _ _ _ _ _ _ _ _/ |
| /4 5\ |
|/_____________________\|
0 1
So let's start with my Texture/Indices/Vertices Array:
That is what i set up :
#define RECT_TOP_R {1, 1, 0}
#define RECT_TOP_L {-1, 1, 0}
#define RECT_BOTTOM_R {1, -1, 0}
#define RECT_BOTTOM_L {-1, -1, 0}
#define BACK_RIGHT {1, 0, -1.73}
#define BACK_LEFT {-1, 0, -1.73}
const GLKVector3 Vertices[] = {
RECT_BOTTOM_L, //0
RECT_BOTTOM_R, //1
RECT_TOP_R, //2
RECT_TOP_L, //3
BACK_LEFT, //4
BACK_RIGHT //5
};
const GLKVector4 Color[] = {
{1,0,0,1},
{0,1,0,1},
{0,0,1,1},
{0,1,0,1},
{1,0,0,1},
{0,1,0,1},
{0,0,1,1},
{0,1,0,1}
};
const GLubyte Indices[] = {
0,1,3,
2,4,5,
0,1
};
const GLfloat texCoords[] = {
0,0,
1,0,
0,1,
1,1,
1,1,
0,0,
0,0,
1,0
};
Here I generate/bind the buffers.
glGenBuffers(1, &vertexArray);
glBindBuffer(GL_ARRAY_BUFFER, vertexArray);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition,3,GL_FLOAT,GL_FALSE,sizeof(Vertices),0);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
glGenBuffers(1, &colArray);
glEnableVertexAttribArray(GLKVertexAttribColor);
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_FLOAT, GL_FALSE, sizeof(Color), 0);
glBufferData(GL_ARRAY_BUFFER, sizeof(Color), Color, GL_STATIC_DRAW);
glGenBuffers(1, &texArray);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(texCoords),0);
glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords), texCoords, GL_STATIC_DRAW);
So I have a questions regarding buffers:
What is the difference between GL_ARRAY_BUFFER and GL_ELEMENT_ARRAY_BUFFER ?
Here is the gelegate method, which is called whenever it redraws:
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
self.contentScaleFactor = 2.0;
self.opaque = NO;
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
[self.effect prepareToDraw];
glDrawElements(GL_TRIANGLE_STRIP, sizeof(Indices), GL_UNSIGNED_BYTE, 0);
}
So, the code obviously does not work accordingly. Could you please help me ? I have been trying to get it to work, but I am losing my nerves.
Ok, so I definitely did something wrong there. I reused code from a website which basically stored all the Vertex data in one struct. I, however, have changed the code, in that I have separated the individual attribute arrays (colors, texture coordinates) into individual arrays. Before, the struct was buffered on its own, so the struct was processed by the GPU as a whole with the texture array and the color array. Now - after my changes - I need to generate and bind those buffers individually.
Another problem I could partly resolve was the one with the indices and texture mapping. I do not know whether I understood that right, but if I assign the texture coordinates (x,y) to a certain index and then reuse that index - with the aim of having another texture coordinate in that exact place - then apparently I would not have reason to wonder why everything is messed up.
What I ended up doing did not exactly solve my problem, but I got a whole lot nearer to my set goal and I am quite proud of my learning curve so far as far as openGL is concerned.
This answer is intended for others who might face the same problems and I hope that I do not spread any wrong information here. Please feel free to edit/point out any mistakes.
In response to your own answer, the vertex data in a struct you mentioned is called a struct of arrays. Apple recommend you use this layout.
I am creating a batching class that uses VAOs and VBOs to manage meshes. However, when attempting to use glDrawElements, I get EXEC_BAD_ACCESS and GL_INVALID_OPERATION when binding back to my VAO. Here is the code:
glGenVertexArraysOES(1, &arrayID);
glBindVertexArrayOES(arrayID); // Bind INTO VAO, opening state
// Load shaders and textures and bind them using glUseProgram etc.
glGenBuffers(1, &vboID);
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glGenBuffers(1, &indexID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexID);
glBindVertexArrayOES(0); // Bind AWAY from VAO, saving state
Glfloat data[length];
glBindVertexArrayOES(arrayID); // Bind INTO VAO, open state
unsigned int glfloatsize = sizeof(GLfloat);
unsigned int stride = kStride * glfloatsize;
// Fill Vertex information
glBufferData(GL_ARRAY_BUFFER, vertCount * glfloatsize * kStride, NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, vertCount * glfloatsize * kStride, data);
glEnableVertexAttribArray(kPositionLocation);
glVertexAttribPointer(kPositionLocation, 3, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(0));
glEnableVertexAttribArray(kNormalLocation);
glVertexAttribPointer(kNormalLocation, 3, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(3));
glEnableVertexAttribArray(kColorLocation);
glVertexAttribPointer(kColorLocation, 4, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(6));
glClientActiveTexture(GL_TEXTURE0);
glEnableVertexAttribArray(kTextureLocation);
glVertexAttribPointer(kTextureLocation, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(10));
// Fill Index information
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * sizeof(GLushort), NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexCount * sizeof(GLushort), index);
glBindVertexArrayOES(0); // Bind AWAY from VAO, saving state
// DO OTHER STUFF
/** RENDER (EXPLODES WITH EXEC_BAD_ACCESS) **/
glBindVertexArrayOES(arrayID);
glDrawElements(renderMode, indexCount, GL_UNSIGNED_SHORT, 0);
glBindVertexArrayOES(0);
/** RENDER (WORKS CORRECTLY [index is a scoped array of GLushorts that are uploaded to the VBO above...]) **/
glBindVertexArrayOES(arrayID);
glDrawElements(renderMode, indexCount, GL_UNSIGNED_SHORT, index);
glBindVertexArrayOES(0);
Any idea why I am receiving EXEC_BAD_ACCESS when attempting to use a GL_ELEMENT_ARRAY_BUFFER VBO?
Are you sure that following statements are true?
all OpenGL function doesn't set error - call glGetError after EACH of glXYZ function and check the result.
kStride >= 24
length == vertCount * kStride
index array has indexCount elements with GLushort type
all elements of index array has value less than vertCount value
there are no other glEnableVertexAttribArray calls
How much memory will consume a texture loaded with this method ?
With this method, will a 1024x1024 texture consume 4MB anyway ? ( regardless of loading it as RGBA4444 ) ?
-(void)loadTexture:(NSString*)nombre {
CGImageRef textureImage =[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:nombre ofType:nil]].CGImage;
if (textureImage == nil) {
NSLog(#"Failed to load texture image");
return;
}
// Dimensiones de nuestra imagen
imageSizeX= CGImageGetWidth(textureImage);
imageSizeY= CGImageGetHeight(textureImage);
textureWidth = NextPowerOfTwo(imageSizeX);
textureHeight = NextPowerOfTwo(imageSizeY);
GLubyte *textureData = (GLubyte *)calloc(1,textureWidth * textureHeight * 4);
CGContextRef textureContext = CGBitmapContextCreate(textureData, textureWidth,textureHeight,8, textureWidth * 4,CGImageGetColorSpace(textureImage),kCGImageAlphaPremultipliedLast );
CGContextDrawImage(textureContext, CGRectMake(0.0, 0.0, (float)textureWidth, (float)textureHeight), textureImage);
/**************** Convert data to RGBA4444******************/
//Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRGGGGBBBBAAAA"
void *tempData = malloc(textureWidth * textureHeight * 2);
unsigned int* inPixel32 = (unsigned int*)textureData;
unsigned short* outPixel16 = (unsigned short*)tempData;
for(int i = 0; i < textureWidth * textureHeight ; ++i, ++inPixel32)
*outPixel16++ =
((((*inPixel32 >> 0) & 0xFF) >> 4) << 12) | // R
((((*inPixel32 >> 8) & 0xFF) >> 4) << 8) | // G
((((*inPixel32 >> 16) & 0xFF) >> 4) << 4) | // B
((((*inPixel32 >> 24) & 0xFF) >> 4) << 0); // A
free(textureData);
textureData = tempData;
// Ya no necesitamos el bitmap, lo liberamos
CGContextRelease(textureContext);
glGenTextures(1, &textures[0]);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, textureData);
free(textureData);
//glEnable(GL_BLEND);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
The GL ES 1.1.12 specification (pdf) has this to say on the subject:
The GL stores the resulting texture with internal component resolutions of its
own choosing. The allocation of internal component resolution may vary based
on any TexImage2D parameter (except target), but the allocation must not be a
function of any other state and cannot be changed once established.
However, according to the iphone dev center, RGBA4444 is natively supported. So I would expect it to consume 2MB with your code snippet. Do you have reasons to doubt it's using 2MB ?
In OpenGL, there is no way to find out how much video-memory a texture uses.
Also there is no single answer to the question to start with. Such details depend on graphic card, driver version, platform..
From the OpenGL ES Programming Guide for iOS:
If your application cannot use
compressed textures, consider using a
lower precision pixel format. A
texture in RGB565, RGBA5551, or
RGBA4444 format uses half the memory
of a texture in RGBA8888 format. Use
RGBA8888 only when your application
needs that level of quality.
Above that paragraph, they also really recommend using PVRTC compressed textures, because they save even more memory.