4.4、OpenGLES_Ch4_1示例

例子OpenGLES_Ch4_1演示了基于GLKit的灯光模拟,并为浏览法向量的效果提供了多个选项。图4-7显示了OpenGLES_Ch4_1应用的屏幕截图。一个简单的钻出平面的四面角锥体。从后面照向角锥体的右边的定向光会让角锥体的某些面比其他的面更亮。使用平面法线天量会产生一个多面的外观。平均法向量会让四面角锥体看起来就像是一个光滑的圆锥体。图4-7中的第三个屏幕截图使用渲染的线描绘了计算出来的法向量和光线的方向。

移动OpenGLES_Ch4_1应用的滑块来升高和降低角锥体的顶点,这会改变角锥体的面与光线方向之间的角度。每次顶点的位置发生改变,法向量就会被重新计算。计算法向量使用了两个不同的方式。当使用平面法线矢量时,每个三角形的每个顶 点拥有相同的法向量,因此三角形看起来是平的。否则,每个顶点的法向量就是共享顶点的所有三角形的法向量的一个平均值。这个例子提供了一个用于切换是否开启法向量绘制的用户接口和一个指示光线方向的线。在移动滑块以显示由于法向量与光线方向之间的角度变化所产生的明暗变化时,请仔细检查法向量。

图 4-7

图4-8展示了在例子OpenGLES_Ch4_1中使用的几何图形。从A开始到I所标识的九个顶点定义了标识为0到7的8个三角形。用户界面中的滑块会改变顶点E的Z坐标,顶点E定义了三角锥的高度。当顶点E上下移动时,三角形2到5会改变。图4-8等同于图4-7中显示的相同的三角形。例子OpenGLES_Ch4_1是使用侧俯视角来渲染三角形的,参见图4-7。图4-8是使用顶部直视视角来描述三角形的。在OpenGLES_Ch4_1ViewController.m 中使用下面的代码定义和初始化变量和vertexA到vertexI。

图 4-8
//
// Define the positions and normal vectors of each vertex in the
// example.

SceneVertex vertexA = {[-0.5,0.5, -0.5),[0.0, 0.0, 1.0)};
SceneVertex vertexB = {(-0.5,0.0, -0.5),[0.0, 0.0, 1.0]};
SceneVertex vertexC = {[-0.5, 0.5, -0.5),[0.0, 0.0, 1.0)};
SceneVertex vertexD = {( 0.0,0.5, -0.5),(0.0, 0.0, 1.0)};
SceneVertex vertexE = {( 0.0,0.0, 0.0), [0.0, 0.0, 1.0]};
SceneVertex vertexF = {( 0.0, -0.5, -0.5),(0.0, 0.0, 1.0)};
SceneVertex vertexG = {( 0.5,0.5, -0.5),(0.0, 0.0, 1.0)};
SceneVertex vertexH = {( 0.5,0.0, -0.5),[0.0, 0.0, 1.0]};
SceneVertex vertexI = {( 0.5, -0.5, -0.5),(0.0, 0.0, 1.0)};

新数据类型SceneTriangle声明了一个用于保存定义一个三角形的三个顶点的结构体。本例使用OpenGLES_Ch4_1ViewController 类的实例的一个数组来保存在图4-8中描述的8个三角形。


// This data type is used to store information for triangles
typedef struct {
    SceneVertex vertices[3]
} SceneTriangle;

@interface OpenGLES_Ch4_1ViewController ()
{
    SceneTriangle triangles[8];
}
@end

如下代码所示,OpenGLES_Ch4_1ViewController 的“-viewDidLoad” 方法创建了叫做baseEffect和extraEffect的GLKBaseEffect实例。粗体代码设置了baseEffect 的灯光之一。

// Create a base effect that provides standard OpenGL ES 2.0
// shading language programs and set constants to be used for
// all subsequent rendering
self.baseEffect = [ [GLKBaseEffect alloc] init];
self.baseEffect.lightO.enabled = GL_TRUE;
self.baseEffect.light0.diffuseColor = GLKVector4Make(
    0.7f, // Red
    0.7f, // Green
    0.7f, // Blue
    1.0f);// Alpha
self.baseEffect.light0.position = GLKVector4Make(
    1.0f,
    1.0f,
    0.5f,
    0.0f);

extraEffect = [ [GLKBaseEffect alloc] init];
self.extraEffect.useConstantColor = GL_TRUE;
self.extraEffect.constantColor = GLKVector4Make(
    0.0f, // Red
    1.0f,// Green
    0.0f, // Blue.
    1.0f);// Alpha

在“-viewDidLoad”中的下一段代码块提供了在第5章将要讲解的技术的一个预览。在OpenGLES_Ch4_1例子中的整个场景被旋转并定位以更容易地看到三角锥的高度变化。注释掉或者删掉下面的代码块,然后重建并运行OpenGLESCh41工程以从 上向下观察三角锥。

{ // Comment out this block to render the scene top down
    GLKMatrix4 modelViewMatrix = GLKMatrix4MakeRotation(
        GLKMathDegreesToRadians(-60.0f), 1.0f, 0.0f, 0.0f);
    modelViewMatrix = GLKMatrix4Rotate(
        modelViewMatrix,
        GLKMathDegreesToRadians(-30.0f), 0.0f, 0.0f, 1.0f);

    modelViewMatrix = GLKMatrix4Translate(
        modelViewMatrix,
        0.0f, 0.0f, 0.25f);
    self.baseEffect.transform.modelviewMatrix = modelViewMatrix;
    self.extraEffect.transform.modelviewMatrix = modelViewMatrix;
}

在图4-8中的从0到7的8个三角形被使用如下的从vertexA到vertexI的顶点初始化,然后三角形被存储在一一个顶点属性数组缓存中以供GPU使用。

triangles[0] = SceneTriangleMake(vertexA, vertexB, vertexD) ;
triangles{1] = SceneTriangleMake (vertexB, vertexC, vertexF);
triangles[2] = SceneTriangleMake(vertexD, vertexB, vertexE);
triangles[3] = SceneTriangleMake (vertexE, vertexB, vertexF);
triangles[4] = SceneTriangleMake (vertexD, vertexE, vertexH);
triangles[5] = SceneTriangleMake (vertexE, vertexF, vertexH);
triangles[6] = SceneTriangleMake (vertexG, vertexD, vertexH);
triangles[7] = SceneTriangleMake( vertexH, vertexF, vertexI) ;
// Create vertex buffer containing vertices to draw
self.vertexBuffer = [ [AGLKVertexAttribArrayBuffer alloc]
        initWithAttribstride:sizeof (SceneVertex )
            numberOfvertices:sizeof (triangles) / sizeof ( SceneVertex)
                        data:triangles
                        usage:GL_DYNAMIC_DRAW];

下一个代码块显示了OpenGLES_Ch4_ViewController 的“-glkView:drawInRect:"方法的整个实现。为绘制准备好GLKitbaseEffect。像素颜色渲染缓存被清除。每个顶点的位置和法线属性被准备好以供GPU使用。粗体代码发送每个顶点的法向量给GPU。使用baseEffect渲染的三角形包含由GLKit在后台自动生成的Shanding Language程序提供的模拟灯光。最后,如果OpenGLES_Ch4_1ViewController 的should-DrawNormals属性值是YES,那么OpenGLES_Ch4_1ViewController 的“-drawNormals" 方法会被调用。

///////////////////////11/////////11////111//1111////////
// GLKView delegate method: Called by the view controller's view
// whenever Cocoa Touch asks the view controller's view to
// draw itself. (In this case, render into a frame buffer that
// shares memory with a Core Animation Layer )
- (void)glkView: (GLKView *)view drawInRect : (CGRect )rect {

    [self.baseEffect prepareToDraw] ;
    // Clear back frame buffer (erase previous drawing)
    [(AGLKContext★)view.context clear:GL_COLOR_BUFFER_BIT];


    [self.vertexBuffer prepareToDrawwithAttrib:GLKVertexAttribPosition
                            numberOfCoordinates:3
                            attriboffset:offsetof(SceneVertex, position)
                            shouldEnable:YES];


    [self.vertexBuffer prepareToDrawwithAttrib:GLKVertexAttribNormal
                            numberOfCoordinates:3
                                    attriboffset:offsetof (SceneVertex, normal)
                                    shouldEnable:YES];


    // Draw triangles using vertices in the currently bound vertex
    // buffer
    [self.vertexBuffer drawArrayWithMode:GL_TRIANGLES
                        startVertexIndex:0
                        numberofVertices:sizeof(triangles) / sizeof(SceneVertex)];

    if(self.shouldDrawNormals){
        [self drawNormals];
    }
}

shouldDrawNormals属性同centerVertexHeight和shouldUseFaceNormals属性一起在OpenGLES_Ch4_1ViewController.h 中声明。更新属性值的代码在OpenGLES_Ch4_1ViewController类的实现中。每当一个如滑块或者切换器的用户界面对象改变时,OpenGLES_Ch4_1ViewController 会使用Cocoa Touch Target-Action设计模式。Target-Action设计模式的帮助文档网址为: http://developer.apple.com/iphone/library/documentation/General/Conceptual/Devpedia-CocoaApp/TargetAction.html。本质上接收一个单独的对象参数的方法会被用户界面对象调用来更新应用的状态。改变状态的特定对象是用户界面对象的目标(target)。 这个方法叫做动作(action)。 按照惯例,动作方法的参数是调用这个动作的用户界面对象。

很多动作方法使用下面的形式来声明:

- (IBAction)takeShouldUseFaceNormalsFrom:(UISwitch * )sender ;
- (IBAction )takeShouldDrawNormalsFrom:(UISwitch * )sender;
- (IBAction)takeCenterVertexHeightFrom:(UISlider★ )sender;

Xcode的.storyboard编辑器使可视化用户界面构建成为可能,并包含了目标和动作的详细说明。在例子OpenGLES_Ch4_1中,每当用户移动了滑块,一个滑块用户 界面就会调用“ -takeCenterVertexHeightFrom:”动作。“-takeCenterVertexHeightFrom:”的实现是非常直白的,如下代码所示。


// This method sets the value of the center vertex height to the
// value obtained from sender
- (IBAction )takeCenterVertexHeightFrom: (UISlider *)sender {
    self.centerVertexHeight = sender.value;
}

当centerVertexHeight属性值变化时,包含图4-8中的vertexE的三角形也会变化。当三角形变化时,三角形的法向量必须被重新计算。这个重新计算是例子OpenGLES_Ch4_1的 关键。

OpenGLES_Ch4_1ViewController 通过实现属性的自定义访问器方法来拦截属性的改变。访问器是在第2章中介绍的,它们是被特别命名的方法,可以被调用来修改属性值。当编译类似self.centerVertexHeight = sender.value; 的点标记语法时,Objective-C编译器会自动生成对于访问器方法的调用。用来设置centerVertexHeight属 性的值的方法被命名为-setCenterVertexHeight:


// This method sets the value of centerVertexHeight and updates
// vertex normals
- (void )setCenterVertexHeight: (GLfloat )aValue {

    centerVertexHeight = aValue;

    SceneVertex newVertexE = vertexE;
    newertexE.position.z = self.centerVertexHeight;
    triangles[2] = SceneTriangleMake(vertexD, vertexB, newVertexE);
    triangles[3] = SceneTriangleMake( newVertexE, vertexB, vertexF);
    triangles[4] = SceneTriangleMake (vertexD, newVertexE, vertexH);
    triangles[5] = SceneTriangleMake( newVertexE, vertexF, vertexH) ;
    [self updateNormals];

}

每次“ -setCenterVertexHeight:”方法被调用来改变centerVertexHeight属性的值时,vertexE的高度(Z分量)也改变了,并且包含vertexE的四个三角形也被重新创建。之后OpenGLES_Ch4_1ViewController 的“ -updateNormals”方法被调用来重新计算受影 响后的法向量。



// triangles using either face normals or averaged vertex normals.
- (void )updateNormals {

if (self. shouldUseFaceNormals){ // Use face normal vectors to produce facets effect
// Lighting Step 3
    SceneTrianglesUpdateFaceNormals(triangles);
} else { // Interpolate normal vectors for smooth rounded effect
// Lighting Step 3
    SceneTrianglesUpdateVertexNormals (triangles);
// Reinitialize the vertex buffer containing vertices to draw
[self.vertexBuffer
            reinitWithAttribstride: sizeof (SceneVertex)
                    numberOfVertices:sizeof(triangles) / sizeof (SceneVertex)
                        data:triangles];
}

}

包含法向量的三角形的顶点值被更新在顶点属性数组缓存中,以便在下一次场景被渲染时,它们可以被GPU使用。如果你对于法向量重新计算的数学计算过程比较好奇,请看一下在OpenGLES_Ch4_1ViewController.m 文件中的SceneTrianglesUpdateFace-Normals(函数。它使用了在4.2节介绍过的SceneVector3UnitNormal()函数。使用例子OpenGLES_Ch4_1做测试来影响渲染结果。试着改变灯光的位置。这个例子会为光源保持默认颜色值,试着为环境和镜面反射光部分指定颜色。试着添加第二个光源。

results matching ""

    No results matching ""