I am trying to follow a tutorial on Dynamic Textures in iOS by Ray Wenderlich
http://www.raywenderlich.com/3857/how-to-create-dynamic-textures-with-ccrendertexture
but using Cocos2D 2.0 and OpenGL ES 2.0 instead of 1.1. The tutorial begins by drawing a coloured square to the screen with a shadow gradient applied to it, but I cannot get the gradient to render to the coloured square. This part of the tutorial is where OpenGL ES code is sent to the CCRenderTexture, so I figure I must be setting up my OpenGL ES 2.0 code wrong (I have very little experience with OpenGL / OpenGL ES). The OpenGL ES 1.1 code is
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
float gradientAlpha = 0.7;
CGPoint vertices[4];
ccColor4F colors[4];
int nVertices = 0;
vertices[nVertices] = CGPointMake(0, 0);
colors[nVertices++] = (ccColor4F){0, 0, 0, 0 };
vertices[nVertices] = CGPointMake(textureSize, 0);
colors[nVertices++] = (ccColor4F){0, 0, 0, 0};
vertices[nVertices] = CGPointMake(0, textureSize);
colors[nVertices++] = (ccColor4F){0, 0, 0, gradientAlpha};
vertices[nVertices] = CGPointMake(textureSize, textureSize);
colors[nVertices++] = (ccColor4F){0, 0, 0, gradientAlpha};
glVertexPointer(2, GL_FLOAT, 0, vertices);
glColorPointer(4, GL_FLOAT, 0, colors);
glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nVertices);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
which goes between the CCRenderTexture begin and end methods (full code can be found at the above link). My Cocos2D 2.0 / OpenGL ES 2.0 attempt is
float gradientAlpha = 0.7;
CGPoint vertices[4];
ccColor4F colors[4];
int nVertices = 0;
vertices[nVertices] = CGPointMake(0, 0);
colors[nVertices++] = (ccColor4F){0, 0, 0, 0 };
vertices[nVertices] = CGPointMake(textureSize, 0);
colors[nVertices++] = (ccColor4F){0, 0, 0, 0};
vertices[nVertices] = CGPointMake(0, textureSize);
colors[nVertices++] = (ccColor4F){0, 0, 0, gradientAlpha};
vertices[nVertices] = CGPointMake(textureSize, textureSize);
colors[nVertices++] = (ccColor4F){0, 0, 0, gradientAlpha};
// Setup OpenGl ES shader programs
CCGLProgram *positionColourProgram = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionColor];
[rt setShaderProgram:positionColourProgram];
ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position | kCCVertexAttribFlag_Color);
glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_FLOAT, GL_FALSE, 0, colors);
glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nVertices);
where rt is the CCRenderTexture object. There are no errors in the console but the image on the screen is a solid colour square with no gradient. Do I need to use an OpenGL blending function perhaps? Any help would be much appreciated. Thanks in advance.
I have figured out the changes needed to make it \work and I posted my comment in the tutorial's forum http://www.raywenderlich.com/forums//viewtopic.php?f=20&t=512&start=40
Hope it is not too late for you.
To save you time from looking through the forum to find my posting, here it is what I posted there:
I have posted my fix at:
http://www.wfu.edu/~ylwong/download/cocos2d-2.0-texture/
The HelloWorldLayer.mm is the final file incorporated all the changes so you do not have to type them in. The pdf file marks up the changes in case you want to see what the changes are.
Basically, in addition to replacing the statements that are not supported in OpenGLES 2.0, I have to add code to set up the shaders for vertex and fragment. Also, instead of using the range 0 to textureSize in the vertex arrays, I have to use the range -1 to 1, which means that in setting up the vertex arrays, the texture width is now 2, 0 becomes -1, and textureSize becomes 1.
To set up the shaders for that tutorial, we can use the ones that come with Cocos2d or write custom but simple shaders. I have included both methods to choose from.
Hope this helps!
Related
I've implemented a particle engine for a game for iPad. On the iPad Simulator I get a very good framerate with >500 particles (way more than I need). On an iPad itself however, I get completely different results. With just 10 particles (I need a few more than that) I only get a very poor framerate...
As a basis I've taken this tutorial to implement my Particle Emitter class: http://www.71squared.com/en/article/806/iphone-game-programming-tutorial-8-particle-emitter
(uses OpenGL ES 1)
Because I use OpenGL ES 2.0, I wrote my own render method:
- (void) renderParticles:(RenderMode)renderMode ofParticleEmitter:(ParticleEmitter*)particleEmitter xOffset:(int)xoffset yOffset:(int)yoffset
{
PointSprite *vertices = [particleEmitter getVertices];
for (int p = 0; p < particleEmitter.particleCount; p++) {
CC3GLMatrix *modelView = [CC3GLMatrix matrix];
// Translate the Modelviewmatrix
[modelView populateFromTranslation:CC3VectorMake(_cameraX, _cameraY, -5.0)];
[modelView translateByX:vertices[p].x + xoffset];
[modelView translateByY:vertices[p].y + yoffset];
[modelView translateByZ:101.0];
[modelView scaleByX:2.0];
[modelView scaleByY:2.0];
glUniformMatrix4fv(_modelViewUniformT, 1, 0, modelView.glMatrix);
glBindTexture(GL_TEXTURE_2D, [particleEmitter getTexture]);
// Create and Bind a rectangular VBO
[self calcCharacterVBOwithCols:1 rows:1 currentCol:1 currentRow:1];
glVertexAttribPointer(_positionSlotT, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) 0);
glEnableVertexAttribArray(_positionSlotT);
// Fragment Shader value
float opacity = 1.0;
glUniform1f(_opacity, opacity);
// Normal render, add Texture coordinates
// Activate Texturing Pipeline and Bind Texture
glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glEnableVertexAttribArray(_texCoordSlot);
glDrawElements(GL_TRIANGLES, sizeof(IndicesLayer)/sizeof(IndicesLayer[0]), GL_UNSIGNED_BYTE, 0);
glDisableVertexAttribArray(_texCoordSlot);
glDisableVertexAttribArray(_positionSlotT);
[self destroyCharacterVBO];
}
}
Did I miss some essential point on particles? What can I do better to get a better framerate on the device?
The problem seems to have been assigning the particle texture again and again for every single particle even though they all use the same texture.
So by binding the texture
glBindTexture(GL_TEXTURE_2D, [particleEmitter getTexture]);
before looping over all the particles I get a much faster frame rate that is comparable to the simulator.
I'm using GLKit in an OpenGL project. Everything is based on GLKView and GLKBaseEffect (no custom shaders). In my project I have several views that have GLKViews for showing 3D objects, and occasionally several of those view can be "open" at once (i.e. are in the modal view stack).
While until now everything was working great, in a new view I was creating I needed to have a rectangle with texture to simulate a measuring tape for the 3D world of my app. For some unknown reason, in that view only, the texture isn't loaded right into the opengl context: the texture is loaded right by GLKTextureLoader, but when drawing the rectangle is black, and looking at the OpenGL frame in debug, I can see that an empty texture is loaded (there's a reference to a texture, but it's all zeroed out or null).
The shape I'm drawing is defined by: (it was originally a triangle strip, but I switched for triangles to make sure it's not the issue)
static const GLfloat initTape[] = {
-TAPE_WIDTH / 2.0f, 0, 0,
TAPE_WIDTH / 2.0f, 0, 0,
-TAPE_WIDTH / 2.0f, TAPE_INIT_LENGTH, 0,
TAPE_WIDTH / 2.0f, 0, 0,
TAPE_WIDTH / 2.0f, TAPE_INIT_LENGTH, 0,
-TAPE_WIDTH / 2.0f, TAPE_INIT_LENGTH, 0,
};
static const GLfloat initTapeTex[] = {
0, 0,
1, 0,
0, 1.0,
1, 0,
1, 1,
0, 1,
};
I set the effect variable as:
effect.transform.modelviewMatrix = modelview;
effect.light0.enabled = GL_FALSE;
// Projection setup
GLfloat ratio = self.view.bounds.size.width/self.view.bounds.size.height;
GLKMatrix4 projection = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(self.fov), ratio, 0.1f, 1000.0f);
effect.transform.projectionMatrix = projection;
// Set the color of the wireframe.
if (tapeTex == nil) {
NSError* error;
tapeTex = [GLKTextureLoader textureWithContentsOfFile:[[[NSBundle mainBundle] URLForResource:#"ruler_texture" withExtension:#"png"] path] options:nil error:&error];
}
effect.texture2d0.enabled = GL_TRUE;
effect.texture2d0.target = GLKTextureTarget2D;
effect.texture2d0.envMode = GLKTextureEnvModeReplace;
effect.texture2d0.name = tapeTex.name;
And the rendering loop is:
[effect prepareToDraw];
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribPosition, COORDS, GL_FLOAT, GL_FALSE, 0, tapeVerts);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 0, tapeTexCoord);
glDrawArrays(GL_TRIANGLES, 0, TAPE_VERTS);
glDisableVertexAttribArray(GLKVertexAttribPosition);
glDisableVertexAttribArray(GLKVertexAttribTexCoord0);
I've also tested the texture itself in another view with other objects and it works fine, so it's not the texture file fault.
Any help would be greatly appreciated, as I'm stuck on this issue for over 3 days.
Update: Also, there are no glErrors during the rendering loop.
After many many days I've finally found my mistake - When using multiple openGL contexts, it's important to create a GLKTextureLoader using a shareGroup, or else the textures aren't necessarily loaded to the right context.
Instead of using the class method textureWithContentOf, every context needs it's own GLKTextureLoader that is initialized with context.sharegroup, and use only that texture loader for that view. (actually the textures can be saved between different contexts, but I didn't needed that feature of sharegroups).
Easy tutorial http://games.ianterrell.com/how-to-texturize-objects-with-glkit/
I think it will help you.
I am drawing a simple GL_LINE_LOOP on a black background. No matter what I do with the glColorPointer and colors[] array I can't make the lines any other color than white. What am I doing wrong?
I'm relatively new to open gl for iPhone and haven't found an answer on Google or here for my problem so I really appreciate any answers.
//glPushMatrix();
glDisable(GL_TEXTURE_2D);
static const GLubyte colors[] = {
255, 0, 255, 255,
255, 0, 255, 255,
255, 0, 255, 255
};
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState (GL_COLOR_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
glLineWidth(5.0);
GLfloat vertices[] = { -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0 };
glVertexPointer(3, GL_FLOAT, 0, vertices);
glDrawArrays(GL_LINE_LOOP, 0, 3);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glEnable(GL_TEXTURE_2D);
glPopMatrix();
Try disabling texturing...
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,0);
I'm trying to figure out how to work with VBOs, using an OpenGL 2.0 rendering context. I've got a 2D (ortho) rendering context set up, and I can draw a simple rectangle like this:
glBegin(GL_QUADS);
glColor4f(1, 1, 1, 1);
glVertex2f(0, 0);
glVertex2f(0, 10);
glVertex2f(100, 10);
glVertex2f(100, 0);
glEnd;
But when I try to do it with a VBO, it fails. I set up the VBO like this, with the same data as before:
procedure initialize;
const
VERTICES: array[1..8] of single =
(
0, 0,
0, 10,
100, 10,
100, 0
);
begin
glEnable(GL_VERTEX_ARRAY);
glGenBuffers(1, #VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(VERTICES), #VERTICES[1], GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
end;
and I try to draw like this:
begin
glColor4f(1, 1, 1, 1);
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glVertexPointer(2, GL_FLOAT, 0, 0);
glDrawArrays(GL_QUADS, 0, 1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
end;
From everything I've read, that ought to work. I run it through gDEBugger and there are no GL errors, and the data in the VBO is getting loaded correctly, but nothing actually appears when I swap the buffers. Changing the data in the vertex array to use normalized coordinates (from 0..1.0) also ends up displaying nothing. Any idea what I'm doing wrong? (Assume the render context itself is set up correctly and the GL function pointers have all been loaded correctly.)
glDrawArrays(GL_QUADS, 0, 1);
Looks like you're trying to draw a quad with a single vertex. You need three more:
glDrawArrays(GL_QUADS, 0, 4);
Or switch to points:
glDrawArrays(GL_POINTS, 0, 1);
I am displaying text using Quartz. Here is my code:
CGContextRef myContext = UIGraphicsGetCurrentContext();
CGContextSelectFont(myContext, "Helvetica", 12, kCGEncodingMacRoman);
CGContextSetCharacterSpacing(myContext, 8);
CGContextSetTextDrawingMode(myContext, kCGTextFillStroke);
CGContextSetRGBFillColor(myContext, 0, 0, 0, 1);
CGContextSetRGBStrokeColor(myContext, 0, 0, 0, 1);
CGContextSetTextMatrix(myContext,CGAffineTransformMake(1, 0, 0, -1, 0, 0));
CGContextShowTextAtPoint(myContext, textOrigin.x, textOrigin.y,[way.name UTF8String],[way.name length]);
This displays my text the right way up and in the right direction, however I also need to add a rotation to the text using CGAffineTransformMakeRotation(angle);. I can't seem to work out how to aply two affine transforms to the text matrix, though, without one overwriting the other. Any help would be great.
You can combine matrices with CGAffineTransformConcat, e.g.
CGAffineTransform finalTransf = CGAffineTransformConcat(t1, t2);
If you just need to apply rotation to an existing matrix, use CGAffineTransformRotate, e.g.
CGAffineTransform t = CGAffineTransformMake(1, 0, 0, -1, 0, 0);
CGAffineTransform t = CGAffineTransformRotate(t, M_PI/2);
CGContextSetTextMatrix(myContext, t);
To supplement #kennytm's answer, to apply more than two transforms you can do the following:
var t = CGAffineTransformIdentity
t = CGAffineTransformTranslate(t, CGFloat(100), CGFloat(300))
t = CGAffineTransformRotate(t, CGFloat(M_PI_4))
t = CGAffineTransformScale(t, CGFloat(-1), CGFloat(2))
// ... add as many as you want, then apply it to to the view
imageView.transform = t
Go here to see my full answer.