I have an OpenGL ES scene which is made up of about 20 objects. In the render method for each object I have code which scales, rotates and positions (using glmultmatrix) that object in the correct place in each scene (see code below).
My question is how can I then apply a transformation to the entire scene as a whole ? E.g scale / enlarge the entire scene by 2 ?
glPushMatrix();
glLoadIdentity();
//Move some objects.
if (hasAnimations) {
glTranslatef(kBuildingOffset);
//scale
glScalef(kModelScale);
//glMultMatrixf(testAnimation);
zRotation = kBuildingzRotation
xRotation = kBuildingxRotation
yRotation = kBuildingyRotation
glRotatef(yRotation, 0.0f, 1.0, 0.0f);
glRotatef(xRotation, 1.0f, 0.0f, 0.0f);
glRotatef(zRotation, 0.0f, 0.0f, 1.0f);
//NSLog(#"ANIMATION FRAME IS %d", animationFrame);
//NSLog(#"MATRICE IS %f", animationArray[0][0]);
glMultMatrixf(animationArray[animationFrame]);
//glMultMatrixf(matricesArray);
glMultMatrixf(matricePivotArray);
//glMultMatrixf(testAnimation);
}
//First rotate our objects as required.
if ([objectName isEqualToString:#"movingobject1"]) {
glTranslatef(kFan1Position);
glScalef(kModelScale);
glMultMatrixf(matricesArray);
glTranslatef(0, 0, 0);
zRotation +=kFanRotateSpeed;
yRotation =kyFanFlip;
xRotation = kxFanRotation;
glRotatef(zRotation, 0.0f, 0.0f, 1.0f);
glRotatef(yRotation, 0.0f, 1.0f, 0.0f);
glRotatef(xRotation, 1.0f, 0.0, 0.0f);
glTranslatef(0.0, 0.0, -300);
}
if ([objectName isEqualToString:#"movingobject2"]) {
glTranslatef(kFan2Position);
glScalef(kModelScale);
glMultMatrixf(matricesArray);
glTranslatef(0, 0, 0);
zRotation +=kFanRotateSpeed;
yRotation = kyFanFlip;
xRotation = kxFanRotation;
glRotatef(-kFan3YOffset, 0.0, 1.0, 0.0);
glRotatef(zRotation, 0.0f, 0.0f, 1.0f);
glRotatef(yRotation, 0.0f, 1.0f, 0.0f);
glRotatef(xRotation, 1.0f, 0.0, 0.0f);
glRotatef(kFan3YOffset, 0.0f, 1.0f, 0.0f);
glTranslatef(0.0, 0.0, -300);
}
if ([objectName isEqualToString:#"movingobject3"]) {
glTranslatef(kFan3Position);
glScalef(kModelScale);
glMultMatrixf(matricesArray);
glTranslatef(0, 0, 0);
zRotation +=kFanRotateSpeed;
yRotation =kyFanFlip;
xRotation =kxFanRotation;
glRotatef(-kFan2YOffSet, 0.0, 1.0, 0.0);
glRotatef(zRotation, 0.0f, 0.0f, 1.0f);
glRotatef(yRotation, 0.0f, 1.0f, 0.0f);
glRotatef(xRotation, 1.0f, 0.0f, 0.0f);
glRotatef(kFan2YOffSet, 0.0, 1.0, 0.0);
glTranslatef(0.0, 0.0, -300);
}
//Then position the rest of the scene objects.
if (![objectName isEqualToString:#"movingobject1"])
if (![objectName isEqualToString:#"movingobject2"])
if(![objectName isEqualToString:#"movingobject3"])
if (!hasAnimations) {
glLoadIdentity();
glTranslatef(kBuildingOffset);
//scale
glScalef(kModelScale);
zRotation = kBuildingzRotation
xRotation = kBuildingxRotation
yRotation = kBuildingyRotation
glRotatef(yRotation, 0.0f, 1.0, 0.0f);
glRotatef(xRotation, 1.0f, 0.0f, 0.0f);
glRotatef(zRotation, 0.0f, 0.0f, 1.0f);
if ([Matrices count]!=0) {
glMultMatrixf(matricesArray);
}
if (hasPivotNode) {
glMultMatrixf(matricePivotArray);
}
}
[mesh render];
glPopMatrix();
//restore the matrix
You should be able to achieve this easily enough by pushing the transform matrix you desire on to the matrix stack before you do any of your object-specifc transforms, but then don't load the identity matrix each time you push another matrix onto the stack. Practically speaking, this will transform all subsequent matrix operations. This is the basic pattern...
// Push an identity matrix on the bottom of the stack...
glPushMatrix();
glLoadIdentity();
// Now scale it, so all subsequent transforms will be
// scaled up 2x.
glScalef(2.f, 2.f, 2.f);
foreach(mesh) {
glPushMatrix();
//glLoadIdentity(); This will erase the scale set above.
glDoABunchOfTransforms();
[mesh render];
glPopMatrix();
}
Related
I have a square image of size 320x320, from which I create an OpenGL texture. I use the most basic Vertext and Fragment shaders and I want to display the texture in the entire view. The view (EAGLView derived from UIView as found in many OpenGL iOS samples) is also of size 320x320.
The problem is, the image is drawn on the top left corner, covering only around 50% of the entire view. It does not cover 100% of the view. I don't know why?
Here is my code:
position = glGetAttribLocation(m_shaderProgram, "position");
inputTextureCoordinate = glGetAttribLocation(m_shaderProgram, "inputTextureCoordinate");
inputImageTexture = glGetUniformLocation(m_shaderProgram, "inputImageTexture");
static const GLfloat textureCoordinates[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
static const GLfloat imageVertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
};
[EAGLContext setCurrentContext:context];
glViewport(0, 0, backingWidth, backingHeight); // These are 320, 320
glUseProgram(m_shaderProgram);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, sourceTextureID); // The texture is also of size 320x320
glUniform1i(inputImageTexture, 2);
glVertexAttribPointer(position, 2, GL_FLOAT, 0, 0, imageVertices);
glVertexAttribPointer(textureCoordinate, 2, GL_FLOAT, 0, 0, textureCoordinates);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
Vertext Shader.
attribute vec4 position;
attribute vec4 inputTextureCoordinate;
varying vec2 textureCoordinate;
void main()
{
gl_Position = position;
textureCoordinate = inputTextureCoordinate.xy;
}
Fragment Shader.
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
void main()
{
gl_FragColor = texture2D(inputImageTexture, textureCoordinate);
}
So the problem is that the texture's dimensions were not 2's power. So we need to scale the textureCoordinates accordingly. Inserting following lines solved the problem...
GLfloat textureCoordinates[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
float nearest2sPower = 2;
if (nearest2sPower < backingWidth) {
while (nearest2sPower < backingWidth) {
nearest2sPower *= 2;
}
}
verticalFlipTextureCoordinates[2] = backingWidth/nearest2sPower;
verticalFlipTextureCoordinates[6] = backingWidth/nearest2sPower;
nearest2sPower = 2;
if (nearest2sPower < backingWidth) {
while (nearest2sPower < backingWidth) {
nearest2sPower *= 2;
}
}
verticalFlipTextureCoordinates[1] = backingHeight/nearest2sPower;
verticalFlipTextureCoordinates[3] = backingHeight/nearest2sPower;
I am trying to rotate an object around it's center with the following code :
-(void)render
{
// clear the matrix
glPushMatrix();
glLoadIdentity();
// move to my position
//scale
glScalef(0.000015, 0.000015, 0.000015);
xRotation =280.0;
zRotation =1.0;
yRotation +=0.5;
glRotatef(xRotation, 1.0f, 0.0f, 0.0f);
glRotatef(yRotation, 0.0f, 1.0, 0.0f);
glRotatef(zRotation, 0.0f, 0.0f, 1.0f);
[mesh render];
//restore the matrix
glPopMatrix();
}
This code works correctly and the object rotates around the center. The problem is that I also have a matrix which defines the starting position of the object in the scene called matricesArray. I added a new line to my code to multiply by this matrix and place the object in its correct starting position :
-(void)render
{
// clear the matrix
glPushMatrix();
glLoadIdentity();
// move to my position
//xRotation = 280;
//scale
glScalef(0.000015, 0.000015, 0.000015);
xRotation =280.0;
zRotation =1.0;
yRotation +=0.5;
glRotatef(xRotation, 1.0f, 0.0f, 0.0f);
glRotatef(yRotation, 0.0f, 1.0, 0.0f);
glRotatef(zRotation, 0.0f, 0.0f, 1.0f);
glMultMatrixf(matricesArray); //This is the line I have added to apply the matrix.
[mesh render];
//restore the matrix
glPopMatrix();
}
The problem is that the object now rotates in a wide circle around the screen.
My question is what is the correct order to apply the matrix in my code ?
Thank you in advance.
You need to first rotate then translate to the location you want it to be.
I am trying to draw a rectangle using the GLPaint example project provided by apple. I have tried modifying the vertices but cannot get a rectangle to appear on the screen. The finger painting works perfectly. Am I missing something in my renderRect method?
- (void)renderRect {
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
// Replace the implementation of this method to do your own custom drawing.
static const GLfloat squareVertices[] = {
-0.5f, -0.33f,
0.5f, -0.33f,
-0.5f, 0.33f,
0.5f, 0.33f,
};
static float transY = 0.0f;
glTranslatef(0.0f, (GLfloat)(sinf(transY)/2.0f), 0.0f);
// Render the vertex array
glVertexPointer(2, GL_FLOAT, 0, squareVertices);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Display the buffer
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
The rest of the project is set up stock to allow drawing on the screen but just for reference these are the gl settings that are set.
// Set the view's scale factor
self.contentScaleFactor = 1.0;
// Setup OpenGL states
glMatrixMode(GL_PROJECTION);
CGRect frame = self.bounds;
CGFloat scale = self.contentScaleFactor;
// Setup the view port in Pixels
glOrthof(0, frame.size.width * scale, 0, frame.size.height * scale, -1, 1);
glViewport(0, 0, frame.size.width * scale, frame.size.height * scale);
glMatrixMode(GL_MODELVIEW);
glDisable(GL_DITHER);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnable(GL_BLEND);
// Set a blending function appropriate for premultiplied alpha pixel data
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_POINT_SPRITE_OES);
glTexEnvf(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE);
glPointSize(width / brushScale);
static const GLfloat squareVertices[] = {
30.0f, 300.0f,//-0.5f, -0.33f,
280.0f, 300.0f,//0.5f, -0.33f,
30.0f, 170.0f,//-0.5f, 0.33f,
280.0f, 170.0f,//0.5f, 0.33f,
};
That's definitely too much. OpenGL has normalized screen coords in range [-1..1]. So you have to convert device coords to normalized ones.
Issues are:
(1) the following code:
glMatrixMode(GL_PROJECTION);
CGRect frame = self.bounds;
CGFloat scale = self.contentScaleFactor;
// Setup the view port in Pixels
glOrthof(0, frame.size.width * scale, 0, frame.size.height * scale, -1, 1);
glViewport(0, 0, frame.size.width * scale, frame.size.height * scale);
Establishes that the on-screen coordinates range from (0, 0) in the lower left to frame.size in the upper right. In other words, one OpenGL unit is one iPhone point. So your array of:
static const GLfloat squareVertices[] = {
-0.5f, -0.33f,
0.5f, -0.33f,
-0.5f, 0.33f,
0.5f, 0.33f,
};
Is less than 1 pixel in size.
(2) you have the following in the setup:
brushImage = [UIImage imageNamed:#"Particle.png"].CGImage;
/* ...brushImage eventually becomes the current texture... */
glEnable(GL_TEXTURE_2D);
You subsequently fail to supply texture coordinates for your quad. Probably you want to disable GL_TEXTURE_2D.
So the following:
static const GLfloat squareVertices[] = {
0.0f, 0.0f,
0.0, 10.0f,
90.0, 0.0f,
90.0f, 10.0f,
};
glDisable(GL_TEXTURE_2D);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
// Render the vertex array
glVertexPointer(2, GL_FLOAT, 0, squareVertices);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
Will produce a white quad 90 points wide and 10 points tlal in the lower left of the screen.
I am executing the following, which I have derived from a few different tutorials (Just a single render pass, initialisation code not shown but works fine for untextured primitives):
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(0, xSize, 0, ySize, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_SRC_COLOR);
GLuint texture[1];
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
int width = 50;
int height = 50;
void* textureData = malloc(width * height * 4);
CGColorSpaceRef cSp = CGColorSpaceCreateDeviceRGB();
CGContextRef ct = CGBitmapContextCreate(textureData, width, height, 8, width*4, cSp, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGContextSetRGBFillColor(ct, 0, 1, 0, 1);
CGContextFillRect(ct, CGRectMake(0, 0, 50, 50));
CGContextRelease(ct);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);
float verts[] = {
0.0f, 0.0f, 0.0f,
50.0f, 0.0f, 0.0f,
0.0f, 50.0f, 0.0f,
50.0f, 50.0f, 0.0f
};
float texCords[] = {
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f
};
glVertexPointer(3, GL_FLOAT, 0, verts);
glTexCoordPointer(2, GL_FLOAT, 0, texCords);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisable(GL_TEXTURE_2D);
The result is a white square. Not the green one as intended. Can anyone spot the error(s) in my code which result in its' failure to render?
I hope to get this working then move it on to text rendering.
The problem is that width and height are not powers of two. There are two solutions:
Use the texture rectangle extension. Set the texture target to GL_TEXTURE_RECTANGLE_ARB instead of GL_TEXTURE_2D. You will have to enable this extension before using it. Note that rectangle textures do not support mipmaps.
Use powers of two for texture dimensions.
The gradient in question is Figure 8-5 from the Quartz 2D Programming Guide, "A radial gradient that varies between a point and a circle".
I'm trying to build a CGGradient object (not a CGShading object, which might be the problem) like so:
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGFloat colors[] =
{
0, 0, 0, 0.9,
0, 0, 0, 0
};
CGGradientRef gradient = CGGradientCreateWithColorComponents(rgb, colors, NULL, sizeof(colors)/(sizeof(colors[0])*sizeof(CGFloat)));
CGContextClipToRect(context, rect);
CGContextDrawRadialGradient(context, gradient, startCenter, startRadius, endCenter, endRadius, gradientDrawingOptions);
CGGradientRelease(gradient);
CGColorSpaceRelease(rgb);
Of course, that isn't exactly right -- the centre points and radii are correct, but the actual gradient doesn't look the same. I just wish Apple had provided the source code for each example! >:(
UPDATE: These color values add the shading on top of other content (drawing from a point out to a circle):
CGFloat colors[] =
{
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.75f
};
Using these color values is pretty close (drawing from a point out to a circle):
CGFloat colors[] =
{
0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 0.0f, 0.0f, 0.75f
};