iOS全景圖片之OpenGL學(xué)習(xí)筆記

OpenGL版本

在開發(fā)OpenGL項(xiàng)目前,需要根據(jù)業(yè)務(wù)需求選擇合適的版本。在初始化EAGLContext時(shí)指定ES版本號。
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];

EAGLContext

與UIKit中CGContextRef相似,EAGLContext相當(dāng)于OpenGL繪制句柄或者上下文,在繪制試圖之前,需要指定使用創(chuàng)建的上下文繪制
[EAGLContextsetCurrentContext:view.context];

創(chuàng)建和配置GLKit視圖

可以以編程方式或使用Interface Builder創(chuàng)建和配置GLKView對象。在使用它繪制之前,必須將其與EAGLContext對象相關(guān)聯(lián)。

  • 以編程方式創(chuàng)建視圖時(shí),首先創(chuàng)建上下文,然后將其傳遞給視圖的initWithFrame:context:方法。
  • 從故事板加載視圖后,創(chuàng)建上下文并將其設(shè)置為視圖的上下文屬性的值。

GLKit視圖會自動(dòng)創(chuàng)建和配置自己的OpenGL ES framebuffer對象和renderbuffers??梢允褂靡晥D的可繪制屬性來控制這些對象的屬性。如果更改GLKit視圖的大小,比例因子或可繪制屬性,則會在下次繪制內(nèi)容時(shí)自動(dòng)刪除并重新創(chuàng)建相應(yīng)的framebuffer對象和renderbuffers。

繪制GLKit視圖

GLKView_diagram_2x.png

如圖所示,概述了繪制OpenGL ES內(nèi)容的三個(gè)步驟:準(zhǔn)備OpenGL ES基礎(chǔ)設(shè)施,發(fā)布繪圖命令,并將呈現(xiàn)的內(nèi)容呈現(xiàn)給Core Animation進(jìn)行顯示。 GLKView類實(shí)現(xiàn)了第一和第三步。對于第二步,您將實(shí)現(xiàn)一個(gè)繪圖方法,如下代碼中的示例所示。

- (void)drawRect:(CGRect)rect
{
    // Clear the framebuffer
    glClearColor(0.0f, 0.0f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Draw using previously configured texture, shader, uniforms, and vertex array
    glBindTexture(GL_TEXTURE_2D, _planetTexture);
    glUseProgram(_diffuseShading);
    glUniformMatrix4fv(_uniformModelViewProjectionMatrix, 1, 0, _modelViewProjectionMatrix.m);
    glBindVertexArrayOES(_planetMesh);
    glDrawElements(GL_TRIANGLE_STRIP, 256, GL_UNSIGNED_SHORT);
}

注意:glClear函數(shù)提示OpenGL ES可以丟棄任何現(xiàn)有的幀緩沖區(qū)內(nèi)容,避免了昂貴的內(nèi)存操作將以前的內(nèi)容加載到內(nèi)存中。為了確保最佳性能,您應(yīng)該在繪制之前始終調(diào)用此函數(shù)。

定義要繪制圖片的頂點(diǎn)坐標(biāo)和紋理坐標(biāo)

OpenGL ES中坐標(biāo)系是和iOS常用的Quartz 2D坐標(biāo)系是不一樣的,Quartz 2D坐標(biāo)系屬于右手坐標(biāo)系,OpenGL ES屬于左手坐標(biāo)系。先說一下左手坐標(biāo)系和右手坐標(biāo)系。
伸出左手,讓拇指和食指成“L”形狀,拇指向右,食指向上,中指指向起那方,這時(shí)就建立了一個(gè)“左手坐標(biāo)系”,拇指、食指和中指分表代表x、y、z軸的正方向?!坝沂肿鴺?biāo)系”就是用右手,如下圖所示:

“左手坐標(biāo)系”和“右手坐標(biāo)系”.png

在iOS開發(fā)中,屏幕左上角是坐標(biāo)原點(diǎn),往右是x軸正方向,往下是y軸正方向。而在OpenGL ES中,屏幕的中點(diǎn)是坐標(biāo)原點(diǎn),往右是x軸正方向,往下是y軸正方向,其中z軸的正方向是從屏幕往外的方向,如下圖所示:

OpenGL ES坐標(biāo)系.png

根據(jù)OpenGL ES的坐標(biāo)系,我們定義一下要繪制的圖片的幾個(gè)頂點(diǎn),頂點(diǎn)坐標(biāo)和紋理坐標(biāo)是放在一個(gè)GLfloat數(shù)組中管理的,定義一組頂點(diǎn)數(shù)據(jù)的跨度為5,其中前三個(gè)存儲頂點(diǎn)坐標(biāo),后兩個(gè)存儲紋理坐標(biāo),下圖一共定義了4個(gè)頂點(diǎn),就是矩形的四個(gè)頂點(diǎn),需要注意的是,雖然坐標(biāo)都是0.5,但是繪制出來的圖形并不是正方形,因?yàn)槲覀冇脕碜罱K顯示的是iPhone屏幕,手機(jī)的長和寬并不相等。

頂點(diǎn)坐標(biāo)和紋理坐標(biāo).png

OpenGL ES不能繪制多邊形,只能繪制點(diǎn),線,三角形,OpenGL可以繪制多邊形,由于我們繪制的圖片是一個(gè)矩形,又兩個(gè)三角形構(gòu)成,就是下圖中的兩個(gè)頂點(diǎn)索引(0,1,3)和(1,2,3)組成的三角形拼成一個(gè)矩形。

頂點(diǎn)索引.png
- (void)setupOpenGL {
    
    [EAGLContext setCurrentContext:_context];
    glEnable(GL_DEPTH_TEST);
    // 頂點(diǎn)
    GLfloat *vVertices  = NULL;
    // 紋理
    GLfloat *vTextCoord = NULL;
    // 索引
    GLushort *indices   = NULL;
    int numVertices     = 0;
    _numIndices         = esGenSphere(200, 1.0, &vVertices, &vTextCoord, &indices, &numVertices);
    // 加載頂點(diǎn)索引數(shù)據(jù)
    // 創(chuàng)建索引buffer并將indices的數(shù)據(jù)放入
    glGenBuffers(1, &_vertexIndicesBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vertexIndicesBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, _numIndices*sizeof(GLushort), indices, GL_STATIC_DRAW);
    // 加載頂點(diǎn)坐標(biāo)
    // 創(chuàng)建頂點(diǎn)buffer并將vVertices中的數(shù)據(jù)放入
    glGenBuffers(1, &_vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, numVertices*3*sizeof(GLfloat), vVertices, GL_STATIC_DRAW);
    //設(shè)置頂點(diǎn)屬性,對頂點(diǎn)的位置,顏色,坐標(biāo)進(jìn)行賦值
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*3, NULL);
 
    // 創(chuàng)建紋理buffer并將vTextCoord數(shù)據(jù)放入
    glGenBuffers(1, &_vertexTexCoord);
    glBindBuffer(GL_ARRAY_BUFFER, _vertexTexCoord);
    glBufferData(GL_ARRAY_BUFFER, numVertices*2*sizeof(GLfloat), vTextCoord, GL_DYNAMIC_DRAW);
    
    //設(shè)置紋理屬性,對紋理的位置,顏色,坐標(biāo)進(jìn)行賦值
    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*2, NULL);

    // 將圖片轉(zhuǎn)換成為紋理信息
    NSString *imagePath = [[NSBundle mainBundle] pathForResource:self.imageName ofType:self.imageNameType];
    
    // 由于OpenGL的默認(rèn)坐標(biāo)系設(shè)置在左下角, 而GLKit在左上角, 因此需要轉(zhuǎn)換
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],
                             GLKTextureLoaderOriginBottomLeft,
                             nil];
    
    _textureInfo = [GLKTextureLoader textureWithContentsOfFile:imagePath options:options error:nil];
    
    // 設(shè)置著色器的紋理
    _effect                    = [[GLKBaseEffect alloc] init];
    _effect.texture2d0.enabled = GL_TRUE;
    _effect.texture2d0.name    = _textureInfo.name;
}

//啟用頂點(diǎn)位置(坐標(biāo))數(shù)組,之前說過opengl是狀態(tài)機(jī),需要什么狀態(tài)就啟動(dòng)什么狀態(tài)
glEnableVertexAttribArray(GLKVertexAttribPosition);

 GLfloat vertexs[] = {
        -0.5, -0.5, 0,     0.0, 0.0,   //左下
        -0.5,  0.5, 0,     0.0, 1.0,   //左上
         0.5,  0.5, 0,     1.0, 1.0,   //右上
         0.5, -0.5, 0,     1.0, 0.0,   //右下
    };
啟用通用頂點(diǎn)屬性
 /*
  index:指定通用頂點(diǎn)數(shù)據(jù)的索引,這個(gè)值的范圍從0到支持的最大頂點(diǎn)屬性數(shù)量減1
  功能:用于啟用通用頂點(diǎn)屬性
*/
void glEnableVertexAttribArray(GLuint index);
禁止通用頂點(diǎn)屬性
 /*
  index:指定通用頂點(diǎn)數(shù)據(jù)的索引,這個(gè)值的范圍從0到支持的最大頂點(diǎn)屬性數(shù)量減1
*/
void glDisableVertexAttribArray(GLuint index);
頂點(diǎn)數(shù)組設(shè)置值
index: 通用頂點(diǎn)屬性索引
size: 頂點(diǎn)數(shù)組中為頂點(diǎn)屬性指定的分量數(shù)量,取值范圍1~4
type: 數(shù)據(jù)格式 ,兩個(gè)函數(shù)都包括的有效值是
      GL_BYTE  GL_UNSIGNED_BYTE  GL_SHORT  GL_UNSIGNED_SHORT  GL_INT  GL_UNSIGNED_INT
      glVertexAttribPointer還包括的值為:GL_HALF_FLOAT GL_FLOAT 等
normalized: 僅glVertexAttribPointer使用,表示非浮點(diǎn)數(shù)據(jù)類型轉(zhuǎn)換成浮點(diǎn)值時(shí)是否應(yīng)該規(guī)范化
stride: 每個(gè)頂點(diǎn)由size指定的頂點(diǎn)屬性分量順序存儲。stride指定頂點(diǎn)索引i和i+1表示的頂點(diǎn)之間的偏移。
    如果為0,表示順序存儲。如果不為0,在取下一個(gè)頂點(diǎn)的同類數(shù)據(jù)時(shí),需要加上偏移。
ptr: 如果使用“頂點(diǎn)緩沖區(qū)對象”,表示的是該緩沖區(qū)內(nèi)的偏移量。

void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *ptr);
// 取值為“整數(shù)”版本
void glVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *ptr);

這里由于我們使用iOS封裝好的GLkit框架,不需要我們設(shè)置著色器程序,里面有內(nèi)置的設(shè)置好的index位置,就是下面的變量:

typedef NS_ENUM(GLint, GLKVertexAttrib)
{
    GLKVertexAttribPosition,
    GLKVertexAttribNormal,
    GLKVertexAttribColor,
    GLKVertexAttribTexCoord0,
    GLKVertexAttribTexCoord1
} NS_ENUM_AVAILABLE(10_8, 5_0);

GLKit里面的GLKVertexAttribPosition和GLKVertexAttribTexCoord0分別表示頂點(diǎn)坐標(biāo)和紋理坐標(biāo)兩個(gè)變量的屬性索引。(頂點(diǎn)索引不是頂點(diǎn)數(shù)據(jù),這里我們不需要管這個(gè)值)

啟用頂點(diǎn)數(shù)組和指定頂點(diǎn)屬性.png

上面的程序最需要注意的是變量的偏移很重要,如果把GLfloat寫成CGFloat,會導(dǎo)致圖片渲染不出來。

參考文獻(xiàn):
http://www.itdecent.cn/p/23e938fab9ca
http://www.itdecent.cn/p/954339d57541

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容