iphone-OpenGL ES 教程-07--西蒙

来源:互联网 发布:外贸市场调查 知乎 编辑:程序博客网 时间:2024/05/16 04:33
 OpenGL ES 07 – 对物体进行独立的转换









起初,我想下一章介绍光照的,但是我又想到别的。我们依然有很多基本对象及转换没有完成,特 别是我们在不同的坐标系中对不同的物体进行转换。

记得我们如何在整个屏幕内使用 glTranslatef() 和 glRotatef ()?如果我想作一些不同的事情,我就会使用一个很方便的函数: glLoadIdentity().但是 glLoadIdentity()是“代价昂贵”在屏幕渲染技术中,所以现在,我会采用更有效的方法。

要作到这点,我将介绍一个新的对 象。我们将增加一个金字塔,并且使用 glTranslatef()来移动它,使用 glRotatef()来旋转它。和立方体独立开,并且不使用 glLoadIdentity() 来重置矩阵。

在我们的世界里增加一个金字塔
这不是仅仅增加一个三角形,让我们坚持使用3d物 体,我们会增加一个金字塔(想像一下Giza平原上的大石头)

有件事,我在以前的教程中避免去说明的,就是我们创建的立方体事一个复杂对 象,即物体是由一个以上的原点组成的。从技术上来说,我们的矩形也是一个复杂物体,但我们只使用了单一的OpenGL的函数去绘制它,我们可以将其看为一 个简单物体,我避免说明复杂物体是因为我不想让我们的工作看起来是一个“艰苦的工作”

现在你已经完成了一个复杂物体了。恭喜你!并且,现 在是时候建立第二个了。

金字塔不是难事。它是由一个矩形以及会聚到这个矩形上面的中心点的4个三角形组成的。一旦你将物体可以分解为你所 认知的简单图元,任何复杂的物体都不是问题。唯一的变化是你物体中图元的数目而已。

好,开启Xcode并且开启上个教程的工程。这次不要 删除任何东西,我们增加代码来构成一个平行线。

为了帮助你组成金字塔,我们需要分解它的各个组成部分,让我们开始:

    const GLfloat pyramidVertices[] = {
        // Our pyramid consists of 4 triangles and a square base.
        // We'll start with the square base
        -1.0, -1.0, 1.0,            // front left of base
        1.0, -1.0, 1.0,             // front right of base
        1.0, -1.0, -1.0,            // rear left of base
        -1.0, -1.0, -1.0,           // rear right of base

因此,我们创建了一个新的物体,和我们作的一样。这个矩形 是底座,所有的y坐标都是-1.0。

现在,我们可以创建金字塔的正面,这次我们创建的三角形而不是矩形。

        // Front face
        -1.0, -1.0, 1.0,            // bottom left of triangle
        1.0, -1.0, 1.0,             // bottom right
        0.0, 1.0, 0.0,              // top centre -- all triangle vertices
                                    //     will meet here

这是我们创建的唯一的真实三角形,这个三角形的角在矩形的前边,然后向后倾斜,上面的点在矩形的中心上面 (在x轴线上)

我们继续创建其他的三个三角形,使用相同的方法,下面是完整的顶点数组。

 // Our new object definition code goes here    const GLfloat pyramidVertices[] = {        // Our pyramid consists of 4 triangles and a square base.        // We'll start with the square base        -1.0, -1.0, 1.0,            // front left of base        1.0, -1.0, 1.0,             // front right of base        1.0, -1.0, -1.0,            // rear left of base        -1.0, -1.0, -1.0,           // rear right of base         // Front face        -1.0, -1.0, 1.0,            // bottom left of triangle        1.0, -1.0, 1.0,             // bottom right        0.0, 1.0, 0.0,              // top centre -- all triangle vertices                                    //     will meet here         // Rear face        1.0, -1.0, -1.0,   // bottom right (when viewed through front face)        -1.0, -1.0, -1.0,           // bottom left        0.0, 1.0, 0.0,              // top centre         // left face        -1.0, -1.0, -1.0,           // bottom rear        -1.0, -1.0, 1.0,            // bottom front        0.0, 1.0, 0.0,              // top centre         // right face        1.0, -1.0, 1.0,             // bottom front        1.0, -1.0, -1.0,            // bottom rear        0.0, 1.0, 0.0               // top centre    };  

增加金字塔的定义到 drawView 方法里和 cubeVerticies[] 的定义在一起。

在我们继续前,我想先说明下金字塔的定义。

首先,这是第一个我们同时提 供了三角形及矩形的对象。这个金字塔是由一个矩形和4个三角形组成的。因为我们调用glDrawArrays()为每个单独的图元,但这不防碍我们每次使 用单独的定义来绘制不同的图元。那么,当我们绘制金字塔,一切都变的清楚了。

第2,再一次的提醒。我已指定所有的顶点是逆时针的。即使后 面看起来似乎是顺时针的,但是从OpenGL的角度看,它还是逆时针的。

其实,我想在我3d图形教学结束前,我们会一直讨论这个问题。

绘 制金字塔
现在低头去找立方体的代码。首先,删除glLoadIdentity() 这行,它已经不需要了。

在 rota+=0.5以后,我们增加绘制金字塔的代码。现在,我们要在不影响立方体的情况下,使用glTranslatef() 去移动金字塔,并且使用 glRotatef() 去旋转金字塔。为了做到这点,我们需要在不影响其他图元的情况下使用 glTranslatef() 和 glRotatef().

OpenGL 为我们使用这两个函数提供了快捷的方法。

    glPushMatrix();

    // Translation and drawing code goes here....

    glPopMatrix();

OpenGL 告诉我们如果要将我们的矩阵变换放如到堆栈中就要使用 glPushMatrix().我将我们的pyramidVertices[] 和 cubeVertices[] 目标 “推” 到 OpenGL’s 的堆栈中.

Note: 如果你不明白我说的堆栈是什么意思,那么你可能需要一本c或者object c 的工具书。

所以,我们的数据复制是安全的,我们可以象其 他OpenGL大师一样进行转换了。让我们开始绘制这个金字塔吧!

    glPushMatrix();
    {
        glTranslatef(-2.0, 0.0, -8.0);
        glRotatef(rota, 1.0, 0.0, 0.0);
        glVertexPointer(3, GL_FLOAT, 0, pyramidVertices);
        glEnableClientState(GL_VERTEX_ARRAY);

首 先要指出的是, glPushMatrix()后面的那个{.这个不是必要的,但是你在学习的时候最好加上它,{}内表示了被推入堆栈和弹出堆栈之间做的工作。

在 glPushMatrix() 后面的四行,我马上解释给你听. 所有我们所做的就是要求glTranslatef ( )把金字塔远离( 0 , 0 , 0 )的左侧和回8点到屏幕(进一步远离观众) 。那么我们的金字塔周围旋转的X轴,而不是所有三个轴计算的立方体的例子在过去的教程。最后,我们只要告诉OpenGL的有关数据,并使它能够被使用。

好 了,在我们做好坐标转换以后,我们开始绘制金字塔。

        // Draw the pyramid
        // Draw the base -- it's a square remember
        glColor4f(1.0, 0.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

前四个坐标表 示了我们金字塔的底部。我们将它的颜色设置为红色(也可以进行纹理映射),并且使用 GL_TRIANGLE_FAN 来绘制矩形。我们是从(pyramidVerticies[0~3]),使用4个的顶点。

Ok,我们完成矩形了,现在开始绘制第一个正面 的三角形。

        // Front Face
        glColor4f(0.0, 1.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLES, 4, 3);

除里改变颜色,我们改变绘制模 式为 GL_TRIANGLES的原因是显然的,我们已经从数组的第4个(pyramidVerticies[4~6])开始,延伸3个顶点来绘制。所以,你可 以看到,一个矩形和一个三角形可以很容易的共存于一个数据结构里。

下一步,我们继续绘制我们其他三个三角形:

        // Rear Face
        glColor4f(0.0, 0.0, 1.0, 1.0);
        glDrawArrays(GL_TRIANGLES, 7, 3);
        
        // Right Face
        glColor4f(1.0, 1.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLES, 10, 3);
        
        // Left Face
        glColor4f(1.0, 0.0, 1.0, 1.0);
        glDrawArrays(GL_TRIANGLES, 13, 3);
    }
    glPopMatrix();

每次,我们只是改变颜色,并改变开始的偏移值。 OpenGL知道每个顶点是由三个坐标组成的,通过我们的 glVertexPointer() 函数。

最后,我们关闭},并且调用 glPopMatrix().

现在,在这点上我们可以绘制立方体。但是,请注意,你可以一遍又一遍的重复绘制,只需要将绘制函数放在 glPushMatrix() 和 glPopMatrix()中间.

绘制立方体-修订
这里是绘制立方体的完整代码。最大的改变 就是代码头尾的函数 glPushMatrix() 和 glPopMatrix()


     glPushMatrix();    {        glTranslatef(2.0, 0.0, -8.0);        glRotatef(rota, 1.0, 1.0, 1.0);        glVertexPointer(3, GL_FLOAT, 0, cubeVertices);        glEnableClientState(GL_VERTEX_ARRAY)// Draw the front face in Red        glColor4f(1.0, 0.0, 0.0, 1.0);        glDrawArrays(GL_TRIANGLE_FAN, 0, 4)// Draw the top face in green        glColor4f(0.0, 1.0, 0.0, 1.0);        glDrawArrays(GL_TRIANGLE_FAN, 4, 4)// Draw the rear face in Blue        glColor4f(0.0, 0.0, 1.0, 1.0);        glDrawArrays(GL_TRIANGLE_FAN, 8, 4)// 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);    }    glPopMatrix()

其他的变化只有 glTranslatef 的参数。
这 里是完整的drawView函数。

 - (void)drawView {     // Our new object definition code goes here    const GLfloat pyramidVertices[] = {        // Our pyramid consists of 4 triangles and a square base.        // We'll start with the square base        -1.0, -1.0, 1.0,            // front left of base        1.0, -1.0, 1.0,             // front right of base        1.0, -1.0, -1.0,            // rear left of base        -1.0, -1.0, -1.0,           // rear right of base         // Front face        -1.0, -1.0, 1.0,            // bottom left of triangle        1.0, -1.0, 1.0,             // bottom right        0.0, 1.0, 0.0,              // top centre -- all triangle vertices                                    //     will meet here         // Rear face        1.0, -1.0, -1.0,     // bottom right (when viewed through front face)        -1.0, -1.0, -1.0,           // bottom left        0.0, 1.0, 0.0,              // top centre         // left face        -1.0, -1.0, -1.0,           // bottom rear        -1.0, -1.0, 1.0,            // bottom front        0.0, 1.0, 0.0,              // top centre         // right face        1.0, -1.0, 1.0,             // bottom front        1.0, -1.0, -1.0,            // bottom rear        0.0, 1.0, 0.0               // top centre    }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    }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    }[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);     glTexCoordPointer(2, GL_SHORT, 0, squareTextureCoords);    glEnableClientState(GL_TEXTURE_COORD_ARRAY)// Our new drawing code goes here    rota += 0.5;     glPushMatrix();    {        glTranslatef(-2.0, 0.0, -8.0);        glRotatef(rota, 1.0, 0.0, 0.0);        glVertexPointer(3, GL_FLOAT, 0, pyramidVertices);        glEnableClientState(GL_VERTEX_ARRAY)// Draw the pyramid        // Draw the base -- it's a square remember        glColor4f(1.0, 0.0, 0.0, 1.0);        glDrawArrays(GL_TRIANGLE_FAN, 0, 4)// Front Face        glColor4f(0.0, 1.0, 0.0, 1.0);        glDrawArrays(GL_TRIANGLES, 4, 3)// Rear Face        glColor4f(0.0, 0.0, 1.0, 1.0);        glDrawArrays(GL_TRIANGLES, 7, 3)// Right Face        glColor4f(1.0, 1.0, 0.0, 1.0);        glDrawArrays(GL_TRIANGLES, 10, 3)// Left Face        glColor4f(1.0, 0.0, 1.0, 1.0);        glDrawArrays(GL_TRIANGLES, 13, 3);    }    glPopMatrix();     glPushMatrix();    {        glTranslatef(2.0, 0.0, -8.0);        glRotatef(rota, 1.0, 1.0, 1.0);        glVertexPointer(3, GL_FLOAT, 0, cubeVertices);        glEnableClientState(GL_VERTEX_ARRAY)// Draw the front face in Red        glColor4f(1.0, 0.0, 0.0, 1.0);        glDrawArrays(GL_TRIANGLE_FAN, 0, 4)// Draw the top face in green        glColor4f(0.0, 1.0, 0.0, 1.0);        glDrawArrays(GL_TRIANGLE_FAN, 4, 4)// Draw the rear face in Blue        glColor4f(0.0, 0.0, 1.0, 1.0);        glDrawArrays(GL_TRIANGLE_FAN, 8, 4)// 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);    }    glPopMatrix();      glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);    [context presentRenderbuffer:GL_RENDERBUFFER_OES][self checkGLError:NO];}  


请注意,我们并没有为金字塔添加一个新的纹理坐标数 组。我们可以使用相同的坐标数组,因为它依然为这个范例工作。

改变好了以后,点击”Build an Go”




你所有需要主要的是,这两个物体正在独立的改变自己。金字塔只沿x轴旋转,而立方体沿3轴旋转。再次说明,颜色只是让你更容易的分辨每 个面。

采用3d概念:法线
记不记得我早些时候说过,要保证每个三角形都是逆时针的,即使有些背面的三角形从正面看起来象是顺时针 的?

恩,这是因为你要保证你每个面的数据都是“标准”的。简单的说,一个面的法线是一个假想的线从物体的面抽出。为了说明这点,请看下面 的图:




在 上面的图片里,这个三角形的法线就是一条从面出来的线。当前的三角形的法线表示,这个三角形的3个顶点是逆时针的。我会在介绍光照的那章里说明什么是法 线。

(在这里我真的要说说,我真的很佩服这教程的原作者,他居然可以将如此简单的代码说的如此的复杂,请注意,我没有一点的贬义,而是对 他非常的敬佩。明明每个教程只有几行代码,他确不怕麻烦的一次又一次的说明其中的概念,解释每一行代码的表面意思和内部的概念。这样细致的工作打个比方来 说,就是一个博士生在细心的教幼儿园小朋友唱歌。不是一般人可以做到的。比我们所阅读到的其他OpenGL书籍更加的认真,也更加的细致。这也是我继续翻 译下去的主要动力。)

原创粉丝点击