《深入了解OpenGL》第五讲 :光照
这章,我们将描述更酷的话题——光照。在此之前,我们先简单得介绍一下如何消除隐藏表面。
我们知道,当远处的物体被近处的一个物体遮挡后,我们将看不到那个远处的物体,而是只能看到那近处的物体。
如果我们要使整个场景看上去有立体感,那么一般是画最远处的物体,然后由远向近绘制。这样,当远处的物体与近处的物体发生叠交时,叠交部分会显示近处物体的那部分。
但是,对于一个非常复杂的3D游戏而言,这么做首先在算法上比较复杂,而且如果在CPU客户端去做的话也非常耗时,因此,GPU目前一般都有Z缓存(深度缓存)来解决这个事情。
深度缓存的原理就是把一个距离观察平面(通常就是近侧裁剪平面)的深度值(即距离)与窗口中的每个像素相关联。一开始使用 glClear(GL_DEPTH_BUFFER_BIT)把所有像素的深度值设置为最大可能的距离(一般是远侧裁剪平面)。随后,我们在场景中就可以以任意顺序来绘制物体了。此时,物体的遮挡都是由GPU硬件负责计算的。计算遮挡一般都是由GPU中专门的逻辑电路来执行完成的。
我们以前讲过这两个函数:
void glOrtho( GLdouble left,
GLdouble right,
GLdouble bottom,
GLdouble top,
GLdouble nearVal,
GLdouble farVal);
void glFrustum( GLdouble left,
GLdouble right,
GLdouble bottom,
GLdouble top,
GLdouble nearVal,
GLdouble farVal);
这两个函数中的最后两个参数就分别表示近侧距离和远侧距离。
我们下面将举一个比较简单的例子来说明这个情况。
因为iPhone版本的代码对深度缓存的配置非常容易,代码都自动生成的,所以我们下面将以iPhone代码作为例子:
# #import <QuartzCore/QuartzCore.h>
# #import <OpenGLES/EAGLDrawable.h>
#
# #import "EAGLView.h"
#
# #define USE_DEPTH_BUFFER 1
#
# // A class extension to declare private methods
# @interface EAGLView ()
#
# @property (nonatomic, retain) EAGLContext *context;
#
# - (BOOL) createFramebuffer;
# - (void) destroyFramebuffer;
#
# @end
#
#
# @implementation EAGLView
#
# @synthesize context;
#
#
# // You must implement this method
# // Override the layerClass method of the UIView class so that objects of your view class create and
# // initialize a CAEAGLLayer object rather than a CALayer object.
# + (Class)layerClass {
# return [CAEAGLLayer class];
# }
#
# //The GL view is stored in the nib file. When it\'s unarchived it\'s sent -initWithCoder:
# - (id)initWithCoder:(NSCoder*)coder {
#
# if ((self = [super initWithCoder:coder])) {
# // Get the layer associated with the view by calling the layer method of UIView.
# CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
#
# // Set the layer properties.
#
# // For optimal performance, it is recommended that you mark the layer as opaque by setting the opaque property provided by the CALayer class.
# eaglLayer.opaque = YES;
# // Optionally configure the surface properties of the rendering surface by assigning a new dictionary of
# // values to the drawableProperties property of the CAEAGLLayer object.
# eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
# [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
#
# context = nil; // [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
# if(context == nil)
# {
# // Before your application can execute any OpenGL ES commands, it must first create and initialize an EAGL context and make it the current context.
# context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
#
# if (!context || ![EAGLContext setCurrentContext:context]) {
# [self release];
# return nil;
# }
# }
# }
#
# return self;
# }
#
#
# - (void)drawView {
#
# // Replace the implementation of this method to do your own custom drawing
# const struct
# {
# GLubyte colours[4];
# GLfloat vertices[3];
# }vertexInfoList[] = {
#
# // The first rectangle
# { .colours = {255, 0, 0, 255},
# .vertices = {-0.8f, 0.5f, -4.0f}
# },
#
# {
# .colours = {255, 0, 0, 255},
# .vertices = {-0.8f, -0.5f, -4.0f}
# },
#
# {
# .colours = {255, 0, 0, 255},
# .vertices = {0.2f, 0.5f, -4.0f}
# },
#
# {
# .colours = {255, 0, 0, 255},
# .vertices = {0.2f, -0.5f, -4.0f}
# },
#
# // The second rectangle
# {
# .colours = {0, 255, 0, 255},
# .vertices = {-0.3, 0.3f, -3.0f}
# },
#
# {
# .colours = {0, 255, 0, 255},
# .vertices = {-0.3f, -0.7f, -3.0f}
# },
#
# {
# .colours = {0, 255, 0, 255},
# .vertices = {0.7f, 0.3f, -3.0f}
# },
#
# {
# .colours = {0, 255, 0, 255},
# .vertices = {0.7f, -0.7f, -3.0f}
# }
# };
#
# // Drawing code here.
# glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#
# glVertexPointer(3, GL_FLOAT, 16, vertexInfoList[0].vertices);
# glColorPointer(4, GL_UNSIGNED_BYTE, 16, vertexInfoList[0].colours);
#
# glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
# glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
#
# // Assuming you allocated a color renderbuffer to point at a Core Animation layer, you present its contents by making it the current renderbuffer
# // and calling the presentRenderbuffer: method on your rendering context.
# glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
# [context presentRenderbuffer:GL_RENDERBUFFER_OES];
# }
#
#
# - (void)layoutSubviews {
# [EAGLContext setCurrentContext:context];
# [self destroyFramebuffer];
# [self createFramebuffer];
# [self drawView];
# }
#
#
# - (BOOL)createFramebuffer {
#
# // An OpenGL ES 2.0 application would omit the OES suffix.
#
# // Create the framebuffer and bind it so that future OpenGL ES framebuffer commands are directed to it.
# glGenFramebuffersOES(1, &viewFramebuffer);
# glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
#
# // Create a color renderbuffer, allocate storage for it, and attach it to the framebuffer.
# glGenRenderbuffersOES(1, &viewRenderbuffer);
# glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
# // Create the color renderbuffer and call the rendering context to allocate the storage on our Core Animation layer.
# // The width, height, and format of the renderbuffer storage are derived from the bounds and properties of the CAEAGLLayer object
# // at the moment the renderbufferStorage:fromDrawable: method is called.
# [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
# glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
#
# // Retrieve the height and width of the color renderbuffer.
# glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
# glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
#
# if (USE_DEPTH_BUFFER) {
# // Perform similar steps to create and attach a depth renderbuffer.
# glGenRenderbuffersOES(1, &depthRenderbuffer);
# glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
# glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
# glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
# }
#
# // Test the framebuffer for completeness.
# if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
# NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
# return NO;
# }
#
# // Initialize the OpenGL ES context state
# [EAGLContext setCurrentContext:context];
#
评论