Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5544469
  • 博文数量: 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:21:15

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

记得我们如何在整个屏幕内使用 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”


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


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

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

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


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


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

阅读(1713) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~