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.