改变loadTexture[]
第一件需要做的是就是改变 loadTexture[]. 基本上,我们可以改变调用如下:
- (void)loadTexture:(NSString *)name intoLocation:(GLuint)location;
向前看。我们改变 loadTexture[] 函数让它知道加载纹理的名词和路径就可以加载纹理. 现在改变 loadTexture[] 函数如下:
- (void)loadTexture:(NSString *)name intoLocation:(GLuint)location {
CGImageRef textureImage = [UIImage imageNamed:name].CGImage;
不要忘记删除 @”checkerplate.png” 取而带之的是我们的文件名. 删除 glGenTextures() 函数并且改变 glBindTexture() 函数如下:
glBindTexture(GL_TEXTURE_2D, location);
就是这样,现在这个方法更加的有用,我们将调用glGenTextures() 在我们的 initWithCoder[] 函数里。当我们要加载某个纹理的时候,我们就调用 loadTexture[] ,在本教程里,我们将加载6个纹理, 所以在 initWithCoder[] 里调用完 setupView, 我们增加如下的函数:
[self setupView];
glGenTextures(6, &textures[0]);
[self loadTexture:@"bamboo.png" intoLocation:textures[0]];
[self loadTexture:@"flowers.png" intoLocation:textures[1]];
[self loadTexture:@"grass.png" intoLocation:textures[2]];
[self loadTexture:@"lino.png" intoLocation:textures[3]];
[self loadTexture:@"metal.png" intoLocation:textures[4]];
[self loadTexture:@"schematic.png" intoLocation:textures[5]];
另外,切换到头文件,改变纹理如下:
GLuint textures[6];
获得渲染
好,回到drawView[]. 删除有关金字塔的所有东西。我们已经不需要再使用它了。删除我们使用的颜色。所以看起来就如下:
// Our new drawing code goes here
rota += 0.2;
glPushMatrix();
{
glTranslatef(0.0, 0.0, -4.0); // Change this line
glRotatef(rota, 1.0, 1.0, 1.0);
glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
glEnableClientState(GL_VERTEX_ARRAY);
// Draw the front face
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
// Draw the top face
glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
// Draw the rear face
glDrawArrays(GL_TRIANGLE_FAN, 8, 4);
// Draw the bottom face
glDrawArrays(GL_TRIANGLE_FAN, 12, 4);
// Draw the left face
glDrawArrays(GL_TRIANGLE_FAN, 16, 4);
// Draw the right face
glDrawArrays(GL_TRIANGLE_FAN, 20, 4);
}
glPopMatrix();
你可以不需要使用 glPushMatrix() 和 glPopMatrix() .如果你想你的代码100%的优化,就可以删除它。
现在我们已经获得了一些改变,让我们第二次停下来,思考OpenGL的状态。
好了,记得刚才我们加载过纹理,我们可以使用 glBindTexture()来告诉OpenGL我们希望那个纹理工作。所以,再加载完6个纹理以后,OpenGL将默认使用最后一个加载的纹理,如果你点击 “Build and Go”, 你将看到一个只有单一纹理的立方体。
因为我们想每个面都有一个纹理,我们需要告诉OpenGL我们想使用那个纹理来绘制,在我们调用glDrawArrays()之前。所以,我们的纹理代码如下:
glPushMatrix();
{
glTranslatef(0.0, 0.0, -4.0);
glRotatef(rota, 1.0, 1.0, 1.0);
glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
glEnableClientState(GL_VERTEX_ARRAY);
// Draw the front face
glBindTexture(GL_TEXTURE_2D, textures[0]);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
// Draw the top face
glBindTexture(GL_TEXTURE_2D, textures[1]);
glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
// Draw the rear face
glBindTexture(GL_TEXTURE_2D, textures[2]);
glDrawArrays(GL_TRIANGLE_FAN, 8, 4);
// Draw the bottom face
glBindTexture(GL_TEXTURE_2D, textures[3]);
glDrawArrays(GL_TRIANGLE_FAN, 12, 4);
// Draw the left face
glBindTexture(GL_TEXTURE_2D, textures[4]);
glDrawArrays(GL_TRIANGLE_FAN, 16, 4);
// Draw the right face
glBindTexture(GL_TEXTURE_2D, textures[5]);
glDrawArrays(GL_TRIANGLE_FAN, 20, 4);
}
glPopMatrix();
点击 “Build and Go”
回答一个问题,那些代码完成了如上的功能。
纹理包装
下一节就是我一直在说的重复和包装纹理。重复纹理只是简单的多次重复相同的纹理。这是非常有用的内容,用较小的纹理组成一面墙。(必须是要整齐的纹理)
我们要做的是绘制我们的立方体的一个面,使用一个纹理重复四次。我要用这个竹子纹理改变正面,你可以做你任意喜欢的一个面。
这非常容易,你还记得OpenGL是如何从图片中抽取像素去渲染对象的吗?它使用了纹理坐标数组,之前我都告诉你,使用0.0-1.0。如果我要使图像出现两次,我之要将1.0改变为2.0,重复的纹理就会出现了。
所以,在我们的纹理数组里,改变如下:
const GLshort squareTextureCoords[] = {
// Front face
0, 2, // top left
0, 0, // bottom left
2, 0, // bottom right
2, 2, // top right
// Top face
0, 1, // top left
0, 0, // bottom left
1, 0, // bottom right
1, 1, // top right
点击 “Build and Go”:
我只是修改了前面,如果你喜欢,你可以把其他面都做修改。
这是OpenGL默认的纹理映射状态,当纹理坐标数组里的值大于1.0的时候。如果你想改变这个状态,你也可以调用 glTexParameterf().比如,你可以改变如下:
// Draw the front face
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
上面的代码,在加载纹理坐标中数组大于1以后,就会出现如下效果
纹理的边缘像素一直延伸到左上角。这就是GL_CLAMP_TO_EDGE.
让我快速解释这两行代码:
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
我们调用了 glTexParameterf() 当我们加载纹理的时候设置纹理过滤。像一般的OpenGL函数,它具有“双重功能”。有三个参数,但是在OpenGL ES只有两个有效果。第一个参数一般使 GL_TEXTURE_2D ,OpenGL ES 只支持它。
第二个参数是告诉OpenGL什么“变量”是我们要改变的,一般来说,是 GL_TEXTURE_WRAP_x.
这第三个参数就是我们要设置的。
OpenGL 通过设置 “变量” or “设置” GL_TEXTURE_WRAP_S t到GL_CLAMP_TO_EDGE, 我们告诉 OpenGL 不要重复纹理而是延伸s 坐标 (什么是 “s”, 后面会说). 我们也做了相同的t坐标,等下运行下模拟器,看看是什么样的。
这里设置的默认值是 GL_REPEAT.
s t 坐标?
不,不是玩笑。OpenGL在它的纹理中使用了s t 坐标系统。它确实存在,以避免混淆,因为你必须记住我们的纹理并不是真实存在于我们的3d世界,他们只是我们放置到3d世界的某些东西。所以,你可以认为 s 是横向,t 是纵向.但你旋转它的时候,它是不是一样的使用?