6.3、动画化颜色和灯光:例子OpenGLES_Ch6_3

图6-6是例子OpenGLES_Ch6_3的屏幕截图。随着时间重定位OpenGLES灯光或者让OpenGL ES灯光指向不同的位置会产生视觉效果。每个灯光的颜色和强度可以变化。例子OpenGLES_Ch6_3会添加灯光动画到例子OpenGLES_Ch6_2生成的飘扬旗帜上。

例子OpenGLES_Ch6_3会使用第4章简单提到的“ 聚光灯”。聚光灯是在一个圆锥体范围内散发光线而不是各个方向上散发光线的位置光源。除了有一个位置,每个聚光灯还有一个spotDirection、一个spotCutoff角度和一个spotExponent,如图6-7所示。spotDirection是一个用来定义圆锥体内的光线照射方向的矢量。spotCutoff角度是-一个用度计量的角度,这个角度决定了灯光圆锥体的宽度。spotExponent 决定了灯光从聚光灯的中心到边缘变暗的速度。

GLKit的GLKBaseEffect和GLKEffectPropertyLight类一起实现了聚光灯。GLKit提供了两个不同的OpenGL ES灯光实现方式。设置GLKBaseEffect的lightingType属性为GLKLightingTypePerVertex会告诉GLKit去计算在几何图形中的每个顶点的光线值,并在顶点之间插值。设置lightingType为GLKLightingTypePerPixel会产生一个更高质量的渲染结果,但这需要为每个片元重新计算光线效果,而这比每顶点计算要耗费更多的GPU计算量。不为聚光灯做片元计算,光照范围的边缘就会出现锯齿。在OpenGLES_Ch6_3ViewController 的“ -viewDidLoad”方 法里将lightingType设置为GLK-LightingTypePerVertex就会看到锯齿。

图 6-6
图 6-7

例子OpenGLES_Ch6_3和本章接下来的例子会使用GLKit的GLKBaseEffect类的自定义子类AGLKTextureTransformBaseEffect。在例子OpenGLES_Ch6_3中这两个类会产生相同的渲染结果。AGLKTextureTransformBaseEffect类的实现强调的是坐标系变换,这个坐标系变换是指本章后面将会讲解的灯光位置和方向的坐标系变换。另外,后面的例子会用到在写作本书时GLKBaseEffect还不支持的纹理效果。现在利用AGLKTextureTransformBaseEffect可以最大限度地减少例子OpenGLES_Ch6_3和后面的例子之间的不同。


注意

AGLKTextureTransformBaseEffect 类的实现重新创建了GLKit的GLKBaseEffect类的子集,这个子集包含了聚光灯和定向灯。AGLKTextureTransformBaseEffect类使用的Shading Language程序是精心制作的,但是仍然不能提供GLKBaseEffect类的所有灯光功能。跟其他的“AGLK"类一样,最好尽可能使用苹果支持的版本。浏览一下AGLKTextureTransformBaseEffect类的实现,感受一下GLKit的实现方式(利用OpenGLES2.0),以及ShadingLanguage程序的潜在限制。聚光灯是在OpenGLES_Ch6_3ViewController的“-viewDidLoad” 方法中配置的。首先,这个方法会设置总体的灯光模式。


// Use high quality lighting (important for spot lights)
self.baseEffect.lightingType = GLKLightingTypePerPixel;
self.baseEffect.lightModelTwosided = GL_FALSE;
self.baseEffect.lightModelAmbientColor = GLKVector4Make(
    0.6f, // Red
    0.6f, // Green
    0.6f, // Blue
    1.0f);// Alpha

双面灯光可以让几何体对象的前面和背面使用不同的材质。前面就是法向量的方向与之背离的面。光线与材质的相互作用决定了片元的颜色,因此使用不同的材质有可能产生令人印象深刻的效果。在写作本书时,GLKBaseEffect在同一时间只支持一种材质 ,因此是否开启双面灯光只是决定了背面是否完全照亮。

这里定义了两个聚光灯。用于light1的代码与用于light0的代码是相似的,除了light0有一个黄色(相当于混合红色和绿色)而light1有一个青色(相当于混合绿色和蓝色)。

//Light 0 is a spot 1ight
self.baseEffect.light0.enabled = GL_TRUE;
self.baseEffect.light0.spotExponent = 20.0f;
self.baseEffect.light0.spotCutoff = 30.0f;
self.baseEffect.light0.diffuseColor = GLKVector4Make(
    1.0f, // Red
    1.0f, // Green
    0.0f, // Blue
    1.0f);// Alpha
self.baseEffect.light0.specularColor = GLKVector4Make(
    0.0f, // Red 
    0.0f, // Green
    0.0f, // Blue
    1.0f);// Alpha

第三个灯光(light2) 会被设置为一个简单的定向光。回顾一下第4章,定向光的位置是最后-一个元素设置为0.0的一个四元素矢量。变量spotLight2Position会被初始化为矢量{0.0f, 0.5f, 1.0f, 0.0f}。 这个位置表示这个定向光照射自一个无限远的光源。


// Light 2 is directional
self . baseEffect. light2.enabled = GL_ TRUE;
self . baseEffect. light2Position = spotLight2Position;
self .baseEffect. light2.diffuseColor = GLKVector 4Make(
0.5f, // Red
0.5f, // Green
0.5f, // Blue
1.0f);// Alpha

"-viewDidLoad" 方法配置了聚光灯的大部分属性,然后这些属性就永远不会再改变了。但是,与定向光不同的是,聚光灯的位置和方向并不是预先指定的。按照惯例,OpenGL会使用当前(实际上是在设置位置和方向时)的model-view矩阵来变换位置光源的位置和方向。因此,每当灯光坐标系变化时必须重新设置位置和方向。

例子OpenGLES_Ch6_3会旋转聚光灯来移动光束,同时光束会与波动变化的旗帜相互作用。OpenGLES_Ch6_3ViewController 使用了四个新属性来动画化光束的方向,它们是spotLightOTiltAboutXAngleDeg、spotLightOTiltAboutZAngleDeg、 spotLight1Tilt About-XAngleDeg和spotLight1TiltAboutZAngleDeg。与其他动画-样,基于时间的函数决定了属性值的变化。

- (void)updateSpotLightDirections {
    spotLight0TiltAboutXAngleDeg = -20.0f + 30.0f * sinf(
    self.timeSinceLastResume) ;
    spotLight0TiltAboutZAngleDeg = 30.0f * cosf(
    self.timeSinceLastResume);
    spotLight1TiltAboutXAngleDeg = 20.0f + 30.0f *. cosf(
    self.timeSinceLastResume) ;
    spotLight1TiltAboutzAngleDeg = 30.0f * sinf(
    self.timeSinceLastResume);

}

OpenGLES_Ch6_3ViewController 的两个方法-drawLight0和-drawLight1都会变换基于聚光灯的倾斜角属性的model-view坐标系,设置一个聚光灯的位置和方向,然后绘制一个简单的“能够发光”的模型来标示灯光的位置和方向。下面的粗体代码会配置聚光灯为一个位于原点的位置光源,并且聚光方向总是向下指向Y轴的负方向。“能够发光”的模型相应地使用一个接近原点的顶点网格,并且似乎指向Y轴的负方向。每次绘制一个灯光和模型时会创建一个本地坐标系,这个本地坐标系会产生在这个例子中看到的移动的灯光效果。



- (void)drawLight0 {
    GLKMatrix4 savedModelviewMatrix = self.baseEffect.transform.modelviewMatrix;
    self.baseEffect.transform.modelviewMatrix =    GLKMatrix4Translate ( savedModelviewMatrix,
        spotLightoPosition.x,
        spotLightOPosition.y,
        spotLightPosition.z);

    self.baseEffect.transform.modelviewMatrix = GLKMatrix4Rotate (
                    self.baseEffect.transform.modelviewMatrix,
                    GLKMathDegreesToRadians(self.spotLightOTiltAboutXAngleDeg),1,0,0);

    self.baseEffect.transform.modelviewMatrix = GLKMatrix4Rotate (
                    self.baseEffect.transform.modelviewMatrix,
                        GLKMathDegreesToRadians (self.spotLightOTiltAboutZAngleDeg),0,0,1);


    self.baseBffect.light0Position = GLKVector4Make(0, 0, 0, 1);
    self.baseBffect.light0SpotDirection = GLKVector3Make(0, -1, 0);
    [self.baseBffect prepareToDraw] ;
    [self.canLightModel draw];

    self.baseEffect.transform.modelviewMatrix = savedModelviewMatrix;
)

results matching ""

    No results matching ""