OpenGL ES实现AGLKView模仿GLKView

首先我们去创建这个一个AGLKView去继承自UIView类,然后我们进行自定义操作,主要就是去模仿GLKView中的定义

#import <UIKit/UIKit.h>
#import <OpenGLES/ES2/gl.h>
#import <OpenGLES/ES2/glext.h>

@class EAGLContext;
//协议和GLKViewDelegate类似
@protocol AGLKViewDelegate;

// Type for depth buffer formats.
//深度缓冲区的格式
typedef enum
{
    AGLKViewDrawableDepthFormatNone = 0,
    AGLKViewDrawableDepthFormat16,
} AGLKViewDrawableDepthFormat;


/////////////////////////////////////////////////////////////////
// This subclass of the Cocoa Touch UIView class uses OpenGL ES
// to render pixel data into a Frame Buffer that shares pixel
// color storage with a Core Animation Layer.
@interface AGLKView : UIView
{    
   //默认的帧缓冲区
   GLuint        defaultFrameBuffer;
   //颜色缓冲区
   GLuint        colorRenderBuffer;
   //深度缓冲区
   GLuint        depthRenderBuffer;

}

@property (nonatomic, weak) IBOutlet id 
   <AGLKViewDelegate> delegate;
//上下文   
@property (nonatomic, strong) EAGLContext *context;
@property (nonatomic, readonly) NSInteger drawableWidth;
@property (nonatomic, readonly) NSInteger drawableHeight;
@property (nonatomic) AGLKViewDrawableDepthFormat
   drawableDepthFormat;

- (instancetype)initWithFrame:(CGRect)frame context:(EAGLContext *)context;
- (void)display;

@end

#pragma mark - AGLKViewDelegate
//让AGLKView的代理对象实现渲染操作的方法
@protocol AGLKViewDelegate <NSObject>

@required
- (void)glkView:(AGLKView *)view drawInRect:(CGRect)rect;

AGLKView的实现方法

#import "AGLKView.h"
#import <QuartzCore/QuartzCore.h>


@implementation AGLKView

@synthesize delegate;
@synthesize context;
@synthesize drawableDepthFormat;

/////////////////////////////////////////////////////////////////
// This method returns the CALayer subclass to be used by  
// CoreAnimation with this view
//设置AGLKView的layer层,由于GLKView就是属于这一类型的CAEAGLLayer,所以我们也要进行设置
+ (Class)layerClass
{
   return [CAEAGLLayer class];
}


/////////////////////////////////////////////////////////////////
// This method is designated initializer for the class 
//该方法是该类的指定初始化器方法
- (id)initWithFrame:(CGRect)frame context:(EAGLContext *)aContext;
{
   if ((self = [super initWithFrame:frame]))
   {
      CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

        //设置CAEAGLLayer实例中要用到的OpenGL ES的帧缓存类型的信息
      /*
       kEAGLDrawablePropertyRetainedBacking 的值为NO,不使用保留背景其实就是告诉Core Animation 不要试图保留任何以前绘制的图像留作以后重用
       kEAGLColorFormatRGBA8 告诉Core Animation 用8位来保存层内每个像素的每个颜色元素的值
       */
      eaglLayer.drawableProperties = 
         [NSDictionary dictionaryWithObjectsAndKeys:
             [NSNumber numberWithBool:NO], 
             kEAGLDrawablePropertyRetainedBacking, 
             kEAGLColorFormatRGBA8, 
             kEAGLDrawablePropertyColorFormat, 
             nil];

      self.context = aContext;
   }

   return self;
}


/////////////////////////////////////////////////////////////////
// This method is called automatically to initialize each Cocoa
// Touch object as the object is unarchived from an 
// Interface Builder .xib or .storyboard file.
- (id)initWithCoder:(NSCoder*)coder
{    
   if ((self = [super initWithCoder:coder]))
   {
      CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

      //设置CAEAGLLayer实例中要用到的OpenGL ES的帧缓存类型的信息
      /*
       kEAGLDrawablePropertyRetainedBacking 的值为NO,不使用保留背景其实就是告诉Core Animation 不要试图保留任何以前绘制的图像留作以后重用
       kEAGLColorFormatRGBA8 告诉Core Animation 用8位来保存层内每个像素的每个颜色元素的值
       */
      eaglLayer.drawableProperties = 
         [NSDictionary dictionaryWithObjectsAndKeys:
             [NSNumber numberWithBool:NO], 
             kEAGLDrawablePropertyRetainedBacking, 
             kEAGLColorFormatRGBA8, 
             kEAGLDrawablePropertyColorFormat, 
             nil];

   }

   return self;
}


/////////////////////////////////////////////////////////////////
// This method sets the receiver's OpenGL ES Context. If the 
// receiver already has a different Context, this method deletes
// OpenGL ES Frame Buffer resources in the old Context and the 
// recreates them in the new Context.
- (void)setContext:(EAGLContext *)aContext
{
   //由于上下文保存了缓存,所以修改视图的上下文会导致先前创建的所有缓存失效,并需要创建和配置新的缓存,会受缓存操作影响的上下文是在调用openGL ES 函数之前设定为当前上下文的
   if(context != aContext)
   {  // Delete any buffers previously created in old Context
      [EAGLContext setCurrentContext:context];

      if (0 != defaultFrameBuffer)
      {
         glDeleteFramebuffers(1, &defaultFrameBuffer); // Step 7
         defaultFrameBuffer = 0;
      }

      if (0 != colorRenderBuffer)
      {
         glDeleteRenderbuffers(1, &colorRenderBuffer); // Step 7
         colorRenderBuffer = 0;
      }

      if (0 != depthRenderBuffer)
      {
         glDeleteRenderbuffers(1, &depthRenderBuffer); // Step 7
         depthRenderBuffer = 0;
      }

      context = aContext;

      if(nil != context)
      {  // Configure the new Context with required buffers
         context = aContext;
         [EAGLContext setCurrentContext:context];

         glGenFramebuffers(1, &defaultFrameBuffer);    // Step 1
         glBindFramebuffer(                            // Step 2
            GL_FRAMEBUFFER,             
            defaultFrameBuffer);

         glGenRenderbuffers(1, &colorRenderBuffer);    // Step 1
         glBindRenderbuffer(                           // Step 2
            GL_RENDERBUFFER, 
            colorRenderBuffer);

         // Attach color render buffer to bound Frame Buffer
         //配置当前的帧缓存,去连接colorRenderBuffer,去保存渲染的像素颜色
         glFramebufferRenderbuffer(
            GL_FRAMEBUFFER, 
            GL_COLOR_ATTACHMENT0, 
            GL_RENDERBUFFER, 
            colorRenderBuffer);

         // Create any additional render buffers based on the
         // drawable size of defaultFrameBuffer
         //根据defaultFrameBuffer的drawable size创建任何额外的render缓冲区。
         [self layoutSubviews];
      }
   }
}


/////////////////////////////////////////////////////////////////
// This method returns the receiver's OpenGL ES Context
- (EAGLContext *)context
{
   return context;
}


/////////////////////////////////////////////////////////////////
// Calling this method tells the receiver to redraw the contents 
// of its associated OpenGL ES Frame Buffer. This method 
// configures OpenGL ES and then calls -drawRect:
- (void)display;
{
   //设置视图的上下文为当前的上下文
   [EAGLContext setCurrentContext:self.context];
   //告诉OpenGL ES 让渲染填满整个帧缓存
   glViewport(0, 0, self.drawableWidth, self.drawableHeight);

   //调用视图的drawRect方法就是用来实现用OpenGL ES的真正的绘图
   [self drawRect:[self bounds]];

    //让上下文调整外观并且去使用Core Animation合成器把帧缓存的像素颜色渲染缓存和其他相关层混合起来,然后在屏幕上显示一个renderbuffer的内容
   [self.context presentRenderbuffer:GL_RENDERBUFFER];
}


/////////////////////////////////////////////////////////////////
// This method is called automatically whenever the receiver
// needs to redraw the contents of its associated OpenGL ES
// Frame Buffer. This method should not be called directly. Call
// -display instead which configures OpenGL ES before calling
// -drawRect:
//在里面调用的是- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect;方法
- (void)drawRect:(CGRect)rect
{

   if(delegate)
   {
      //让代理去调用glkView:drawInRect方法
      [self.delegate glkView:self drawInRect:[self bounds]];
   }
}


/////////////////////////////////////////////////////////////////
// This method is called automatically whenever a UIView is 
// resized including just after the view is added to a UIWindow.
//当一个UIView被重新调整,包括视图被添加到一个UIWindow时,这个方法就会被自动调用。
//任何接收到视图重新调整大小的消息,都会去调用layoutSubviews方法
- (void)layoutSubviews
{
   CAEAGLLayer  *eaglLayer = (CAEAGLLayer *)self.layer;

   // Make sure our context is current
   //设置视图的上下文为当前的上下文
   [EAGLContext setCurrentContext:self.context];

   // Initialize the current Frame Buffer’s pixel color buffer  初始化当前帧缓冲区的像素颜色缓存,它会去和Core Animation Layer去共享像素颜色仓库
   // so that it shares the corresponding Core Animation Layer’s
   // pixel color storage.
   //下面这个方法会调整视图的缓存的尺寸以匹配层的尺寸,所以会在setContext设置完上下文之后进行调用
   [self.context renderbufferStorage:GL_RENDERBUFFER 
      fromDrawable:eaglLayer];

   //如果深度缓冲区存在,就去删除
   if (0 != depthRenderBuffer)
   {
      glDeleteRenderbuffers(1, &depthRenderBuffer); // Step 7
      depthRenderBuffer = 0;
   }

   GLint currentDrawableWidth = self.drawableWidth;
   GLint currentDrawableHeight = self.drawableHeight;

   if(self.drawableDepthFormat != 
      AGLKViewDrawableDepthFormatNone &&
      0 < currentDrawableWidth &&
      0 < currentDrawableHeight)
   {
      //生成深度缓冲区
      glGenRenderbuffers(1, &depthRenderBuffer); // Step 1
       //绑定深度缓冲区
      glBindRenderbuffer(GL_RENDERBUFFER,        // Step 2
         depthRenderBuffer);
       //初始化深度缓冲区
      glRenderbufferStorage(GL_RENDERBUFFER,     // Step 3 
         GL_DEPTH_COMPONENT16, 
         currentDrawableWidth, 
         currentDrawableHeight);
       //连接深度缓冲区
      glFramebufferRenderbuffer(GL_FRAMEBUFFER,  // Step 4 
         GL_DEPTH_ATTACHMENT, 
         GL_RENDERBUFFER, 
         depthRenderBuffer);
   }

   // Check for any errors configuring the render buffer
   //检查配置渲染缓冲区的错误
   GLenum status = glCheckFramebufferStatus(
      GL_FRAMEBUFFER) ;

   if(status != GL_FRAMEBUFFER_COMPLETE) {
       NSLog(@"failed to make complete frame buffer object %x", status);
   }

   // Make the Color Render Buffer the current buffer for display
   //使颜色渲染缓冲区在当前缓冲区显示
   glBindRenderbuffer(GL_RENDERBUFFER, colorRenderBuffer);
}


/////////////////////////////////////////////////////////////////
// This method returns the width in pixels of current context's
// Pixel Color Render Buffer
- (NSInteger)drawableWidth;
{
   GLint          backingWidth;

    //根据OpenGL ES的glGetRenderbufferParameteriv来查询当前上下文的帧缓存的像素颜色的渲染缓存尺寸
   glGetRenderbufferParameteriv(
      GL_RENDERBUFFER, 
      GL_RENDERBUFFER_WIDTH, 
      &backingWidth);

   return (NSInteger)backingWidth;
}


/////////////////////////////////////////////////////////////////
// This method returns the height in pixels of current context's
// Pixel Color Render Buffer
//返回高度
- (NSInteger)drawableHeight;
{
   GLint          backingHeight;

   glGetRenderbufferParameteriv(
      GL_RENDERBUFFER, 
      GL_RENDERBUFFER_HEIGHT, 
      &backingHeight);

   return (NSInteger)backingHeight;
}


/////////////////////////////////////////////////////////////////
// This method is called automatically when the reference count 
// for a Cocoa Touch object reaches zero.
- (void)dealloc
{
   // Make sure the receiver's OpenGL ES Context is not current
   if ([EAGLContext currentContext] == context)
   {  

      [EAGLContext setCurrentContext:nil];
   }

   // Deletes the receiver's OpenGL ES Context
   //设置context=nil,就会去删除缓存区对象会去调用setContext方法
   context = nil;
}

之后我们再去自定义一个AGLKViewController类,这个类就是拿来模仿GLKViewController类的,很简单里面我们只需要去添加一个CADisplayLink定时器类和可以调节刷新的帧率就可以了。类定义如下所示

#import <UIKit/UIKit.h>
#import "AGLKView.h"

@class CADisplayLink;


@interface AGLKViewController : UIViewController 
   <AGLKViewDelegate>
{
   CADisplayLink     *displayLink;
   NSInteger         preferredFramesPerSecond;
}

/////////////////////////////////////////////////////////////////
// This property contains the desired frames per second rate for
// drawing. The default is 30.
@property (nonatomic) NSInteger preferredFramesPerSecond;

/////////////////////////////////////////////////////////////////
// This property contains the actual frames per second based 
// upon the value for preferredFramesPerSecond property 
// and the screen on which the GLKView resides. 
// The value is as close to preferredFramesPerSecond as 
// possible, without exceeding the screen's refresh rate. This 
// value does not account for dropped frames, so it is not a 
// measurement of your statistical frames per second. It is the 
// static value for which updates will take place.
@property (nonatomic, readonly) NSInteger framesPerSecond;

/////////////////////////////////////////////////////////////////
// Thi property determines whether to pause or resume drawing
// at the rate defined by the framesPerSecond property.
// Initial value is NO.
@property (nonatomic, getter=isPaused) BOOL paused;

@end

类的实现

#import "AGLKViewController.h"
#import <QuartzCore/QuartzCore.h>


@implementation AGLKViewController

/////////////////////////////////////////////////////////////////
// This constant defines the default number of frame per second
// rate to redraw the receiver's view when the receiver is not
// paused.
//定义了每秒帧数的默认数量
static const NSInteger kAGLKDefaultFramesPerSecond = 30;


/////////////////////////////////////////////////////////////////
// This method is the designated initializer.
// The receiver's Core Animation displayLink instance is created
// and configured to prompt redraw of the receiver's view
// at the default number of frames per second rate.
- (id)initWithNibName:(NSString *)nibNameOrNil 
   bundle:(NSBundle *)nibBundleOrNil;
{
    if(nil != (self = [super initWithNibName:nibNameOrNil 
       bundle:nibBundleOrNil]))
    {
       //创建displayLink,循环调用drawView方法,这个方法是去调用的AGLKView的
       //display方法,display方法调用AGLKView的drawRect方法,draw内部则让代理对象
       //去调用- (void)glkView:(AGLKView *)view drawInRect:(CGRect)rect方法
      displayLink = 
         [CADisplayLink displayLinkWithTarget:self 
            selector:@selector(drawView:)];

       //设置首选帧率为kAGLKDefaultFramesPerSecond也就是30
      self.preferredFramesPerSecond = 
         kAGLKDefaultFramesPerSecond;

      //添加进运行循环
      [displayLink addToRunLoop:[NSRunLoop currentRunLoop] 
         forMode:NSDefaultRunLoopMode];

      self.paused = NO;
    }

    return self;
}
/////////////////////////////////////////////////////////////////
// This method is called automatically to initialize each Cocoa
// Touch object as the object is unarchived from an 
// Interface Builder .xib or .storyboard file.
// The receiver's Core Animation displayLink instance is created
// and configured to prompt redraw of the receiver's view
// at the default number of frames per second rate.
- (id)initWithCoder:(NSCoder*)coder
{    
   if (nil != (self = [super initWithCoder:coder]))
   {
      displayLink = 
         [CADisplayLink displayLinkWithTarget:self 
            selector:@selector(drawView:)];

      self.preferredFramesPerSecond = 
         kAGLKDefaultFramesPerSecond;

      [displayLink addToRunLoop:[NSRunLoop currentRunLoop] 
         forMode:NSDefaultRunLoopMode];

      self.paused = NO;
   }

   return self;
}
/////////////////////////////////////////////////////////////////
// This method is called when the view controller's view is 
// loaded and performs initialization before the view is asked 
// to draw.
- (void)viewDidLoad
{
   [super viewDidLoad];

   // Verify the type of view created automatically by the
   // Interface Builder storyboard
   AGLKView *view = (AGLKView *)self.view;
    //判断view是不是AGLKView类的
   NSAssert([view isKindOfClass:[AGLKView class]],
      @"View controller's view is not a AGLKView");

   view.opaque = YES;
   view.delegate = self;
}

/////////////////////////////////////////////////////////////////
// This method is called when the receiver's view appears and
// unpauses the receiver. 
- (void)viewDidAppear:(BOOL)animated
{
   [super viewDidAppear:animated];
   self.paused = NO;
}

/////////////////////////////////////////////////////////////////
// This method is called when the receiver's view disappears and
// pauses the receiver. 
- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
   self.paused = YES;
}

/////////////////////////////////////////////////////////////////
// This method is called automatically and allows all standard 
// device orientations. 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
   if ([[UIDevice currentDevice] userInterfaceIdiom] == 
      UIUserInterfaceIdiomPhone) 
   {
       return (interfaceOrientation != 
          UIInterfaceOrientationPortraitUpsideDown);
   } 
   else 
   {
       return YES;
   }
}


/////////////////////////////////////////////////////////////////
// This method is called at the receiver's framesPerSecond rate
// when the receiver is not paused and instructs the receiver's 
// view to redraw.
- (void)drawView:(id)sender
{
   [(AGLKView *)self.view display];
}


/////////////////////////////////////////////////////////////////
// Returns the receiver's framesPerSecond property value.  
- (NSInteger)framesPerSecond;
{
   return 60 / displayLink.frameInterval;
}


/////////////////////////////////////////////////////////////////
// This method returns the desired frames per second rate at for
// redrawing the receiver's view.
- (NSInteger)preferredFramesPerSecond;
{
   return preferredFramesPerSecond;
}


/////////////////////////////////////////////////////////////////
// This method sets the desired frames per second rate at for
// redrawing the receiver's view.
- (void)setPreferredFramesPerSecond:(NSInteger)aValue
{
   preferredFramesPerSecond = aValue;

    //设置小于1会出错,如果设置为2 就表示为帧率的一半
   displayLink.frameInterval = MAX(1, (60 / aValue));
}


/////////////////////////////////////////////////////////////////
// This method returns YES if the receiver is paused and NO 
// otherwise. The receiver does not automatically prompt redraw
// of the receiver's view when paused.
- (BOOL)isPaused
{
   return displayLink.paused;
}


/////////////////////////////////////////////////////////////////
// This method sets whether the receiver is paused. The receiver 
// automatically prompts redraw of the receiver's view 
// unless paused.
- (void)setPaused:(BOOL)aValue
{
   displayLink.paused = aValue;
}


/////////////////////////////////////////////////////////////////
// This required AGLKViewDelegate method does nothing. Subclasses
// of this class may override this method to draw on behalf of
// the receiver's view.
- (void)glkView:(AGLKView *)view drawInRect:(CGRect)rect;
{
}

@end

接下来就用这两个类来实现画一个三角形,创建一个继承于AGLKViewController的类

类定义

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

@interface OpenGLES_ViewController : AGLKViewController
{
   GLuint vertexBufferID;
}
//苹果帮我们封装好的可以相当于一个着色器简化类
@property (strong, nonatomic) GLKBaseEffect *baseEffect;

@end

类的实现,其实主要就是设置顶点数据,传入顶点数据给顶点缓冲区,vertexBufferID这个是顶点缓冲区的标识符,然后再去实现- (void)glkView:(AGLKView *)view drawInRect:(CGRect)rect这个代理方法,然后进行绘制前的配置操作,比如说调用glClear清屏,以及设置允许使用绑定顶点缓存的位置,和设置数据格式调用glVertexAttribPointer方法,然后再调用glDrawArrays进行绘制

#import "OpenGLES_ViewController.h"

@implementation OpenGLES_Ch2_2ViewController

@synthesize baseEffect;

/////////////////////////////////////////////////////////////////
// This data type is used to store information for each vertex
typedef struct {
   GLKVector3  positionCoords;
}
SceneVertex;

/////////////////////////////////////////////////////////////////
// Define vertex data for a triangle to use in example
static const SceneVertex vertices[] = 
{
   {{-0.5f, -0.5f, 0.0}}, // lower left corner
   {{ 0.5f, -0.5f, 0.0}}, // lower right corner
   {{-0.5f,  0.5f, 0.0}}  // upper left corner
};


/////////////////////////////////////////////////////////////////
// Called when the view controller's view is loaded
// Perform initialization before the view is asked to draw
- (void)viewDidLoad
{
   [super viewDidLoad];

   // Verify the type of view created automatically by the
   // Interface Builder storyboard
   AGLKView *view = (AGLKView *)self.view;
   NSAssert([view isKindOfClass:[AGLKView class]],
      @"View controller's view is not a AGLKView");

   // Create an OpenGL ES 2.0 context and provide it to the
   // view
   //创建view的上下文
   view.context = [[EAGLContext alloc] 
      initWithAPI:kEAGLRenderingAPIOpenGLES2];

   // Make the new context current
   //设置当前的上下文
   [EAGLContext setCurrentContext:view.context];

   // Create a base effect that provides standard OpenGL ES 2.0
   // shading language programs and set constants to be used for 
   // all subsequent rendering
   //创建basseffect对象
   self.baseEffect = [[GLKBaseEffect alloc] init];
   self.baseEffect.useConstantColor = GL_TRUE;
   self.baseEffect.constantColor = GLKVector4Make(
      1.0f, // Red
      1.0f, // Green
      1.0f, // Blue
      1.0f);// Alpha

   // Set the background color stored in the current context 
   glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // background color

   // Generate, bind, and initialize contents of a buffer to be 
   // stored in GPU memory
   glGenBuffers(1,                // STEP 1
      &vertexBufferID);
   glBindBuffer(GL_ARRAY_BUFFER,  // STEP 2
      vertexBufferID); 
   glBufferData(                  // STEP 3
      GL_ARRAY_BUFFER,  // Initialize buffer contents
      sizeof(vertices), // Number of bytes to copy
      vertices,         // Address of bytes to copy
      GL_STATIC_DRAW);  // Hint: cache in GPU memory
}


/////////////////////////////////////////////////////////////////
// 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:(AGLKView *)view drawInRect:(CGRect)rect
{

    //准备绘制
   [self.baseEffect prepareToDraw];

   // Clear back frame buffer (erase previous drawing)
   glClear(GL_COLOR_BUFFER_BIT);

   // Enable use of positions from bound vertex buffer
   glEnableVertexAttribArray(      // STEP 4
      GLKVertexAttribPosition);

   glVertexAttribPointer(          // STEP 5
      GLKVertexAttribPosition, 
      3,                   // three components per vertex
      GL_FLOAT,            // data is floating point
      GL_FALSE,            // no fixed point scaling
      sizeof(SceneVertex), // no gaps in data
      NULL);               // NULL tells GPU to start at 
                           // beginning of bound buffer

   // Draw triangles using the first three vertices in the 
   // currently bound vertex buffer
   glDrawArrays(GL_TRIANGLES,      // STEP 6
      0,  // Start with first vertex in currently bound buffer
      3); // Use three vertices from currently bound buffer
}


/////////////////////////////////////////////////////////////////
// Called when the view controller's view has been unloaded
// Perform clean-up that is possible when you know the view 
// controller's view won't be asked to draw again soon.
- (void)viewDidUnload
{
   [super viewDidUnload];

   // Delete buffers that aren't needed when view is unloaded
   if (0 != vertexBufferID)
   {
      glDeleteBuffers (1,          // STEP 7 
                       &vertexBufferID);  
      vertexBufferID = 0;
   }

   // Stop using the context created in -viewDidLoad
   ((AGLKView *)self.view).context = nil;
   [EAGLContext setCurrentContext:nil];
}

@end

效果图,画出一个三角形

这里写图片描述

总结

  • CAEAGLLayer是Core Animation提供的标准层类之一,CAEAGLLayer会与一个OpenGL ES的帧缓存共享它的像素颜色仓库
  • 所有图形的ios应用都包含Core Animation层,所有的绘图都发生在层上。Core Animation合成器会混合当前应用与操作系统的层,从而会在OpenGL ES的后帧缓存中产生最终的像素颜色,之后Core Animation合成器切换前后缓存以便把合成的内存显示到屏幕上
  • GLKBaseEffect类中隐藏了ios所支持的OpenGL ES版本之间的很多的不同,当我们使用OpenGL ES 2.0的时候,GLKBaseEffect会生成直接在GPU当中运行的Shading Language程序

猜你喜欢

转载自blog.csdn.net/zcmuczx/article/details/80308248