10.2、添加模型

图10-5是在一个场景中一起渲染的地形和模型的屏幕截图。例子OpenGLES_Ch10_1会使用与前面例子中的类相似的UtlityMesh和UtilityModel类。例子OpenGLES_Ch10_1向UtilityMesh类添加了一个“-prepareToPick”方法。 这个新方法的工作方式类似于现存的“ -prepareToDraw”方法,只不过没有启用类似法向量和纹理坐标的顶点属 性,因为在拾取中不需要这些属性。

doesRequireLighting属性被添加到了UtilityModel类中。一些模型,包括树木,会具有烘焙进它们纹理的灯光效果。doesRequireLighting 属性用于区分使用OpenGL灯光模型渲染的模型和使用烘焙灯光渲染的模型。

图 10-5

10.2.1 模型放置

TEModelPlacement类是Xcode使用在图10-4中显示的数据定义所生成的,存储了模型的名字、位置和围绕Y轴的旋转角度属性。与地形一起存 储的TEModelPlacement 实例的集合指定了树木、石头和建筑物的位置。例如,在图10-5中的相同的棕榈树模型会渲染多次,因为多个TEModelPlacement实例在地形内的不同位置引用了棕榈树模型的名字。

10.2.2 模型效果

例子OpenGLES_Ch10_1的地形一直具有烘焙灯光和多重纹理。与此相反,模型会使用一个单独的纹理,并且可能会根据情况带有或者不带有烘焙灯光。 UtilityModelEffect类会管理做了优化的用于渲染带有或者不带有烘焙灯光的模型的OpenGL ES Shading Language程序。

//
//  UtilityModelEffect.h
//  
//

#import "UtilityEffect.h"
#import <GLKit/GLKit.h>  


@interface UtilityModelEffect : UtilityEffect

@property (assign, nonatomic, readwrite) GLKMatrix4 
   projectionMatrix;
@property (assign, nonatomic, readwrite) GLKMatrix4 
   modelviewMatrix;
@property (assign, nonatomic, readwrite) GLKVector4    
   globalAmbientLightColor;
@property (assign, nonatomic, readwrite) GLKVector3 
   diffuseLightDirection;
@property (assign, nonatomic, readwrite) GLKVector4    
   diffuseLightColor;
@property (strong, nonatomic, readwrite) GLKTextureInfo *
   texture2D;

- (void)prepareLightColors;
- (void)prepareModelview;
- (void)prepareModelviewWithoutNormal;

@end

当渲染带有灯光的模型时,UtilityModelEffect 的diffuseLightColor和globalAmbient-LightColor属性被设置为与TETerrainEffect的值相匹配。UtilityModelEffect 的diffiuseLight-Direction属性用于设置灯光方向,具体会与烘焙灯光到地形的lightAndWeightsTextureInfo中所使用的灯光方向相匹配。灯光方向是由TETerrain对象存储的。当渲染不带灯光的模型时,diffuseLightColor 被设置为黑色,globalAmbientLightColor 被设置为全白,这样做可以 避免OpenGL ES灯光方程式更改这个模型的纹理颜色。

用于渲染模型的自定义Shading Language程序一UtilityModelShader. vsh和Utility-ModelShader.fsh,仍然比较简单。UtilityModelShader.vsh 的相关部分会向片元程序提供 纹理坐标、变换的位置坐标,以及为一个单独的定向光计算的灯光效果。

void main()
{
   // Texture coords
   v_texCoords[0] = a_texCoords0;

   // Lighting
   lowp vec3 normal = normalize(u_normalMatrix * a_normal);
   lowp float nDotL = max(
      dot(normal, normalize(u_diffuseLightDirection)), 0.0);
   v_lightColor = (nDotL * u_diffuseLightColor) + 
      u_globalAmbient;

   gl_Position = u_mvpMatrix * vec4(a_position, 1.0); 
}

下面的UtilityModelShader.fsh程序会使用--个技巧来正确地渲染带有半透明纹理的模型。第8章讲解过渲染半透明纹理会产生难看的可视伪影,除非这些有纹理的对象是按照其与平截体近面的距离大小顺序来渲染的。但是,当纹理只包含完全透明和完全不透明的纹素(没有半透明)时,可以有不同排序。

void main()
{
   lowp vec4 color = texture2D(u_units[0], v_texCoords[0]);
   if (color.a < 0.2)
   {  // discard nearly transparent fragments
      discard;
   }

   gl_FragColor = color * v_lightColor;
}

在UtilityModelShader.fsh中的粗体代码会测试当前片元的颜色是否为几乎透明的,如果是的话就丟弃这些片元。一个丢弃的片元既不会影响像素颜色渲染缓存,又不会影响深度渲染缓存。这正是渲染类似在图10-5中显示的树木模型时所期望的结果。丢弃的透明片元不会遮挡其他3D对象产生的片元。这个技巧并不适合半透明纹素,因为在像素颜色渲染缓存中的最终像素颜色取决于多重3D对象生成的片元的混合。在这种情 况下,片元的生成顺序变得至关重要,并且排序也是必要的。


注意 “if”语句的条件执行和discard命令的使用是计算最密集的,因此它们会减慢GPU执行运算的速度。例子OpenGLES_Ch10_1的实现是时间、复杂度与计算密集度之间的一个折中,具体是指基于与观察者的距离来排序模型所需的时间和复杂度与Shading Language程序的计算密集度之间的折中。不同的应用可能会有不同的折中基准。


results matching ""

    No results matching ""