如何在OpenGL ES中对立方体进行纹理映射?

5

我在使用OpenGL ES(iPhone)时遇到了很多麻烦,无法使纹理贴图正常工作。

这是我所做的:

  • 建立了一个顶点数组
  • 建立了一个面数组,每个面都引用了顶点数组中每个面的索引
  • 建立了一个颜色数组,以便确定立方体上的每个顶点是哪个。

所有这些都是按照Jeff Lamarche的教程进行的。渲染和移动对象并不成问题。

现在我正在尝试让这个立方体(实际上是一个瓷砖,在Z轴上比X或Y窄)在两个相对的面上贴上纹理(其它面以后再说)。我已经成功地让一个面工作了,但在任何其他面上都没有得到可行的结果。

在OpenGL ES中,最系统的纹理映射对象的方法是什么?有人能看出我的代码中的错误吗?

#import "GLViewController.h"
#import "ConstantsAndMacros.h"
#import "OpenGLCommon.h"
#import "Cube.h"

@implementation GLViewController

@synthesize initDone;
@synthesize tileArray;
@synthesize tileRows;
@synthesize tileCols;
@synthesize cubes;
@synthesize gridOffsetX;
@synthesize gridOffsetY;
@synthesize gridOffsetZ;
@synthesize tileSpacing;

- (void)drawView:(UIView *)theView
{
    static GLfloat rot = 0.0;

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    // This is the same result as using Vertex3D, just faster to type and
    // can be made const this way

    static const Vertex3D vertices[]= {
        {1.0f,  -1.0f,   0.2f},
        {1.0f,  -1.0f,  -0.2f},
        {1.0f,   1.0f,  -0.2f},
        {1.0f,   1.0f,   0.2f},
        {-1.0f, -1.0f,   0.2f},
        {-1.0f, -1.0f,  -0.2f},
        {-1.0f,  1.0f,  -0.2f},
        {-1.0f,  1.0f,   0.2f}
    };  

    static const Color3D colors[] = {
        {1.0, 0.0, 0.0, 20.0},
        {1.0, 1.0, 1.0, 20.0},
        {1.0, 1.0, 1.0, 20.0},
        {0.0, 0.0, 1.0, 20.0},
        {0.0, 1.0, 0.0, 20.0},
        {1.0, 1.0, 1.0, 20.0},
        {1.0, 1.0, 1.0, 20.0},
        {1.0, 1.0, 1.0, 20.0},
    };

    static const GLubyte cubeFaces[] = {
        0, 1, 3,     
        2, 3, 1,   

        0, 3, 4, 
        3, 4, 7,        // first main face

        2, 1, 6,        // second main face
        1, 6, 5,

        5, 6, 7, 
        5, 4, 7,

        7, 6, 3, 
        6, 3, 2,

        4, 0, 5,
        1, 0, 5, 
    };

    static const Vector3D normals[] = {
        {0.200000, -0.400000, 0.000000},
        {0.400000, -0.200000, -0.400000},
        {0.333333, 0.333333, -0.333333},
        {0.400000, 0.400000, -0.200000},
        {-0.333333, -0.333333, 0.333333},
        {-0.400000, -0.400000, -0.200000},
        {-0.200000, 0.400000, -0.400000},
        {-0.400000, 0.200000, 0.000000},
    };

    static const GLfloat texCoords[] = {
        0.0, 0.0,   // texture  face
        1.0, 1.0,
        0.0, 1.0,
        1.0, 1.0,
        0.0, 0.0,
        1.0, 0.0,


        0.0, 0.0,   // texture  face
        1.0, 1.0,
        1.0, 0.0,
        1.0, 0.0,
        0.0, 1.0,
        1.0, 1.0,

        0.0, 0.0,   // texture  face
        1.0, 1.0,
        0.0, 1.0,
        1.0, 1.0,
        0.0, 0.0,
        1.0, 0.0,

        0.0, 0.0,   // texture  face
        1.0, 1.0,
        0.0, 1.0,
        1.0, 1.0,
        0.0, 0.0,
        1.0, 0.0,

        0.0, 0.0,   // texture  face
        1.0, 1.0,
        0.0, 1.0,
        1.0, 1.0,
        0.0, 0.0,
        1.0, 0.0,

        0.0, 0.0,   // 
        1.0, 1.0,
        0.0, 1.0,
        1.0, 1.0,
        0.0, 0.0,
        1.0, 0.0,

    };

      glTexCoordPointer(2, GL_FLOAT, 0, texCoords);


    glLoadIdentity();
    glClearColor(0.7, 0.7, 0.7, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glVertexPointer(3, GL_FLOAT, 0, vertices);
    glColorPointer(4, GL_FLOAT, 0, colors);
    glNormalPointer(GL_FLOAT, 0, normals);
    glTexCoordPointer(2, GL_FLOAT, 0, texCoords);

    NSMutableArray *tempRow;
    Cube *tempCube;
    for (int i = 1; i <= cubes.tileRows; i++)
    {
        tempRow = [cubes rowAtIndex:i-1];
        for (int j = 1; j <= cubes.tileCols; j++)
        {
            tempCube = [tempRow objectAtIndex:j-1];
            glLoadIdentity();
            glTranslatef(gridOffsetX + (tileSpacing * (GLfloat)i), gridOffsetY + (tileSpacing * (GLfloat)j), gridOffsetZ);
            glRotatef(rot, 1.0, 0.0, 0);
            glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, cubeFaces);
        }
    }

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    static NSTimeInterval lastDrawTime;
    if (lastDrawTime)
    {
        NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime;
        rot+=30 * timeSinceLastDraw;                
    }
    //NSLog(@"rot is %f", rot);
    lastDrawTime = [NSDate timeIntervalSinceReferenceDate];    
}



-(void)setupView:(GLView*)view
{
    initDone = NO;

    tileRows = 5;
    tileCols = 7;
    gridOffsetX = 5.2f;
    gridOffsetY = 6.9f;
    gridOffsetZ = -14.0;
    tileSpacing = -2.15f;

    cubes = [[Cubes alloc] initWithRowCount:tileRows colCount: tileCols ];

    const GLfloat zNear = 0.01, zFar = 1000.0, fieldOfView = 50.0; 
    GLfloat size; 
    glEnable(GL_DEPTH_TEST);
    glMatrixMode(GL_PROJECTION); 
    size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0); 
    CGRect rect = view.bounds; 

//  glOrthof(-5.0,                                          // Left
//           5.0,                                          // Right
//             -5.0 / (rect.size.width / rect.size.height),   // Bottom
//           5.0 / (rect.size.width / rect.size.height),   // Top
//           0.01,                                         // Near
//           10000.0);                                     // Far

    glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size / 
               (rect.size.width / rect.size.height), zNear, zFar); 

    glViewport(0, 0, rect.size.width, rect.size.height);  
    glMatrixMode(GL_MODELVIEW);

    glEnable(GL_COLOR_MATERIAL);
    // Enable lighting
    glEnable(GL_LIGHTING);

    // Turn the first light on
    glEnable(GL_LIGHT0);

    // Define the ambient component of the first light
    const GLfloat light0Ambient[] = {0.5, 0.5, 0.5, 1.0};
    glLightfv(GL_LIGHT0, GL_AMBIENT, light0Ambient);

    // Define the diffuse component of the first light
    const GLfloat light0Diffuse[] = {0.7, 0.7, 0.7, 1.0};
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light0Diffuse);

    // Define the specular component and shininess of the first light
    const GLfloat light0Specular[] = {0.7, 0.7, 0.7, 1.0};
    const GLfloat light0Shininess = 0.4;
    glLightfv(GL_LIGHT0, GL_SPECULAR, light0Specular);


    // Define the position of the first light
    const GLfloat light0Position[] = {0.0, 10.0, 10.0, 0.0}; 
    glLightfv(GL_LIGHT0, GL_POSITION, light0Position); 

    // Define a direction vector for the light, this one points right down the Z axis
    const GLfloat light0Direction[] = {0.0, 0.0, -1.0};
    glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light0Direction);

    // Define a cutoff angle. This defines a 90° field of vision, since the cutoff
    // is number of degrees to each side of an imaginary line drawn from the light's
    // position along the vector supplied in GL_SPOT_DIRECTION above
    glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 45.0);


    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_SRC_COLOR);

    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); 

    NSString *path = [[NSBundle mainBundle] pathForResource:@"a-tile-64" ofType:@"png"];
    NSData *texData = [[NSData alloc] initWithContentsOfFile:path];
    UIImage *image = [[UIImage alloc] initWithData:texData];
    if (image == nil)
        NSLog(@"Do real error checking here");

    GLuint width = CGImageGetWidth(image.CGImage);
    GLuint height = CGImageGetHeight(image.CGImage);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    void *imageData = malloc( height * width * 4 );
    CGContextRef context = CGBitmapContextCreate( imageData, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
    CGColorSpaceRelease( colorSpace );
    CGContextClearRect( context, CGRectMake( 0, 0, width, height ) );
    CGContextTranslateCTM( context, 0, height - height );
    CGContextDrawImage( context, CGRectMake( 0, 0, width, height ), image.CGImage );

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);

    CGContextRelease(context);

    free(imageData);
    [image release];
    [texData release];

    glLoadIdentity(); 
};

- (void)dealloc 
{
    [tileArray release];
    [cubes release];

    [super dealloc];
}
@end

颜色显示正常吗? - Rob Jones
+1 链接 Jeff Lamarche 的教程。这正是我所需要的。 - Bogatyr
2个回答

2
我也使用Jeff的教程启动了OpenGL ES。
我建议简化你想做的事情。例如:
- 忘记法线 - 忘记颜色 - 忘记索引 - 创建一个将顶点绑定到其属性的结构体
Jeff提供了一个有用的TexturedVertexData3D结构体来完成这个任务。如果你不想,你不必填写法线部分。
然后,适当地设置步长:
glVertexPointer(3, GL_FLOAT, sizeof(TexturedVertexData3D), &vertices[0]);
glTexCoordPointer(2, GL_FLOAT, sizeof(TexturedVertexData3D), &vertices[0].texCoords);

使用glDrawArrays来绘制您的对象:
glDrawArrays(GL_TRIANGLES, 0, nVertices);

一旦你完成这一步骤,可以将法线和颜色添加到TexturedVertexData3D结构中,并适当设置纹理和颜色指针。然后再次测试,如果有问题请更新。
在此时,你可以开始考虑如何使用索引。只有在渲染成千上万个顶点时,索引才真正有意义。但是,在这个时间点上,使用它们可以获得良好的性能提升。

好的,这很好知道。我按照您建议的使用GLDrawArrays重建了项目,并且一切都运行良好,使用暴力显式顶点。纹理贴图以合理的方式工作,现在我感觉可以像您说的那样逐步改进正在工作的内容。 - omnivore

0

我一直在关注这个问题,因为我正在学习OpenGL,接下来将会转向OpenGL ES。由于还没有人回答,我会给出我的想法,但请不要认为这是专家意见。

需要考虑的一点是,目前为止,您的顶点、颜色和法线数组包含8个“项”,但是您的TexCoord数组有36个“项”。我非常确定当您使用glDrawElements与索引列表一起使用时,它会使用这些索引从所有已激活的数组中选择项目。因此,您的TexCoord数组的最后28个项目将永远不会被使用,它们将根据cubeFaces指定的索引进行选择。在您链接的教程中,所有数组中都有四个项目,这对于对象的单个面非常有效。

然而,对于使用3D对象的索引来说,这是一个问题,因为尽管在立方体中重复使用了几个顶点,但它们的纹理坐标不一定相同,用于不同三角形的纹理坐标也不同。实际上,它们的法线也不同,因此在照明对象时,这可能是您代码的另一个问题。

我不知道这个问题的最佳解决方案是什么,所以我对这个问题的任何其他答案都很感兴趣... 逐点绘制立方体是一种选择。我还想知道是否可以分别绘制立方体的每个面,并每次更改TexCoord数组。或者也许有一些更简单或标准的方法来完成这种事情,我目前还不知道!


我得出了类似的结论。核心问题可能出现在使用顶点数组索引时:我不确定是否使用相同的索引来获取纹理顶点数组中的值。我找不到任何关于这方面的文档。我现在考虑回归基本三角形。我只处理简单的几何图形,因此使用顶点指针数组节省空间的好处可能不值得麻烦。此外:我所做的需要能够以不同的方式将纹理映射到对象表面:使用三角形可能是唯一的方法。 - omnivore

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接