Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5519995
  • 博文数量: 763
  • 博客积分: 12108
  • 博客等级: 上将
  • 技术积分: 15717
  • 用 户 组: 普通用户
  • 注册时间: 2007-09-28 21:21
个人简介

业精于勤,荒于嬉

文章分类

全部博文(763)

文章存档

2018年(6)

2017年(15)

2016年(2)

2015年(31)

2014年(14)

2013年(87)

2012年(75)

2011年(94)

2010年(190)

2009年(38)

2008年(183)

2007年(28)

分类: C/C++

2010-01-30 10:20:31

到目前为止,我们已经对2d物体做了很好的说明。现在是开始创建3d物体的时候了。虽然我们不需要太多的改变,它们需要更多的顶点(如果你创建并使用顶点数组)或者更多的坐标转换,如果你想使用多个平面来创建一个立方体。

也许我该先介绍点和线,但是至今我们已经介绍了一些纹理映射矩形及彩色的三角形,我们不必要去研究那些不够有趣的形状!

另外,我们需要回头去看坐标转换,并且介绍关于旋转的更多细节。并且初学者的东西我不需要在解释。那么,这所有的一切都意味着,我还有更多的教程都没有写。

首先, 贯穿 drawView 函数
对我们艰难的代码工作说88吧。是时候让drawView函数回到最基本的状态了。

将 drawView 函数做成如下:


- (void)drawView {
 
// Our new object definition code goes here
 
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glViewport(0, 0, backingWidth, backingHeight);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
 
// Our new drawing code goes here
 
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
[self checkGLError:NO];
}
 

你应该感谢我之前为3d空间做的一切,因为我们不需要对深度缓冲做解释或者增加许多新的代码了。这些你已经非常熟悉了。

定义一个3d物体
我们将产生一个立方体,因为它们构件很容易,是很常见的3d图形,并且看起来很cool的3d旋转及纹理映射。在我们绘制一个3d立方体之前,我们需要知道3d立方体是由6个我们一直在使用的矩形组成的。这很容易,但我将会把你以前的定义都打破。让我们先来定义正面。

    const GLfloat cubeVertices[] = {
        
        // Define the front face
        -1.0, 1.0, 1.0,             // top left
        -1.0, -1.0, 1.0,            // bottom left
        1.0, -1.0, 1.0,             // bottom right
        1.0, 1.0, 1.0,              // top right
        
顶面和之前的面几乎完全相同,让我来看看:

        // Top face
        -1.0, 1.0, -1.0,            // top left (at rear)
        -1.0, 1.0, 1.0,             // bottom left (at front)
        1.0, 1.0, 1.0,              // bottom right (at front)
        1.0, 1.0, -1.0,             // top right (at rear)
        
请注意,我绘制顶面的方向和我绘制正面的方向不是一样的。(正面是逆时针,而顶面不是)但是我在同一地点开始绘制?如果我们将立方体沿x轴旋转90度,那么第一个点在左上,跟下来是右上?

接下来,后面:

        // Rear face
        1.0, 1.0, -1.0,             // top right (when viewed from front)
        1.0, -1.0, -1.0,            // bottom right
        -1.0, -1.0, -1.0,           // bottom left
        -1.0, 1.0, -1.0,            // top left

注意到顶点的出发点了吗?我们将以这样的方式完成其他面。
       // Bottom Face
        -1.0, -1.0, 1.0,            // Bottom left front
        1.0, -1.0, 1.0,             // right front
        1.0, -1.0, -1.0,            // right rear
        -1.0, -1.0, -1.0,           // left rear

逆可以看到,我用同样的方法和起点。在你的头脑里面想像如何旋转这些面,看到点排列的方式。

最后,我们完成左面和右面:

           // Left face
        -1.0, 1.0, -1.0,            // top left
        -1.0, 1.0, 1.0,             // top right
        -1.0, -1.0, 1.0,            // bottom right
        -1.0, -1.0, -1.0,           // bottom left
        
        // Right face
        1.0, 1.0, 1.0,              // top left
        1.0, 1.0, -1.0,             // top right
        1.0, -1.0, -1.0,            // right
        1.0, -1.0, 1.0              // left

这里是立方体的完整定义:


const GLfloat cubeVertices[] = {
 
// Define the front face
-1.0, 1.0, 1.0, // top left
-1.0, -1.0, 1.0, // bottom left
1.0, -1.0, 1.0, // bottom right
1.0, 1.0, 1.0, // top right
 
// Top face
-1.0, 1.0, -1.0, // top left (at rear)
-1.0, 1.0, 1.0, // bottom left (at front)
1.0, 1.0, 1.0, // bottom right (at front)
1.0, 1.0, -1.0, // top right (at rear)
 
// Rear face
1.0, 1.0, -1.0, // top right (when viewed from front)
1.0, -1.0, -1.0, // bottom right
-1.0, -1.0, -1.0, // bottom left
-1.0, 1.0, -1.0, // top left
 
// bottom face
-1.0, -1.0, 1.0,
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
 
// left face
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0,
-1.0, -1.0, -1.0,
 
// right face
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0
};
 


如果你对坐标系统有问题,你最好在你的脑海里去想像它。如果你的思维还停留在2d平面上,那你真的要努力了,我们已经开始3d了。

将 cubeVertices 放到 drawView 函数,并且说明 “New object definition goes here”.

OK, 现在我们要开始绘制这个棒棒糖(不是翻译错误)。

绘制立方体
最简单的办法就是你用你以前看过的代码直接去绘制立方体。不过现在,我们要使用一些先进的(你理解了,它就很简单)的方法来绘制3d物体。然而,现在,让我来介绍如何在3d中绘制图形。

我们开始的代码不需要在解释了。在我们的注释之下,添加如下代码:
    glLoadIdentity();
    glTranslatef(0.0, 0.0, -6.0);
    glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
    glEnableClientState(GL_VERTEX_ARRAY);

这里没有任何新的代码。我们在开始的重置了我们的矩阵,移动我们的立方体到屏幕里以让我们可以看到,告诉OpenGL将要使用的顶点数组的格式及数据存储位置。

后面的代码几乎和你之前用过的相同:

// Draw the front face in Red
glColor4f(1.0, 0.0, 0.0, 1.0);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

这里没有新的代码,我们告诉OpenGL将红色作为绘制颜色,提取从0开始的4个顶点来绘制一个矩形。现在,用我们数组里面的下4个顶点来绘制顶面。

// Draw the top face in green
glColor4f(0.0, 1.0, 0.0, 1.0);
glDrawArrays(GL_TRIANGLE_FAN, 4, 4);

看看这里的 glDrawArrays()函数. 如果你还记得我对你描述过的,我说,第二个参数是数据开始的偏移。是的,因为我们是绘制立方体的第二个面,我们需要告诉OpenGL从要偏移4个值(也就是 cubeVertices[4], 0-3是第一个面),然后我们用之后的4个顶点来绘制。(老外的教程说的真仔细,我看过3d很多本书了,这是第一次了解第二个参数的意义)

现在,我们来绘制后面:

    // Draw the rear face in Blue
    glColor4f(0.0, 0.0, 1.0, 1.0);
    glDrawArrays(GL_TRIANGLE_FAN, 8, 4);

同样的定义,我们从 cubeVertices[8]开始绘制. 用同样的方法来绘制其他三个面:

    // Draw the bottom face
    glColor4f(1.0, 1.0, 0.0, 1.0);
    glDrawArrays(GL_TRIANGLE_FAN, 12, 4);

    // Draw the left face
    glColor4f(0.0, 1.0, 1.0, 1.0);
    glDrawArrays(GL_TRIANGLE_FAN, 16, 4);

    // Draw the right face
    glColor4f(1.0, 0.0, 1.0, 1.0);
    glDrawArrays(GL_TRIANGLE_FAN, 20, 4);

我们改变了每个面的颜色,并且为 glDrawArrays ()改变了偏移值。

现在,如果你点击了“Build and Go”,你只会得到一个红色的矩形。为了看到所有的6个面,让我们来一起旋转三个轴。

在 glLoadIdentity()之前,添加下面的代码。

        rota += 0.5;

我们的老朋友rota先生回来了。(这作者在搞笑,不太高明)。现在我们需要其他的老朋友 glRotatef() 。在 glTranslatef()函数之后,添加下面:

        glRotatef(rota, 1.0, 1.0, 1.0);

以前,我们只需要使用 glRotatef() 来旋转一个轴,现在我们需要三轴同时旋转。

现在我们点击 “Build and Go” 来看看你得到的:

西蒙iphone-OpenGL ES 教程 -07 - 勇者之尊 - 等待

欢迎你,3d物体。

如何对它进行纹理映射?
我认为,我们现在需要的不只是一个纯色的物体的。让我们使用上个教程里的纹理来纹理映射六个面来使得这个立方体更加的精彩。

好了,我们保持我们工程里面的纹理加载函数。我们只需要修改我们的drawView函数。现在,我带你们去看看如何一次性的快速纹理映射。

首先,你还记得这个吗,在上个教程里的

    const GLshort squareTextureCoords[] = {
        // Front face
        0, 1,       // top left
        0, 0,       // bottom left
        1, 0,       // bottom right
        1, 1,       // top right

恩,我们可以很容易的纹理映射一个面。我们需要扩充它。然而,这是很容易的。想起来我在确定立方体的各个面是如何使用一样的排列方式的吗?(顺时针规范顶点)现在我们来找到这是为什么。

当OpenGL绘制一个纹理到立方体的一个面的时候,因为我们绘制每个面的时候都使用了偏移(4,8,或者12),纹理映射的时候也会采用相同的偏移。所以,为了纹理映射六个面,我们需要重复之前的4个坐标5遍。



const GLshort squareTextureCoords[] = {
// Front face
0, 1, // top left
0, 0, // bottom left
1, 0, // bottom right
1, 1, // top right
 
// Top face
0, 1, // top left
0, 0, // bottom left
1, 0, // bottom right
1, 1, // top right
 
// Rear face
0, 1, // top left
0, 0, // bottom left
1, 0, // bottom right
1, 1, // top right
 
// Bottom face
0, 1, // top left
0, 0, // bottom left
1, 0, // bottom right
1, 1, // top right
 
// Left face
0, 1, // top left
0, 0, // bottom left
1, 0, // bottom right
1, 1, // top right
 
// Right face
0, 1, // top left
0, 0, // bottom left
1, 0, // bottom right
1, 1, // top right
};
 


数据的剪切和粘贴工作完成了!

现在,我们只需要使用几行代码来纹理映射我们的立方体。

在绘制第一个面之前,添加下面代码:

    glTexCoordPointer(2, GL_SHORT, 0, squareTextureCoords);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

这是很简单,别删除颜色处理那段。看看新的生成结果;


西蒙iphone-OpenGL ES 教程 -07 - 勇者之尊 - 等待

嘿,这就象魔法一样。一个纹理映射过的3d物体在3d空间中旋转。

在纹理映射图片中的一个概念
在上个教程里,我忘记了说明这个概念了。虽然我建议你使用自己的纹理来进行纹理映射,但是纹理的尺寸必须是2的 幂数。图片的宽和高必须是  1, 2, 4, ... 32, ... 512, 1024.这个宽和高不相同也可以,但必须是2的 幂数。所以32 x 512同64 x 64一样是正确的尺寸。而30 x 30不是。


glRotatef()和你物体顶点的一个概念

同样,我鼓励你创建你自己的物体。你有没有注意到,我一直把0,0,0作为我三角形,矩形及立方体的中心?这是因为当我们旋转的时候,OpenGL将会以0,0,0作为模型矩阵的中心。它不会调整自己的模型到0,0,0在旋转。如果你的物体不是以0,0,0为中心来创建的,你旋转的时候就知道什么叫“lop-sided”.
阅读(1388) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~