這次我將用OpenGL ES來顯示一張圖片,可能我的學(xué)習(xí)筆記跳躍性很大,對于剛開始學(xué)習(xí)OpenGL ES人來說有弄不懂的地方也是正常的,我也是看了大量的資料才學(xué)會的?,F(xiàn)在網(wǎng)上有大量的資料可以學(xué)習(xí)但有的代碼會用蘋果的GLKit開發(fā)有的代碼不會,所以會造成本來看懂的代碼到下一個(gè)博客又不明白了。我會在筆記中用兩種方式去實(shí)現(xiàn)以有對比性。建議能用蘋果的GLKit最好,因?yàn)榇a減少很多。下面上代碼。
學(xué)習(xí)的代碼都在我的github倉庫歡迎大家學(xué)習(xí)指教!
不用GLKit的方法
為了減少重復(fù)說相同的話這次對于上一個(gè)筆記寫的東西這里將不再介紹而是直接用,這里只說與上一個(gè)筆記增加的內(nèi)容。首先在原項(xiàng)目里添加一張圖。接下來是修改頂點(diǎn)數(shù)據(jù),在上一個(gè)筆記中我們創(chuàng)建了一個(gè)三角形,而顯示圖片需要一個(gè)矩形,其實(shí)我們可以有通過兩個(gè)直角三角形來拼接成一個(gè)正方形。(如果你學(xué)到最后你會發(fā)現(xiàn)在OpenGL的世界里那些復(fù)雜的圖形或三維物體都是由成千上萬個(gè)三角形拼接而成的)。頂點(diǎn)數(shù)據(jù)如下:
GLfloat vertex[] = {
// 第一個(gè)三角形
-0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
// 第二個(gè)三角形
-0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
};
有了圖形的頂點(diǎn)顯示圖片還是不行的,還要告訴程序圖片是怎么放的。也就是紋理頂點(diǎn)數(shù)據(jù),要注意這個(gè)坐標(biāo)和iOS平常用的坐標(biāo)是不一樣的,它的原點(diǎn)在左下角,從左向右和從下到上的取值范圍是0~1。但這里還有一個(gè)問題,就是你最后渲染出來的圖片是上下顛倒的,這是因?yàn)榧y理頂點(diǎn)的原點(diǎn)在左下角但圖片紋理的原點(diǎn)在右上角,坐標(biāo)系統(tǒng)不一樣就造成上下顛倒了。有兩種解決方案:方案一、將紋理頂點(diǎn)數(shù)據(jù)的y坐標(biāo)反著寫也就是將原來的y變成1-y。方案二、就是把圖片數(shù)據(jù)上下顛倒過來。這里我用的是方案二,因?yàn)樵趯戇@篇博客的時(shí)候我的代碼已經(jīng)過了很長時(shí)間了不想再改代碼了,其實(shí)方案一代碼更好一點(diǎn),還是等大家自己嘗試去改吧。紋理頂點(diǎn)數(shù)據(jù)如下:
GLfloat textureVertex[] = {
// 第一個(gè)三角形 紋理坐標(biāo)
0.0f, 1.0f, // 左上
0.0f, 0.0f, // 左下
1.0f, 0.0f, // 右下
// 第二個(gè)三角形
0.0f, 1.0f, // 左上
1.0f, 0.0f, // 右下
1.0f, 1.0f // 右上
};
接下來我們要修改著色器,原來我們傳的是固定的顏色,這次要根據(jù)紋理來生成相應(yīng)的顏色。將頂點(diǎn)著色器的原碼修改成如下:
attribute vec4 myPosition; // 頂點(diǎn)位置
// 輸入的色彩
attribute vec2 textureCoord;
// 輸出的色彩
varying vec2 outTextTureCoord;
void main()
{
gl_Position = myPosition; // 傳遞頂點(diǎn)位置數(shù)據(jù)
outTextTureCoord = textureCoord; // 傳遞色彩數(shù)據(jù)
}
將片元著色器的原碼改成下如:
precision mediump float; // 聲明精度
// 聲明色彩變量
varying vec2 outTextTureCoord;
// 傳遞圖片數(shù)據(jù)
uniform sampler2D myTexture;
void main()
{
vec4 color = texture2D(myTexture, outTextTureCoord);
// 通過紋理坐標(biāo)數(shù)據(jù)來獲取對應(yīng)坐標(biāo)色值并傳遞
gl_FragColor = vec4(color.rgb, 1.0);
}
代碼里的注釋已經(jīng)寫的很明白了,這里就不多說了。
接下來我們開始添加一個(gè)屬性用于傳遞紋理數(shù)據(jù)。
GLuint _myTextTureCoordSlot; // 紋理坐標(biāo)對應(yīng)的槽
然后在原來鏈接著色器的代碼下面添加一個(gè)獲取槽的代碼:
// 獲取紋理坐標(biāo)的槽
_myTextTureCoordSlot = glGetAttribLocation(_myPrograme, "textureCoord");
前面的準(zhǔn)差不多了,該獲取圖片傳遞紋理信息了,在渲染前寫以下代碼:
- (void)setupTexture {
// 能實(shí)現(xiàn)圖片翻轉(zhuǎn)
CGImageRef cgImageRef = [UIImage imageNamed:@"testImage"].CGImage;
GLuint width = (GLuint)CGImageGetWidth(cgImageRef);
GLuint height = (GLuint)CGImageGetHeight(cgImageRef);
CGRect rect = CGRectMake(0, 0, width, height);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
void *data = malloc(width * height * 4);
CGContextRef context = CGBitmapContextCreate(data, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGContextTranslateCTM(context, 0, height);
CGContextScaleCTM(context, 1.0f, -1.0f);
CGColorSpaceRelease(colorSpace);
CGContextClearRect(context, rect);
CGContextDrawImage(context, rect, cgImageRef);
glEnable(GL_TEXTURE_2D); // 開啟2D紋理
// 申請紋理id
glGenTextures(1, &_myTextTure);
// 綁定紋理id
glBindTexture(GL_TEXTURE_2D, _myTextTure);
// 設(shè)置圖像拉伸變形時(shí)的處理方法
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 將圖片數(shù)據(jù)傳遞給GL_TEXTURE_2D,因?yàn)樯厦嬉呀壎y理對象所以會把數(shù)據(jù)傳遞給_myTextTure
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
CGContextRelease(context);
free(data);
}
好了可以渲染了,因?yàn)槲覀冞@次用了6個(gè)頂點(diǎn)所以在Draw方法里要把頂點(diǎn)數(shù)改成6.
glDrawArrays(GL_TRIANGLES, 0, 6);
運(yùn)行一下代碼看看效果怎么樣。

用GLKit實(shí)現(xiàn)的方式
用GLKit會省去很多代碼,這里不用你自定義著色器,也不用想紋理上下顛倒的問題,GLKit都給你解決了,該講的上面已經(jīng)講這了,這里就直接把全部代碼放這了,關(guān)鍵地方有注釋。
#import "ViewController.h"
#import <OpenGLES/ES2/gl.h>
#import <OpenGLES/ES2/glext.h>
typedef struct{
GLKVector3 position;
} Vertex;
typedef struct{
GLKVector2 position;
} TextureCoord;
Vertex vertex[] = {
{-0.5f, 0.5f, 0.0f}, // 左上
{-0.5f, -0.5f, 0.0f}, // 左下
{0.5f, -0.5f, 0.0f}, // 右下
{-0.5f, 0.5f, 0.0f}, // 左上
{0.5f, -0.5f, 0.0f}, // 右下
{0.5f, 0.5f, 0.0f}, // 右上
};
TextureCoord textrueCoord[] = {
{0.0f, 1.0f}, // 左上
{0.0f, 0.0f}, // 左下
{1.0f, 0.0f}, // 右下
{0.0f, 1.0f}, // 左上
{1.0f, 0.0f}, // 右下
{1.0f, 1.0f}, // 右上
};
@interface ViewController ()
@property (nonatomic,strong)GLKBaseEffect * effect;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
GLKView * glView = (GLKView *)self.view;
EAGLContext * contex = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!contex) {
NSLog(@"context創(chuàng)建失敗");
}
if (![EAGLContext setCurrentContext:contex]) {
NSLog(@"設(shè)置當(dāng)前context失敗");
}
glView.context = contex;
glView.drawableDepthFormat = GLKViewDrawableDepthFormat16;
self.effect = [[GLKBaseEffect alloc] init];
// 加載紋理圖片
NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:@(1), GLKTextureLoaderOriginBottomLeft, nil];//GLKTextureLoaderOriginBottomLeft 紋理坐標(biāo)系是相反的
NSError * error;
CGImageRef image = [UIImage imageNamed:@"myImage"].CGImage;
GLKTextureInfo * textureInfo = [GLKTextureLoader textureWithCGImage:image options:options error:&error];
// 設(shè)置紋理可用
self.effect.texture2d0.enabled = GL_TRUE;
// 傳遞紋理信息
self.effect.texture2d0.name = textureInfo.name;
// 設(shè)置頂點(diǎn)
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, vertex);
// 設(shè)置紋理坐標(biāo)
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, textrueCoord);
glClearColor(1.0, 1.0, 1.0, 1.0);
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
glClear(GL_COLOR_BUFFER_BIT);
[self.effect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, 6);
}
代碼是不是很少,這次寫博客與上一次有一段時(shí)間間隔,可能上面會有知識點(diǎn)忘記講了,如果有不清的地放可以在下面問,我給心力說明白。