不解

《深入了解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];
#    

评论

热度(1)