Question

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?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top