I am currently working on an OPENGL ES 2.0 project. I am making a bar graph application. I have achieved success in making the graph. I am trying to insert text on the graph. I found out that there is no direct solution to achieve the same.
I came across a OPENGL ES 1.1 code, which has the following lines in it.
File: In GLViewController.m (from the drawView function)
Texture2D *textTex = [[Texture2D alloc] initWithString:#"Text"
dimensions:CGSizeMake(100., 40.0)
alignment:UITextAlignmentCenter
font:[UIFont boldSystemFontOfSize:10.0]];
[textTex drawAtPoint:CGPointMake(160.0, 100.0) depth:-1];
In the Texture2D.m file,
- (void) drawAtPoint:(CGPoint)point depth:(CGFloat)depth
{
GLfloat coordinates[] = {
0, _maxT,
_maxS, _maxT,
0, 0,
_maxS, 0
};
GLfloat width = (GLfloat)_width * _maxS,
height = (GLfloat)_height * _maxT;
GLfloat vertices[] = {
-width / 2 + point.x, -height / 2 + point.y, depth,
width / 2 + point.x, -height / 2 + point.y, depth,
-width / 2 + point.x, height / 2 + point.y, depth,
width / 2 + point.x, height / 2 + point.y, depth
};
glBindTexture(GL_TEXTURE_2D, texture.name);
glVertexPointer(3, GL_FLOAT, 0, vertices); //1
glTexCoordPointer(2, GL_FLOAT, 0, coordinates); //2
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
When I tried to convert this code into OPENGL ES 2.0 code, I am not able to find a suitable conversion for lines marked 1 and 2. Can someone please help me convert this code to OPENGL ES 2.0 code?
Or if an anyone is aware of an easier method to write text on screen, please let me know.
OpenGL ES 1.x and 2.x share a lot of concepts when it comes to geometry. You specify vertices, then you join them with geometry, which is turned into pixels. The difference is that you specify custom code in two places: to take the input geometry and figure out where it goes on screen and what stuff it passes on through the pipeline, and to figure out what colour an individual fragment is based on the information coming along the pipeline.
Because ES 1.x wasn't programmable in that sense, it had fixed functionality at both places. Which meant fixed data structures, and hence why you supply data via glVertexPointer, glTexCoordPointer, etc. You're supplying values for the fixed, predetermined fields that OpenGL has written into stone as describing a vertex.
Because ES 2.x is fully programmable and minimalist, it makes no assumptions about what fields describe a vertex and accordingly has no special case data provision methods, such as one to supply locations (glVertexPointer), another to supply texture coordinates (glTexCoordPointer), etc.
In ES 2.x you use glVertexAttribPointer to replace all of the fixed meaning ES 1.x calls. The first parameter identifies which attribute you're specifying by index, that index having been forced by yourself via glBindAttribLocation or left to figure themselves out and requested back via glGetAttribLocation.
You'll also need to write a suitable fragment and pixel shader, essentially just to pass the texture coordinates you nominate on, and to sample the texture per pixel, then pass that on. Should be just one or two lines in the body of each.
Probably you have some sort of framework in place for dealing with this stuff in your graphing. Because ES 2.x is so generic about attributes, it should be possible to reuse much the same stuff.
Related
I'm currently working on an iOS game where, long story short, I need to draw a lot of moving cubes - approximate maximum of 200 per frame. Emphasis on moving because yes, I have indeed Googled away for hours on this topic and have yet to find a suitable solution for fast, efficient drawing of multiple objects where their position updates every frame.
Through my endless amounts of research on this subject most seem to mention VBOs, however I'm not sure this would suit my case where the position of every object changes every frame.
I'm using OpenGL 1 at the moment - I have working code and on generation 3/4+ devices (the ones which support OpenGL 2, ha) it runs at a reasonable framerate - however when testing on my (old, yes) 2nd-gen iPod touch, it is very sluggish and essentially unplayable.
My code comprises of a static array of vertices for a 'cube' and an array containing the position and colour of every cube. My game logic loop updates the position of every cube in the array. At the moment I'm looping through the cube array, calling glTranslatef and glDrawArrays for every cube. From what I've read this is very inefficient, however I'm completely confused as to how you would optimise it. Any ideas?
(maybe I shouldn't be aiming for old, discontinued iOS devices but given my belief that my code is incredibly inefficient, I figure it'll help my future endeavours regardless if I find a way to address this)
For such simple objects I would make one big VBO say 200 Objects * NrVerticesPerCube, put all the data interleaved Vertex,Normal,UV,Vertex,Normal,UV, etc.
I do something similar in a keyframe animation of a beaver in my game, I start with something like this:
glGenBuffers(1, &vboObjects[vboGroupBeaver]);
glBindBuffer(GL_ARRAY_BUFFER, vboObjects[vboGroupBeaver]);
glBufferData(GL_ARRAY_BUFFER, beaverVerts*8*sizeof(GLfloat), 0, GL_STATIC_DRAW);
vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
NSString *path;
path = [[NSBundle mainBundle] pathForResource:#"beaver01" ofType:#"bin"];
NSFileHandle *model = [NSFileHandle fileHandleForReadingAtPath:path];
float vertice[8];
int counter = 0;
while (read([model fileDescriptor], &vertice, 8*sizeof(float))) {
memcpy(vbo_buffer, vertice, 8*sizeof(GLfloat)); // 0
vbo_buffer += 8*sizeof(GLfloat);
counter++;
}
glUnmapBufferOES(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, 0);
This creates my VBO buffer with the correct size (in this case 8 * sizeof(GLfloat) wich is 3 Verts, 3 Normals and 2UV), and copies the first keyframe to the buffer, you could do the same with you initial object positions, or just leave that and compute latter...
Then in each frame I do interpolation between 2 keyframes for each vertex of my beaver, and just make one draw call, this is very fast for the 4029 vertices my beaver has, and works at 60FPS on my iPhone 3G.
For you doing only gltranslates it would be even simpler, just add the values of x,y,z to each vertice of each cube.
You would update it like this:
glBindBuffer(GL_ARRAY_BUFFER, vboObjects[vboGroupBeaver]);
GLvoid* vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
Bind the vbo buffer and mapit to a buffer var.
Calculate the stuff you want on a temp var.
memcpy(vbo_buffer, currentVert, 6*sizeof(GLfloat)); // 0
vbo_buffer += 8*sizeof(GLfloat);
Copy it and update buffer to next object, repeat until all objects updated...
You could also do all the updates in a seperate array and copy the whole array, but then you would be copying extra info that usually doesn't change (normals and UV). Or you could not use interleaved data and copy that...
glUnmapBufferOES(GL_ARRAY_BUFFER);
Unmap the VBO buffer
glVertexPointer(3, GL_FLOAT, 8*sizeof(GLfloat), (GLvoid*)((char*)NULL));
glNormalPointer(GL_FLOAT, 8*sizeof(GLfloat), (GLvoid*)((char*)NULL+3*sizeof(GLfloat)));
glTexCoordPointer(2, GL_FLOAT,8*sizeof(GLfloat), (GLvoid*)((char*)NULL+6*sizeof(GLfloat)));
glDrawArrays(GL_TRIANGLES, 0, beaverVerts);
Setup your draw call, and draw it all...
If you need to rotate objects and not just gltranslate them, you will need to add some matrix multiplications along the way...
EDIT **
ok, making a gltranste by hand is actually very easy (rotation, etc is a bit trickier).
I'm using a an interleaved plane drawed using TRIANGLE_STRIP instead of triangles, but the principle is the same.
float beltInter[] = {
0.0, 0.0, 0.0, // vertices[0]
0.0, 0.0, 1.0, // Normals [0]
6.0, 1.0, // UV [0]
0.0, 480, 0.0, // vertices[1]
0.0, 0.0, 1.0, // Normals [1]
0.0, 1.0, // UV [1]
320.0, 0.0, 0.0, // vertices[2]
0.0, 0.0, 1.0, // Normals [2]
6.0, 0.0, // UV [2]
320.0, 480, 0.0, // vertices[3]
0.0, 0.0, 1.0, // Normals [3]
0.0, 0.0 // UV [3]
};
So this is interleaved vertex, you got vertex then Normals then UV, if you're not using textures substitute UV for color.
The easiest way is to have an array with all the objects inside (made easy if all your objects are the same size) and make the position updates after draw (instead of in the middle of the opengl frame), better still make a seperate thread, create 2 VBOs update one of them while drawing from the other, something like this:
Thread 1 OpenGL DrawFrom VBO0
Thread 2 Game Updates, update positions on internal array and copy to VBO1, set Var saying VBO1 yes ready (so thread 1 only changes from drawing to VBO1 when all the updates are done).
Thread 1 OpenGL DrawFrom VBO1
Thread 2 Game update, same thing but update VBO0
continue with same logic
this is called double buffering and you use it to garanty stability, without this sometimes your game logic will be updating the VBO while the graphics card needs it and the graphics card will have to wait, resulting in lower FPS.
Anyway, back on topic
to make the equivalent to gltranslatef(10,20,30) just do:
int maxvertices = 4;
float x = 10;
float y = 20;
float z = 30;
int counter = 0;
int stride = 8; // stride is 8 = 3 x vertice + 3 x normal + 2 x UV change to 3 x color or 4 x color depending on your needs
glBindBuffer(GL_ARRAY_BUFFER, vboObjects[myObjects]);
GLvoid* vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
while (counter < (maxVertices*8)) {
beltInter[counter] += x; // just sum the corresponding values to each
beltInter[counter+1] += y;
beltInter[counter+2] += z;
memcpy(vbo_buffer, currentVert, 3*sizeof(GLfloat)); // again only copy what you need, in this case only copying the vertices, if your're updating all the data, you can just do a single memcpy at the end instead of these partial ones
vbo_buffer += stride*sizeof(GLfloat); // forward the buffer
counter += stride; // only update the vertex, but you could update everything
}
glUnmapBufferOES(GL_ARRAY_BUFFER);
glVertexPointer(3, GL_FLOAT, stride*sizeof(GLfloat), (GLvoid*)((char*)NULL));
glNormalPointer(GL_FLOAT, stride*sizeof(GLfloat), (GLvoid*)((char*)NULL+3*sizeof(GLfloat)));
glTexCoordPointer(2, GL_FLOAT,stride*sizeof(GLfloat), (GLvoid*)((char*)NULL+6*sizeof(GLfloat)));
glDrawArrays(GL_TRIANGLE_STRIP, 0, maxVertices);
Of course the update values doesn't have to be the same for all the objects, infact using a base array like this you can update all the info as you go along and just have the routine to copy it to VBO when needed.
All this was written from memory on the fly, so there maybe dragons :-)
Hope that helps.
You could optimise quite a bit by sticking all the coords for all your cubes in a single array, and drawing it with a single glDrawArrays call.
I'm not sure why you'd want to split up the cubes into separate arrays, except maybe because it makes your data structure more elegant/object oriented, but that's the first place I'd look at making an improvement.
Dump the cube coordinates in one big array, and give each cube object an index into that array so that you can still keep your update logic fairly compartmentalised (as in, cube n owns the coordinates in the range x to y and is responsible for updating them, but when you actually draw the coordinates you run glDrawArrays directly on the centralised coord array instead of looping through the cube objects and rendering them individually).
I would like to render image in OpenGL ES, pixel by pixel. I want to do it this way because I plan to move those pixels over time to create various effect.
For performance and design reasons I decided to use only every other pixel in both directions (thus reducing their number to one quarter)
I have only very basic understanding of opengl, so I am probably missing some key knowledge to achieve this.
What is the best way to achieve this? Do I have to really render it pixel by pixel? Or can I somehow create texture out of array of pixels?
I would like to make this work on as much devices as possible (so OpenGL ES 1.1 solution is preffered, but if it is not possible or it would be really inconvenient or slow, 2.0 can be used)
I tried to do this using VBO with mixed results. I am not sure I have done it properly, because there are some problems (and it is very slow). Here is my code:
Initialization:
glGenBuffers(1, &pointsVBO);
glBindBuffer(GL_ARRAY_BUFFER, pointsVBO);
glBufferData(GL_ARRAY_BUFFER, 160*240*sizeof(Vertex), 0, GL_DYNAMIC_DRAW);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
Rendering:
- (void)renderPoints:(ImagePixel**)imagePixels {
int count = 160 * 240;
for(int i = 0; i < count; ++i) {
vertices[i].v[0] = imagePixels[i]->positionX;
vertices[i].v[1] = imagePixels[i]->positionY;
vertices[i].color[0] = imagePixels[i]->red;
vertices[i].color[1] = imagePixels[i]->green;
vertices[i].color[2] = imagePixels[i]->blue;
vertices[i].color[3] = 1;
}
glVertexPointer(2, GL_FLOAT, sizeof(Vertex), vertices[0].v);
glColorPointer(4, GL_FLOAT, sizeof(Vertex), vertices[0].color);
// update vbo
GLvoid *vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
memcpy(vbo_buffer, vertices, count * sizeof(Vertex));
glUnmapBufferOES(GL_ARRAY_BUFFER);
// draw contents of vbo
glDrawArrays(GL_POINTS, 0, count);
}
Vertex struct:
typedef struct Vertex
{
float v[2];
float color[4];
} Vertex;
imagePixels array is filled with data from image.
When I do this, I get most of my image but I am missing few last rows and I can see some random pixels around the screen. Is it possible that I hit some limit in drawArrays that uses only portion of data?
Second problem is, that points in second half of columns aren't aligned properly. I guess this is caused by rounding errors in float math when computing position during rendering itself (supplied coordinates are all multiples of 2). Is there any way how to prevent this? I need all points to be aligned in proper grid.
I will provide you with screenshot as soon as I get my iphone back
If you really are wanting to manipulate every pixel, you should probably just use a single full-screen quad in OpenGL and update its texture each frame.
You can create a texture out of a bitmap array of pixels using glTexImage2D.
I would appreciate some help with the following. I'm trying to render a ring shape on top of another object in OpenGL ES 1.1 for an iPhone game. The ring is essentially the difference between two circles.
I have a graphic prepared for the ring itself, which is transparent in the centre.
I had hoped to just create a circle, and apply the texture to that. The texture is a picture of the ring that occupies the full size of the texture (i.e. the outside of the ring touches the four sides of the texture). The centre of the ring is transparent in the graphic being used.
It needs to be transparent in the centre to let the object underneath show through. The ring is rendering correctly, but is a solid black mass in the centre, not transparent. I'd appreciate any help to solve this.
Code that I'm using to render the circle is as follows (not optimised at all: I will move the coords in proper buffers etc for later code, but I have written it this way to just try and get it working...)
if (!m_circleEffects.empty())
{
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
int segments = 360;
for (int i = 0; i < m_circleEffects.size(); i++)
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(m_circleEffects[i].position.x, m_circleEffects[i].position.y, 0);
glBindTexture(GL_TEXTURE_2D, m_Texture);
float radius = 1.764706;
GLfloat circlePoints[segments * 3];
GLfloat textureCoords[segments * 2];
int circCount = 3;
int texCount = 2;
for (GLfloat i = 0; i < 360.0f; i += (360.0f / segments))
{
GLfloat pos1 = cosf(i * M_PI / 180);
GLfloat pos2 = sinf(i * M_PI / 180);
circlePoints[circCount] = pos1 * radius;
circlePoints[circCount+1] = pos2 * radius;
circlePoints[circCount+2] = (float)z + 5.0f;
circCount += 3;
textureCoords[texCount] = pos1 * 0.5 + 0.5;
textureCoords[texCount+1] = pos2 * 0.5 + 0.5;
texCount += 2;
}
glVertexPointer(3, GL_FLOAT, 0, circlePoints);
glTexCoordPointer(2, GL_FLOAT, 0, textureCoords);
glDrawArrays(GL_TRIANGLE_FAN, 0, segments);
}
m_circleEffects.clear();
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
I've been experimenting with trying to create a ring rather than a circle, but I haven't been able to get this right yet.
I guess that the best approach is actually to not create a circle, but a ring, and then get the equivalent texture coordinates as well. I'm still experimenting with the width of the ring, but, it is likely that the radius of the ring is 1/4 width of the total circle.
Still a noob at OpenGL and trying to wrap my head around it. Thanks in advance for any pointers / snippets that might help.
Thanks.
What you need to do is use alpha blending, which blends colors into each other based on their alpha values (which you say are zero in the texture center, meaning transparent). So you have to enable blending by:
glEnable(GL_BLEND);
and set the standard blending functions for using a color's alpha component as opacity:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
But always keep in mind in order to see the transparent object correctly blended over the object behind, you need to render your objects in back to front order.
But if you only use the alpha as a object/no-object indicator (only values of either 0 or 1) and don't need partially transparent colors (like glass, for example), you don't need to sort your objects. In this case you should use the alpha test to discard fragments based on their alpha values, so that they don't pollute the depth-buffer and prevent the behind lying object from being rendered. An alpha test set with
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.5f);
will only render fragments (~pixels) that have an alpha of more than 0.5 and will completely discard all other fragments. If you only have alpha values of 0 (no object) or 1 (object), this is exactly what you need and in this case you don't actually need to enable blending or even sort your objects back to front.
I have a game that renders a bunch of sprites (several hundred), almost all of which are using the same texture. Currently, I'm calling glDrawArrays(...) for each one, which I recently discovered was very inefficient. After doing some research, I've learned that I need to put all my vertices for every sprite into one big vertex buffer, and call glDrawArrays(...) just once using that. However, when I do so it only draws the first sprite, and the other 200 are blank.
blueSpriteVertices[blueBatchNum * 4] = Vertex3DMake(xloc, yloc, zloc);
blueSpriteVertices[blueBatchNum * 4 + 1] = Vertex3DMake(xloc + size, yloc, zloc);
blueSpriteVertices[blueBatchNum * 4 + 2] = Vertex3DMake(xloc, yloc + size, zloc);
blueSpriteVertices[blueBatchNum * 4 + 3] = Vertex3DMake(xloc + size, yloc + size, zloc);
blueBatchNum++;
//^^This block of code^^ is called iteratively, adding data for various sprites
//(around 200) to the vertex array. "xloc", "yloc", etc. are private members of
//this sprite class
//Draw the whole batch
glEnableClientState(GL_VERTEX_ARRAY);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glColor4f(1, 1, 1, 1);
//This code is actually in the Texture2D class implementation, hence "_name"
//and "coordinates"
glBindTexture(GL_TEXTURE_2D, _name);
glVertexPointer(3, GL_FLOAT, 0, blueSpriteVertices);
glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_VERTEX_ARRAY);
I finally solved this problem by using GL_TRIANGLES instead of GL_TRIANGLE_STRIP, and handled the triangle strips manually. By doing so I was able to eliminate all the "strips" that it was interpreting in between my sprites. Works like a charm now, and the batching definitely improved my game's performance astronomically.
Using (GL_TRIANGLES instead of GL_TRIANGLE_STRIP works for me (on Android)
glDrawElements(GL_TRIANGLES, 6 * mSpriteCounter, GL_UNSIGNED_SHORT, (char*) NULL);
The glDrawArrays() last parameter should contain the number of vertices (in your case you have only 4). Also you must have the same number of texture coordinates to match the drawn vertices!
What I want to achieve:
Drawing png files with alpha exactly as it appears originally, without transforming any pixel. This is because the image is very detailed and I don't want to lose any bit of information.
Animating those images by rotating them and moving. No scaling.
Actually I don't want to use any 3rd party libraries like cocos2d. I have been reading blogs about OpenGL ES, also checked Texture2D.m so I have basic idea about drawing primitives in 3D space. As far as I understand if I need to draw and animate an image(sprite?) I can just make rectangle and map texture. But the problem is that I want my png file appear exactly as original, not scaled or rotated.
What is the best technique to achieve points mentioned above? Drawing textured rectangle in orthogonal viewport? How to preserve original size/color of image?
Sorry if question is a bit messed up, I can clarify.
By your description, I don't understand why you'd use OpenGL ES. Using Quartz and layers would enable 1) drawing PNGs 2) rotating and moving them (even scaling if you wished). It would be easier than setting up an orthogonal projection in OpenGL + handling loading of images.
Now, if you really want to go OpenGL, yes, you should setup an orthogonal projection, with view size strictly equal to the screen size, and draw a rectangle of the exact size, with texture mapped with exact 0/1 coordinates. For the color aspect, you can use 8888 format, which is exact, no compression, no color reduction and with full alpha.
I know you said you don't want to use a third party library, but Cocos2D implements 2D graphics with OpenGL, so you can reference CCSprite.m to see how they did it. That said, you may want to consider if this library is appropriate for your application. In order to do your own 3D rendering all you have to do is extend CCSprite and put in your own code, as you can see in the comment below the state is already setup for you and everything.
CCSprite.m:
-(void) draw
{
NSAssert(!usesBatchNode_, #"If CCSprite is being rendered by CCSpriteBatchNode, CCSprite#draw SHOULD NOT be called");
// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
// Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
// Unneeded states: -
BOOL newBlend = NO;
if( blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST ) {
newBlend = YES;
glBlendFunc( blendFunc_.src, blendFunc_.dst );
}
#define kQuadSize sizeof(quad_.bl)
glBindTexture(GL_TEXTURE_2D, [texture_ name]);
long offset = (long)&quad_;
// vertex
NSInteger diff = offsetof( ccV3F_C4B_T2F, vertices);
glVertexPointer(3, GL_FLOAT, kQuadSize, (void*) (offset + diff) );
// color
diff = offsetof( ccV3F_C4B_T2F, colors);
glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (void*)(offset + diff));
// tex coords
diff = offsetof( ccV3F_C4B_T2F, texCoords);
glTexCoordPointer(2, GL_FLOAT, kQuadSize, (void*)(offset + diff));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
if( newBlend )
glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
#if CC_SPRITE_DEBUG_DRAW
CGSize s = [self contentSize];
CGPoint vertices[4]={
ccp(0,0),ccp(s.width,0),
ccp(s.width,s.height),ccp(0,s.height),
};
ccDrawPoly(vertices, 4, YES);
#endif // CC_TEXTURENODE_DEBUG_DRAW
}