OpenGL ES學(xué)習(xí)之路(2.3) 使用OpenGL ES 加載圖片

效果

image.png

GLKBaseEffect的屬性方法

GLboolean                           _colorMaterialEnabled; //一個(gè)布爾值,指示在計(jì)算燈光與材質(zhì)的交互時(shí)是否使用顏色頂點(diǎn)屬性。
GLboolean                           _fogEnabled; //煙霧是否開(kāi)啟

// Modelview, projection, texture and derived matrices for transformation
GLKEffectPropertyTransform          *_transform; // 模型視圖、投影矩陣、紋理等仿射變換

// Lights
GLKLightingType                     _lightingType; //燈光類(lèi)型
GLKEffectPropertyLight              *_light0, *_light1, *_light2; //三種燈光

// Material for lighting
GLKEffectPropertyMaterial           *_material; //燈光渲染的材質(zhì)

// GL Texture Names
GLKEffectPropertyTexture            *_texture2d0, *_texture2d1; //兩個(gè)紋理

// Texture ordering array
NSArray                             *_textureOrder; //紋理渲染順序

// Constant color (fixed color value to supplant the use of the "color" named vertex attrib array)
GLKVector4                          _constantColor; //填充的顏色

// Fog
GLKEffectPropertyFog                *_fog; // 煙霧效果

// Label for effect
NSString                            *_label;  //命名

// Sync all effect changes for consistent state when drawing
- (void) prepareToDraw;    //準(zhǔn)備繪制

// GL_FALSE 我們的材質(zhì)和顏色相互作用
@property (nonatomic, assign)         GLboolean                          colorMaterialEnabled;
// GL_FALSE 光照模型
@property (nonatomic, assign)         GLboolean                          lightModelTwoSided;
// GL_TRUE 不使用常量顏色
@property (nonatomic, assign)         GLboolean                          useConstantColor;
// Identity Matrices  模型視圖、投影矩陣、紋理等仿射變換
@property (nonatomic, readonly)       GLKEffectPropertyTransform         *transform;
// Disabled  三種燈光
@property (nonatomic, readonly)       GLKEffectPropertyLight             *light0, *light1, *light2;
// GLKLightingTypePerVertex  燈光類(lèi)型
@property (nonatomic, assign)         GLKLightingType                    lightingType;
// { 0.2, 0.2, 0.2, 1.0 } 燈光顏色
@property (nonatomic, assign)         GLKVector4                         lightModelAmbientColor;
// Default material state  燈光渲染的材質(zhì)
@property (nonatomic, readonly)       GLKEffectPropertyMaterial          *material;
// Disabled 兩個(gè)紋理
@property (nonatomic, readonly)       GLKEffectPropertyTexture           *texture2d0, *texture2d1;
// texture2d0, texture2d1 紋理渲染順序
@property (nullable, nonatomic, copy) NSArray<GLKEffectPropertyTexture*> *textureOrder;
// { 1.0, 1.0, 1.0, 1.0 } 填充的顏色
@property (nonatomic, assign)         GLKVector4                         constantColor;
// Disabled
@property (nonatomic, readonly)       GLKEffectPropertyFog               *fog;
//命名
@property (nullable, nonatomic, copy) NSString                           *label;

使用OpenGL ES 加載圖片 案例


#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
@interface ViewController : GLKViewController


@end



viewController.m

#import "ViewController.h"
#import <OpenGLES/ES3/gl.h>
#import <OpenGLES/ES3/glext.h>

@interface ViewController ()<GLKViewDelegate>
{
    EAGLContext *context; //EAGLContent是蘋(píng)果在ios平臺(tái)下實(shí)現(xiàn)的opengles渲染層,用于渲染結(jié)果在目標(biāo)surface上的更新。
    GLKBaseEffect *mEffect;//著色器或者光照
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    //1.設(shè)置OpenGL ES 配置
    [self setUpConfig];
    
    //2.加載頂點(diǎn)數(shù)據(jù)
    [self uploadVertexArray];
    
    //3.加載紋理
    [self uploadTexture];
}

//3.加載紋理
- (void)uploadTexture {
    
    //第一步,獲取紋理圖片路徑
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"cTest" ofType:@"jpg"];
    
    //GLKTextureLoaderOriginBottomLeft ,紋理坐標(biāo)是相反的,所以調(diào)整為左下為[0,0]
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, NULL];
    
    GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:NULL];
    
    //著色器
    mEffect = [[GLKBaseEffect alloc] init];
    //第一個(gè)紋理屬性
    mEffect.texture2d0.enabled = GL_TRUE;
    //紋理的名字
    mEffect.texture2d0.name = textureInfo.name;
}


//2.加載頂點(diǎn)數(shù)據(jù)
- (void)uploadVertexArray {
    
    //第一步:設(shè)置頂點(diǎn)數(shù)組
    //OpenGLES的世界坐標(biāo)系是[-1,1],故而點(diǎn)(0,0)是在屏幕的正中間。
    //頂點(diǎn)數(shù)據(jù),前3個(gè)是頂點(diǎn)坐標(biāo)x,y,z后面2個(gè)是紋理坐標(biāo)
    //紋理坐標(biāo)的取值范圍是[0,1],原點(diǎn)是在左下角。故而點(diǎn)(0,0)在左下角,點(diǎn)(1,1)在右上角
    //2個(gè)三角形構(gòu)成
    GLfloat vertextData[] = {
       //頂點(diǎn)坐標(biāo)。         紋理坐標(biāo)
        0.5, -0.5, 0.0f,  1.0f, 0.0f, //右下
        0.5, 0.5, -0.0f,  1.0f, 1.0f, //右上
        -0.5, 0.5, 0.0f,  0.0f, 1.0f, //
        
        0.5, -0.5, 0.0f,  1.0f, 0.0f, //右下
        -0.5, 0.5, 0.0f,  0.0f, 1.0f, //左上
        -0.5, -0.5, 0.0f, 0.0f, 0.0f, //左下
        
    };
    
    //開(kāi)啟頂點(diǎn)緩沖區(qū)
    //頂點(diǎn)緩存區(qū)
    GLuint buffer;
    //申請(qǐng)一個(gè)緩存區(qū)標(biāo)識(shí)符
    glGenBuffers(1, &buffer);
    //glBindBuffer 把標(biāo)識(shí)符綁定到GL_ARRAY_BUFFER 上
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    //glBufferData 把頂點(diǎn)數(shù)據(jù)從cpu內(nèi)容復(fù)制到gpu內(nèi)存
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertextData), vertextData, GL_STATIC_DRAW);
    
    //第三步:設(shè)置合適的格式從buffer里面讀取數(shù)據(jù)
    /*
     默認(rèn)情況下,出于性能考慮,所有頂點(diǎn)著色器的屬性(Attribute)變量都是關(guān)閉的,意味著數(shù)據(jù)在著色器端是不可見(jiàn)的,哪怕數(shù)據(jù)已經(jīng)上傳到GPU,需要由glEnableVertexAttribArray啟用指定屬性,才可在頂點(diǎn)著色器中訪問(wèn)逐頂點(diǎn)的屬性數(shù)據(jù)。glVertexAttribPointer或VBO只是建立CPU和GPU之間的邏輯連接,從而實(shí)現(xiàn)了CPU數(shù)據(jù)上傳至GPU。但是,數(shù)據(jù)在GPU端是否可見(jiàn),即,著色器能否讀取到數(shù)據(jù),由是否啟用了對(duì)應(yīng)的屬性決定,這就是glEnableVertexAttribArray的功能,允許頂點(diǎn)著色器讀取GPU(服務(wù)器端)數(shù)據(jù)。
     */
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    
    //glVertexAttribPointer 使用來(lái)上傳頂點(diǎn)數(shù)據(jù)到GPU的方法(設(shè)置合適的格式從buffer里面讀取數(shù)據(jù))
    // index: 指定要修改的頂點(diǎn)屬性的索引值
    // size : 指定每個(gè)頂點(diǎn)屬性的組件數(shù)量。必須為1、2、3或者4。初始值為4。(如position是由3個(gè)(x,y,z)組成,而顏色是4個(gè)(r,g,b,a))離下一個(gè)頂點(diǎn)的步長(zhǎng)有多遠(yuǎn)
    // type : 指定數(shù)組中每個(gè)組件的數(shù)據(jù)類(lèi)型。可用的符號(hào)常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值為GL_FLOAT。
    // normalized : 指定當(dāng)被訪問(wèn)時(shí),固定點(diǎn)數(shù)據(jù)值是否應(yīng)該被歸一化(GL_TRUE)或者直接轉(zhuǎn)換為固定點(diǎn)值(GL_FALSE)
    // stride : 指定連續(xù)頂點(diǎn)屬性之間的偏移量。如果為0,那么頂點(diǎn)屬性會(huì)被理解為:它們是緊密排列在一起的。初始值為0
    // ptr    : 指定一個(gè)指針,指向數(shù)組中第一個(gè)頂點(diǎn)屬性的第一個(gè)組件。初始值為0 這個(gè)值受到VBO的影響,從什么位置開(kāi)始,第0位
    
    /*
     VBO,頂點(diǎn)緩存對(duì)象
     在不使用VBO的情況下:事情是這樣的,ptr就是一個(gè)指針,指向的是需要上傳到頂點(diǎn)數(shù)據(jù)指針。通常是數(shù)組名的偏移量。
     
     在使用VBO的情況下:首先要glBindBuffer,以后ptr指向的就不是具體的數(shù)據(jù)了。因?yàn)閿?shù)據(jù)已經(jīng)緩存在緩沖區(qū)了。這里的ptr指向的是緩沖區(qū)數(shù)據(jù)的偏移量。這里的偏移量是整型,但是需要強(qiáng)制轉(zhuǎn)換為const GLvoid *類(lèi)型傳入。注意的是,這里的偏移的意思是數(shù)據(jù)個(gè)數(shù)總寬度數(shù)值。
     
     比如說(shuō):這里存放的數(shù)據(jù)前面有3個(gè)float類(lèi)型數(shù)據(jù),那么這里的偏移就是,3*sizeof(float).
     
     最后解釋一下,glVertexAttribPointer的工作原理:
     首先,通過(guò)index得到著色器對(duì)應(yīng)的變量openGL會(huì)把數(shù)據(jù)復(fù)制給著色器的變量。
     以后,通過(guò)size和type知道當(dāng)前數(shù)據(jù)什么類(lèi)型,有幾個(gè)。openGL會(huì)映射到float,vec2, vec3 等等。
     由于每次上傳的頂點(diǎn)數(shù)據(jù)不止一個(gè),可能是一次4,5,6頂點(diǎn)數(shù)據(jù)。那么通過(guò)stride就是在數(shù)組中間隔多少byte字節(jié)拿到下個(gè)頂點(diǎn)此類(lèi)型數(shù)據(jù)。
     最后,通過(guò)ptr的指針在迭代中獲得所有數(shù)據(jù)。
     那么,最最后openGL如何知道ptr指向的數(shù)組有多長(zhǎng),讀取幾次呢。是的,openGL不知道。所以在調(diào)用繪制的時(shí)候,需要傳入一個(gè)count數(shù)值,就是告訴openGL繪制的時(shí)候迭代幾次glVertexAttribPointer調(diào)用。
     */
     //(GLfloat *)NULL + 0 指針,指向數(shù)組首地址
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0);
    
    //設(shè)置紋理
    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    
    //(GLfloat *)NULL + 3,指向到紋理數(shù)據(jù),相當(dāng)于表示在數(shù)組當(dāng)中,從第3位開(kāi)始,參照數(shù)組來(lái)理解
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);
  
}

//1.設(shè)置OpenGL ES 配置
- (void)setUpConfig {
    
    //context
    /*
     
     //EAGLContent是蘋(píng)果在ios平臺(tái)下實(shí)現(xiàn)的opengles渲染層,用于渲染結(jié)果在目標(biāo)surface上的更新
     kEAGLRenderingAPIOpenGLES1 ->OpenGL ES 1.0,固定管線
     kEAGLRenderingAPIOpenGLES2 ->OpenGL ES 2.0
     kEAGLRenderingAPIOpenGLES3 ->OpenGL ES 3.0
     
     */
    //新建OpenGL ES 上下文
    context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
    
    if (!context) {
        NSLog(@"Failed to create ES context");
        return;
    }
    
    //創(chuàng)建一個(gè)OpenGL ES上下文并將其分配給從storyboard加載的視圖
    //注意:這里需要把stroyBoard記得添加為GLKView
    GLKView *view = (GLKView *)self.view;
    view.context = context;
    
    //配置視圖創(chuàng)建的渲染緩沖區(qū)
    /*
     OpenGL ES 有一個(gè)緩存區(qū),它用以存儲(chǔ)將在屏幕中顯示的顏色。你可以使用其屬性來(lái)設(shè)置緩沖區(qū)中的每個(gè)
     像素的顏色格式。
     默認(rèn):GLKViewDrawableColorFormatRGBA8888,即緩存區(qū)的每個(gè)像素的最小組成部分(RGBA)使用
     8個(gè)bit,(所以每個(gè)像素4個(gè)字節(jié),4*8個(gè)bit)。
     GLKViewDrawableColorFormatRGB565,如果你的APP允許更小范圍的顏色,即可設(shè)置這個(gè)。會(huì)讓你的
     APP消耗更小的資源(內(nèi)存和處理時(shí)間)
     */
    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    
    /*
     OpenGL ES 另一個(gè)緩存區(qū),深度緩沖區(qū)。幫助我們確保可以更接近觀察者的對(duì)象顯示在遠(yuǎn)一些的對(duì)象前面。
     (離觀察者近一些的對(duì)象會(huì)擋住在它后面的對(duì)象)
     默認(rèn):OpenGL把接近觀察者的對(duì)象的所有像素存儲(chǔ)到深度緩沖區(qū),當(dāng)開(kāi)始繪制一個(gè)像素時(shí),它(OpenGL)
     首先檢查深度緩沖區(qū),看是否已經(jīng)繪制了更接近觀察者的什么東西,如果是則忽略它(要繪制的像素,
     就是說(shuō),在繪制一個(gè)像素之前,看看前面有沒(méi)有擋著它的東西,如果有那就不用繪制了)。否則,
     把它增加到深度緩沖區(qū)和顏色緩沖區(qū)。
     缺省值是GLKViewDrawableDepthFormatNone,意味著完全沒(méi)有深度緩沖區(qū)。
     但是如果你要使用這個(gè)屬性(一般用于3D游戲),你應(yīng)該選擇GLKViewDrawableDepthFormat16
     或GLKViewDrawableDepthFormat24。這里的差別是使用GLKViewDrawableDepthFormat16
     將消耗更少的資源,但是當(dāng)對(duì)象非常接近彼此時(shí),你可能存在渲染問(wèn)題()
     */
    //深度緩存區(qū)
    view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    
    /*
     你的OpenGL上下文的另一個(gè)可選的緩沖區(qū)是stencil(模板)緩沖區(qū)。它幫助你把繪制區(qū)
     域限定到屏幕的一個(gè)特定部分。它還用于像影子一類(lèi)的事物=比如你可以使用stencil緩沖
     區(qū)確保影子投射到地板。缺省值是GLKViewDrawableStencilFormatNone,
     意思是沒(méi)有stencil緩沖區(qū),但是你可以通過(guò)設(shè)置其值為GLKViewDrawableStencilFormat8
     (唯一的其他選項(xiàng))使能它
     */
    // view.drawableStencilFormat = GLKViewDrawableStencilFormat8;
    
    //啟用多重采樣
    /*
     這是你可以設(shè)置的最后一個(gè)可選緩沖區(qū),對(duì)應(yīng)的GLKView屬性是multisampling。
     如果你曾經(jīng)嘗試過(guò)使用OpenGL畫(huà)線并關(guān)注過(guò)"鋸齒壯線",multisampling就可以幫助你處理
     以前對(duì)于每個(gè)像素,都會(huì)調(diào)用一次fragment shader(片段著色器),
     drawableMultisample基本上替代了這個(gè)工作,它將一個(gè)像素分成更小的單元,
     并在更細(xì)微的層面上多次調(diào)用fragment shader。之后它將返回的顏色合并,
     生成更光滑的幾何邊緣效果。
     要小心此操作,因?yàn)樗枰加媚愕腶pp的更多的處理時(shí)間和內(nèi)存。
     缺省值是GLKViewDrawableMultisampleNone,但是你可以通過(guò)設(shè)置其值GLKViewDrawableMultisample4X為來(lái)使能它
     */
    //view.drawableMultisample = GLKViewDrawableMultisample4X;
    
    [EAGLContext setCurrentContext:context];
    //開(kāi)啟深度測(cè)試,就是讓離你近的物體可以遮擋離你遠(yuǎn)的物體。
    glEnable(GL_DEPTH_TEST);
    //設(shè)置surface的清除顏色,也就是渲染到屏幕上的背景色。
    glClearColor(0.1, 0.2, 0.3, 1);
}

-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    //清除surface內(nèi)容,恢復(fù)至初始狀態(tài)
    glClear(GL_DEPTH_BUFFER_BIT|GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
   
    glClearColor(0.3f, 0.6f, 1.0f, 1.0f);
    
    //啟動(dòng)著色器,準(zhǔn)備繪制
    [mEffect prepareToDraw];
    //繪制
    /**
     參數(shù)一:繪制模式:三角形
     參數(shù)二:從第幾位開(kāi)始繪制,0位
     參數(shù)三:頂點(diǎn)數(shù)
     */
    glDrawArrays(GL_TRIANGLES, 0, 6);
    
}

@end


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容