I am trying to render two cubes with textures ("body" and "head"). There is a single texture atlas which has all cube faces. To achieve this, I have created a class to encapsulate all the code which draws a cube: MSTexturedCube.
// In MSTexturedCube.h
#import <Foundation/Foundation.h>
#import <GLKit/GLKit.h>
#interface MSTexturedCube : NSObject {
GLKVector3 _vertices[8];
GLKVector3 _triangleVertices[36];
GLKVector2 _textureVertices[36];
}
#property (nonatomic, assign) float xRotationAngle;
#property (nonatomic, assign) float yRotationAngle;
#property (nonatomic, assign) GLKVector3 scale; // x, y, z scale
#property (nonatomic, assign) GLKVector3 translation; // x, y, z translation
- (id)initWithTexture:(GLKTextureInfo *)textureInfo vertexData:(GLKVector2[36])textureVertexData;
- (id)initWithTexture:(GLKTextureInfo *)textureInfo frontFaceCoords:(GLKVector2[4])front right:(GLKVector2[4])right back:(GLKVector2[4])back left:(GLKVector2[4])left top:(GLKVector2[4])top bottom:(GLKVector2[4])bottom;
- (void)draw;
- (void)setVertices:(GLKVector3[8])verticesArray;
#end
// In MSTexturedCube.m
#import "MSTexturedCube.h"
static int vertexIndices[36] = {
// Front
0, 1, 2,
0, 2, 3,
// Right
1, 5, 6,
1, 6, 2,
// Back
5, 4, 7,
5, 7, 6,
// Left
4, 0, 3,
4, 3, 7,
// Top
3, 2, 6,
3, 6, 7,
// Bottom
4, 5, 1,
4, 1, 0,
};
#interface MSTexturedCube ()
#property (nonatomic, strong) GLKBaseEffect *effect;
#property (nonatomic, strong) GLKTextureInfo *textureInfo;
- (void)setupOpenGL;
#end
#implementation MSTexturedCube
#synthesize effect = _effect;
#synthesize textureInfo = _textureInfo;
#synthesize xRotationAngle = _xRotationAngle;
#synthesize yRotationAngle = _yRotationAngle;
#synthesize scale = _scale;
#synthesize translation = _translation;
// Init methods
...
- (void)draw {
// Create matrices
GLKMatrix4 yRotation = GLKMatrix4MakeYRotation(self.yRotationAngle);
GLKMatrix4 xRotation = GLKMatrix4MakeXRotation(self.xRotationAngle);
GLKMatrix4 scale = GLKMatrix4MakeScale(self.scale.x, self.scale.y, self.scale.z);
GLKMatrix4 translation = GLKMatrix4MakeTranslation(self.translation.x, self.translation.y, self.translation.z);
GLKMatrix4 modelMatrix = GLKMatrix4Multiply(translation, GLKMatrix4Multiply(xRotation, GLKMatrix4Multiply(yRotation, scale)));
GLKMatrix4 viewMatrix = GLKMatrix4MakeLookAt(0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
self.effect.transform.modelviewMatrix = GLKMatrix4Multiply(viewMatrix, modelMatrix);
self.effect.transform.projectionMatrix = GLKMatrix4MakePerspective(0.25 * M_PI, 1.0, 2.0, 500.0);
// Set texture properties if a texture is set
if (self.textureInfo) {
self.effect.texture2d0.envMode = GLKTextureEnvModeReplace;
self.effect.texture2d0.target = GLKTextureTarget2D;
self.effect.texture2d0.name = self.textureInfo.name;
}
// Prepare the effect to draw after creating matrices
[self.effect prepareToDraw];
// Set texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(self.textureInfo.target, self.textureInfo.name);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
// Set vertices
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, _triangleVertices);
// Set texture (if set)
if (self.textureInfo) {
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, _textureVertices);
}
// Draw
glDrawArrays(GL_TRIANGLES, 0, 36);
// Disable arrays
glDisableVertexAttribArray(GLKVertexAttribPosition);
glDisableVertexAttribArray(GLKVertexAttribTexCoord0);
}
// Getters & setters
...
#pragma mark - Private methods
- (void)setupOpenGL {
// Set vertices array
_vertices[0] = GLKVector3Make(-0.5, -0.5, 0.5); // Left bottom front
_vertices[1] = GLKVector3Make( 0.5, -0.5, 0.5); // Right bottom front
_vertices[2] = GLKVector3Make( 0.5, 0.5, 0.5); // Right top front
_vertices[3] = GLKVector3Make(-0.5, 0.5, 0.5); // Left top front
_vertices[4] = GLKVector3Make(-0.5, -0.5, -0.5); // Left bottom back
_vertices[5] = GLKVector3Make( 0.5, -0.5, -0.5); // Right bottom back
_vertices[6] = GLKVector3Make( 0.5, 0.5, -0.5); // Right top back
_vertices[7] = GLKVector3Make(-0.5, 0.5, -0.5); // Left top back
// Set the triangle vertices
for (int i = 0; i < 36; i++) {
_triangleVertices[i] = _vertices[vertexIndices[i]];
}
self.effect = [[GLKBaseEffect alloc] init];
}
In my GLKViewController subclass, which implements the GLKViewDelegate and the GLKViewControllerDelegate, I create two MSTexturedCube objects which I initialize with the necessary vertex data and texture vertex data. In the GLKViewDelegate I wrote this function:
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
// Set background color
glClearColor(0.5, 0.5, 0.5, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// Set properties
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
[self.cubes makeObjectsPerformSelector:#selector(draw)];
}
It works perfectly but for one problem: when I rotate the view, the second object in the cubes array renders without any problem, but the first object, even if it's nearer to the camera, appears absolutely transparent if it has the other object below. If it has only the background below it renders also opaque. ¿How can I fix this problem, and make objects respect the z-order?
I solved my problem.
I forgot to add to my EAGLContext a depth buffer by setting my GLView's drawableDepthFormat property to GLKViewDrawableDepthFormat16.
Although it doesn't appear in the question, I had tried to add the following two lines in the GLView's delegate method, but they did nothing:
glDepthFunc(GL_LEQUAL);
glClear(GL_DEPTH_BUFFER_BIT);
Obviously, because the buffer was not set. When I set GLKViewDrawableDepthFormat16 and I add this two lines it works perfectly.
Related
I am following along with an exercise in O'Reilly's iOS Game Development Cookbook to draw a square with OpenGL using textures. This follows a previous recipe for creating a square without texture, and that worked fine. However, when I run the code with texture, as below, I see a triangle rather than a square. Also the triangle is not the shape I would expect if only one of the two triangles I am using to draw the square were drawn. Those are right triangles, whereas the triangle that is actually appearing is isosceles.
//Modified with addition of textureCoordinates
typedef struct {
GLKVector3 position;
GLKVector2 textureCoordinates;
} Vertex;
//Modified with addition of second set of braces{} per line
const Vertex SquareVertices[] = {
{{-1, -1, 0}, {0,0}},
{{1, -1, 0}, {1,0}},
{{1, 1, 0}, {1, 1}},
{{-1, 1, 0}, {0, 1}},
};
const GLubyte SquareTriangles[] = {
0, 1, 2,
2, 3, 0
};
#interface ViewController ()
{
GLuint _vertexBuffer;
GLuint _indexBuffer;
GLKBaseEffect *_squareEffect;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
GLKView *view = (GLKView *)self.view;
view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:view.context];
//Create the vertex array buffer in which opengl will store vertices
//tell opengl to give us a buffer
glGenBuffers(1, &_vertexBuffer);
//make this buffer be the active array buffer
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
//put this data into acive array buffer
//it's as big as square vertices array so we can use the data from that array
//also this data isn't going to change
glBufferData(GL_ARRAY_BUFFER, sizeof(SquareVertices), SquareVertices, GL_STATIC_DRAW);
//now do same for index buffer, which shows which vertices to use to draw triangle
glGenBuffers(1, &_indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(SquareTriangles), SquareTriangles, GL_STATIC_DRAW);
//prepare gl effect
_squareEffect = [[GLKBaseEffect alloc] init];
_squareEffect.texture2d0.name = texture.name;
//first set up projection matrix
float aspectRatio = self.view.bounds.size.width/self.view.bounds.size.height;
float fieldOfViewDegrees = 60.0;
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(fieldOfViewDegrees), aspectRatio, 0.1, 10.0);
_squareEffect.transform.projectionMatrix = projectionMatrix;
//next we describe how the square should be positions (6 units from camera)
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -6.0f);
_squareEffect.transform.modelviewMatrix = modelViewMatrix;
//CODE ADDED TO ADD TEXTURE
NSString *imagePath = [[NSBundle mainBundle] pathForResource:#"3px-tile" ofType:#"png"];
GLKTextureInfo *texture = [GLKTextureLoader textureWithContentsOfFile:imagePath options:nil error:&error];
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
//tell the effect it should prepare opengl to draw using configured settings
[_squareEffect prepareToDraw];
//opengl alreayd knows the vertex array contains vertex data. we now tell how to use
//tell opengl how the data is laid out for position of each vertex in vertex array
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 0, 0);
//how that open gl knows where to find vertex positions, can draw them
int numberOfVertices = sizeof(SquareTriangles)/sizeof(SquareTriangles[0]);
glDrawElements(GL_TRIANGLES, numberOfVertices, GL_UNSIGNED_BYTE, 0);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, textureCoordinates));
}
#end
Here's what I see:
Here's what I've tried:
Back tracing to original square code - it still works if I change the code back
Changing the location in the code where I add the texture - doesn't change anything, the triangle remains.
Why do I have a triangle and how can I get the square back?
Your first call of glVertexAttribPointer
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 0, 0);
Has a stride of 0. This is may be incorrect, be safe and change it to sizeof(Vertex) (as it is in the second call).
If stride is 0, the generic vertex attributes are understood to be tightly packed in the array. The initial value is 0.
Your second call of glVertexAttribPointer should be moved in front of your glDrawElements, although this is most likely not a problem right now.
I'm not sure if this solves the problem, but I can't comment yet so I have to submit an answer. Please update the code if you've fixed it.
I'm creating a view using GLKit to experiment with opengl in iOS. The view has the following load method:
- (void)viewDidLoad
{
[super viewDidLoad];
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!self.context) {
NSLog(#"Failed to create ES context");
}
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
[EAGLContext setCurrentContext:self.context];
self.effect = [[GLKBaseEffect alloc] init];
}
And in a draw method for a shape I have the following code:
float vertices[] = {
-1, -1,
1, -1,
0, 1};
glEnableVertexAttribArray(GLKVertexAttribPosition);
glColor4f(0.0, 1.0, 0.0, 1.0);
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glDrawArrays(GL_TRIANGLES, 0, sizeof(vertices));
glDisableVertexAttribArray(GLKVertexAttribPosition);
This code will draw a triangle in the center of the screen, however the triangle is white. I set the current color to green with the line glColor4f(0.0, 1.0, 0.0, 1.0) but this does not change the current drawing color. How can I change the color of the triangle?
OpenGL 2.0 uses shaders to rasterize the geometry. You need to tell your shader what color you want to see. The way to do this is usually to set the color attribute of the vertices to what you want, I haven't used GLKit but I see that it has a GLKVertexAttribColor. Try setting it to the color you want with:
GLfloat triangle_colors[] = {
1.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
1.0, 0.0, 0.0, 1.0,
};
glVertexAttribPointer(
GLKVertexAttribColor, // attribute
4, // number of elements per vertex, here (r,g,b,a
GL_FLOAT, // the type of each element
GL_FALSE, // take our values as-is
0, // no extra data between each position
triangle_colors
);
glColor4f is not an ES 2.0 call. You must have pulled in an ES 1.0 header file to get that to compile.
The easiest approach if you just want to use a solid color is to define a uniform variable in your shader, and pass in your color as a uniform. Or you can make the color an additional vertex attribute.
If you're using pre-baked shaders provided by GLKit, you should be able to pass in colors using GLKVertexAttribColor, very similar to the way you specify your vertex positions with GLKVertexAttribPosition.
I'm trying to render a cube with a texture on its sides, on OpenGLES 2.0 iOS. Part of the texture has an alpha transparency, and I want to see the back of the face that is behind it. To render both sides of the cube side, I've used
glDisable(GL_CULL_FACE);
and to enable transparency,
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
before drawing. And my cubes are rendered creating instances of the following class:
// In MSTexturedCube.h
#import <Foundation/Foundation.h>
#import <GLKit/GLKit.h>
#interface MSTexturedCube : NSObject {
GLKVector3 _vertices[8];
GLKVector3 _triangleVertices[36];
GLKVector2 _textureVertices[36];
}
#property (nonatomic, assign) float xRotationAngle;
#property (nonatomic, assign) float yRotationAngle;
#property (nonatomic, assign) GLKVector3 scale; // x, y, z scale
#property (nonatomic, assign) GLKVector3 translation; // x, y, z translation
- (id)initWithTexture:(GLKTextureInfo *)textureInfo vertexData:(GLKVector2[36])textureVertexData;
- (id)initWithTexture:(GLKTextureInfo *)textureInfo frontFaceCoords:(GLKVector2[4])front right:(GLKVector2[4])right back:(GLKVector2[4])back left:(GLKVector2[4])left top:(GLKVector2[4])top bottom:(GLKVector2[4])bottom;
- (void)draw;
- (void)setVertices:(GLKVector3[8])verticesArray;
#end
// In MSTexturedCube.m
#import "MSTexturedCube.h"
static int vertexIndices[36] = {
// Front
0, 1, 2,
0, 2, 3,
// Right
1, 5, 6,
1, 6, 2,
// Back
5, 4, 7,
5, 7, 6,
// Left
4, 0, 3,
4, 3, 7,
// Top
3, 2, 6,
3, 6, 7,
// Bottom
4, 5, 1,
4, 1, 0,
};
#interface MSTexturedCube ()
#property (nonatomic, strong) GLKBaseEffect *effect;
#property (nonatomic, strong) GLKTextureInfo *textureInfo;
- (void)setupOpenGL;
#end
#implementation MSTexturedCube
#synthesize effect = _effect;
#synthesize textureInfo = _textureInfo;
#synthesize xRotationAngle = _xRotationAngle;
#synthesize yRotationAngle = _yRotationAngle;
#synthesize scale = _scale;
#synthesize translation = _translation;
// Init methods
...
- (void)draw {
// Create matrices
GLKMatrix4 yRotation = GLKMatrix4MakeYRotation(self.yRotationAngle);
GLKMatrix4 xRotation = GLKMatrix4MakeXRotation(self.xRotationAngle);
GLKMatrix4 scale = GLKMatrix4MakeScale(self.scale.x, self.scale.y, self.scale.z);
GLKMatrix4 translation = GLKMatrix4MakeTranslation(self.translation.x, self.translation.y, self.translation.z);
GLKMatrix4 modelMatrix = GLKMatrix4Multiply(translation, GLKMatrix4Multiply(xRotation, GLKMatrix4Multiply(yRotation, scale)));
GLKMatrix4 viewMatrix = GLKMatrix4MakeLookAt(0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
self.effect.transform.modelviewMatrix = GLKMatrix4Multiply(viewMatrix, modelMatrix);
self.effect.transform.projectionMatrix = GLKMatrix4MakePerspective(0.25 * M_PI, 1.0, 2.0, 500.0);
// Set texture properties if a texture is set
if (self.textureInfo) {
self.effect.texture2d0.envMode = GLKTextureEnvModeReplace;
self.effect.texture2d0.target = GLKTextureTarget2D;
self.effect.texture2d0.name = self.textureInfo.name;
}
// Prepare the effect to draw after creating matrices
[self.effect prepareToDraw];
// Set texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(self.textureInfo.target, self.textureInfo.name);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
// Set vertices
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, _triangleVertices);
// Set texture (if set)
if (self.textureInfo) {
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, _textureVertices);
}
// Draw
glDrawArrays(GL_TRIANGLES, 0, 36);
// Disable arrays
glDisableVertexAttribArray(GLKVertexAttribPosition);
glDisableVertexAttribArray(GLKVertexAttribTexCoord0);
}
// Getters & setters
...
#pragma mark - Private methods
- (void)setupOpenGL {
// Set vertices array
_vertices[0] = GLKVector3Make(-0.5, -0.5, 0.5); // Left bottom front
_vertices[1] = GLKVector3Make( 0.5, -0.5, 0.5); // Right bottom front
_vertices[2] = GLKVector3Make( 0.5, 0.5, 0.5); // Right top front
_vertices[3] = GLKVector3Make(-0.5, 0.5, 0.5); // Left top front
_vertices[4] = GLKVector3Make(-0.5, -0.5, -0.5); // Left bottom back
_vertices[5] = GLKVector3Make( 0.5, -0.5, -0.5); // Right bottom back
_vertices[6] = GLKVector3Make( 0.5, 0.5, -0.5); // Right top back
_vertices[7] = GLKVector3Make(-0.5, 0.5, -0.5); // Left top back
// Set the triangle vertices
for (int i = 0; i < 36; i++) {
_triangleVertices[i] = _vertices[vertexIndices[i]];
}
self.effect = [[GLKBaseEffect alloc] init];
}
Now, the problem is that when I create a cube with the front face transparent and all the others opaque, this face acts as if there were nothing behind, showing the background. However, if I put a smaller opaque cube inside, it's rendered perfectly and I can see it through the transparent face of the biggest cube. Does anyone know why this happens, and how I could fix it?
Thanks in advance! If you need more information about my code, ask me.
Finally I implemented a workaround. It consists in creating two MSTexturedCube objects, with exactly the same vertices and texture vertices, and render one with cull facing enabled and glCullFace(GL_BACK), and the other one with glCullFace(GL_FRONT). It works exactly as intended.
The reason for that is that transparent side was rendered first in your case and filled depth buffer, back sides did not pass depth test and were not drawn. To solve this you can draw opaque and transparent objects separately, first opaque ones then transparent, also you will have to draw transparent objects from back to front. Another option is to use discard for fully transparent pixels, but this only works for fully transparent case and usually quite slow.
I'm trying to learn OpenGL on iOS. I'm following this tutorial.
http://www.raywenderlich.com/5235/beginning-opengl-es-2-0-with-glkit-part-2
I'm sure I followed the steps carefully, but when I get to the end, only don't see any shapes on the screen.
https://gist.github.com/seanhess/5356598
I do see the background color changing, so OpenGL is working in general. Just the call to glDrawElements doesn't seem to be doing anything.
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0);
He has some demo code, but it's not the same as the code he has you write in the tutorial, and I want to understand why this doesn't work before pasting his in.
Where is the GLKBaseEffect supposed to be drawn? I don't see how we actually used it.
The following seems to be wrong, as all the vertices are the same:
// these are not TRIANGLES, they are vertices
const Vertex Vertices[] = {
{{1, -1, 0}, {1, 0, 0, 1}},
{{1, -1, 0}, {1, 0, 0, 1}},
{{1, -1, 0}, {1, 0, 0, 1}},
{{1, -1, 0}, {1, 0, 0, 1}},
};
Even if everything else is correct, your triangle is degenerate as
all is vertices are coincident. When you fix the coordinates, make
sure that the indices reference the vertices in a counter-clockwise
fashion or, even better for your first experiments, call
glDisable(GL_CULL_FACE);
before drawing. You could try to pick your vertices like this:
// these are not TRIANGLES, they are vertices
const Vertex Vertices[] = {
{{-.9, -.9, 0}, {1, 0, 0, 1}},
{{.9, -.9, 0}, {1, 0, 0, 1}},
{{-.9, .9, 0}, {1, 0, 0, 1}},
{{.9, .9, 0}, {1, 0, 0, 1}},
};
// These are the trianges. Just reference the vertices
const GLubyte Indices[] = {
0, 1, 2,
2, 1, 3
};
Edit
Try to comment away the second part of update:
/*
// Set the projection matrix of the effect. It defines the field of view
float aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 40., 10.0);
self.effect.transform.projectionMatrix = projectionMatrix;
// Rotate about z axis
// the model view matrix is the transform applied to any geometry that the effect renders
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0, 0, -6);
_rotation += 90 * self.timeSinceLastUpdate;
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, GLKMathDegreesToRadians(_rotation), 0, 0, 1);
self.effect.transform.modelviewMatrix = modelViewMatrix;
*/
The matrices you define put your object out of the final image. When you don't see anything on the screen one of the first things you should check are your transformation matrices. In your case, the problem is with the perspective transformation:
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 40., 10.0);
Your translated and rotated object must be located at a distance between the 3rd and 4th parameter of this call in order to be drawn, and actually the 3rd parameter is the distance of the so called near plane, and must be smaller than the 4th parameter, the far plane. A safe bet is usually 1 and 100 for these two parameters.
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 1.0, 100.0);
Edit 2
The following is a working version.
//
// HelloGLKitViewController.m
// OpenGL2Tutorial
//
// Created by Sean Hess on 4/10/13.
// Copyright (c) 2013 Sean Hess. All rights reserved.
//
#import "HelloGLKitViewController.h"
typedef struct {
float Position[3];
float Color[4];
} Vertex;
// these are not TRIANGLES, they are vertices
const Vertex Vertices[] = {
{{-.9, -.9, 0}, {1, 0, 0, 1}},
{{.9, -.9, 0}, {1, 0, 0, 1}},
{{-.9, .9, 0}, {1, 0, 0, 1}},
{{.9, .9, 0}, {1, 0, 0, 1}},
};
// These are the trianges. Just reference the vertices
const GLubyte Indices[] = {
0, 1, 2,
2, 1, 3
};
#interface HelloGLKitViewController () {
GLuint _vertexBuffer;
GLuint _indexBuffer;
float _rotation;
float _currentRed;
BOOL _increasing;
}
#property (nonatomic, strong) EAGLContext * context;
#property (nonatomic, strong) GLKBaseEffect * effect;
#end
#implementation HelloGLKitViewController
// I think this method just fills the objective-c stuff with the information from my c structs
- (void)setupGL {
[EAGLContext setCurrentContext:self.context];
self.effect = [GLKBaseEffect new];
glGenBuffers(1, &_vertexBuffer); // supposed to be an array of buffers, doing that trick again
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); // when I say "GL_ARRAY_BUFFER" I mean _vertexBuffer
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);
}
- (void)tearDownGL {
[EAGLContext setCurrentContext:self.context];
glDeleteBuffers(1, &_vertexBuffer);
glDeleteBuffers(1, &_indexBuffer);
self.effect = nil;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!self.context) NSLog(#"FAiled to create ES Context");
GLKView * view = (GLKView *)self.view;
view.context = self.context;
// will automatically pause when interrupted
self.pauseOnWillResignActive = YES;
[self setupGL];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
[self tearDownGL];
}
#pragma mark - GLKViewDelegate
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
glClearColor(_currentRed, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// have to call after you change any properties in the effect, before drrawing
[self.effect prepareToDraw];
// We don't have to do this again, since we did it above in setupGL
// They're already bound to GL_ARRAY_BUFFER and GL_ELEMENT_ARRAYBUFFER
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
// remember my vertex array contains Vertex(s)
// so we're telling it how to read it
// position is 3 floats, the offset is based on that offsetof function
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*) offsetof(Vertex, Position));
glEnableVertexAttribArray(GLKVertexAttribColor);
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*) offsetof(Vertex, Color));
// 1. pretty much always use triangles
// 2. the number of vertices to render. tricky way to do .length on array
// 3. the data type
// 4. seems like should be pointer to indices. We're using VBOs, so it's already set to the GL_ELEMENT_ARRAY_BUFFER. So give it 0.
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0);
}
- (void)update {
if (_increasing) {
_currentRed += 1.0 * self.timeSinceLastUpdate;
} else {
_currentRed -= 1.0 * self.timeSinceLastUpdate;
}
if (_currentRed >= 1.0) {
_currentRed = 1.0;
_increasing = NO;
}
if (_currentRed <= 0.0) {
_currentRed = 0.0;
_increasing = YES;
}
// Set the projection matrix of the effect. It defines the field of view
float aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 1.0, 100.0);
self.effect.transform.projectionMatrix = projectionMatrix;
// Rotate about z axis
// the model view matrix is the transform applied to any geometry that the effect renders
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0, 0, -6);
_rotation += 90 * self.timeSinceLastUpdate;
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, GLKMathDegreesToRadians(_rotation), 0, 0, 1);
self.effect.transform.modelviewMatrix = modelViewMatrix;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.paused = !self.paused;
NSLog(#"timeSinceLastUpdate: %f", self.timeSinceLastUpdate);
NSLog(#"timeSinceLastDraw: %f", self.timeSinceLastDraw);
NSLog(#"timeSinceFirstResume: %f", self.timeSinceFirstResume);
NSLog(#"timeSinceLastResume: %f", self.timeSinceLastResume);
}
#end
Whenever I attempt to render a textured quad, I end up with a triangular section of the texture distorted:
.
The texture is a PNG created in GIMP, and I've tried two seperate methods of loading the texture (both from Apple's own sample code). Each method of loading the texture produced different results (I don't know if it is different default settings, or if there is a problem with the texture), but I couldn't get either to render proper.
I've tried setting up my indices/verticles/texices in multiple different ways, from suggestions posted in Fastest way to draw quads in OpenGL ES? yet still no luck.
What could I be missing?
Code to Load Texture
- (GLuint)setupTexture:(NSString *)fileName {
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
if (!spriteImage) {
NSLog(#"Failed to load image %#", fileName);
exit(1);
}
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);
GLubyte * spriteData = (GLubyte *) calloc(width*height*4, sizeof(GLubyte));
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4,
CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
CGContextRelease(spriteContext);
GLuint texName;
glGenTextures(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
free(spriteData);
return texName;
}
Texture Coordinates and Verticles
const GLfloat texices[] =
{ 0,1,
1,1,
0,0,
1,0 };
glActiveTexture(GL_TEXTURE0);
glUniform1i(_texturedTextureUniformSlot, 0);
glVertexAttribPointer(_texturedTextureSlot, 2, GL_FLOAT, GL_FALSE, 0, texices);
GLfloat vertices[] = {-1, -1, 0, //bottom left corner
-1, 1, 0, //top left corner
1, 1, 0, //top right corner
1, -1, 0}; // bottom right rocner
GLubyte indices[] = {0,1,2, // first triangle (bottom left - top left - top right)
0,2,3}; // second triangle (bottom left - top right - bottom right)
glVertexAttribPointer(3, GL_FLOAT, 0, vertices);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices);
It looks like your texture coordinates may be wrong (notice the texture appears to be wrapping around the left side).
Here is a snippet of code that I've used in the past:
const float quadPositions[] = { 1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0,
1.0, 1.0, 0.0 };
const float quadTexcoords[] = { 1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0 };
// stop using VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
// setup buffer offsets
glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), quadPositions);
glVertexAttribPointer(ATTRIB_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), quadTexcoords);
// ensure the proper arrays are enabled
glEnableVertexAttribArray(ATTRIB_VERTEX);
glEnableVertexAttribArray(ATTRIB_TEXCOORD0);
// draw
glDrawArrays(GL_TRIANGLES, 0, 2*3);
That should draw a two triangles at z=0. You'll want to setup your projection from -1 to 1 in width and height.
Edit:
Here's a working version of your code:
const GLfloat texices[] = { 0, 0,
0, 1,
1, 1,
1, 0 };
const GLfloat vertices[] = { -1, -1, 0, // bottom left corner
-1, 1, 0, // top left corner
1, 1, 0, // top right corner
1, -1, 0}; // bottom right corner
const GLubyte indices[] = { 0, 2, 1, // first triangle (bottom left - top left - top right)
0, 3, 2 };
// ensure no VBOs or IBOs are bound
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), vertices);
glVertexAttribPointer(ATTRIB_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), texices);
// ensure the proper arrays are enabled
glEnableVertexAttribArray(ATTRIB_VERTEX);
glEnableVertexAttribArray(ATTRIB_TEXCOORD0);
glDisable(GL_CULL_FACE);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices);