天天看點

OpenGL ES 加載一張本地圖檔

一:OpenGL ES是什麼

OpenGL ES是以手持和嵌入式為目标的進階3D圖形應用程式API,是目前智能手機主流圖形API,是OpenGL的簡化版本,我們IOS的底層圖形渲染就是使用了OpenGL ES和Metal

二:我們使用OpenGL ES可以實作哪些功能

我們可以使用OpenGL ES來自定義着色器實作濾鏡效果(比如市面上常見相機的圖檔濾鏡,抖音的視訊動态濾鏡),地圖的渲染等

三:我們如何使用OpenGL ES來加載一張本地圖檔

3.1 導入庫 #import <OpenGLES/ES2/gl.h>

3.2 建立上下文,并設定為目前上下文

self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];

[EAGLContext setCurrentContext:**self**.context];

3.3 建立顯示的CAEAGLLayer

CAEAGLLayer *layer = [[CAEAGLLayer alloc] init];

self.myLayer = layer;

layer.frame = CGRectMake(0, (self.view.frame.size.height - self.view.frame.size.width) / 2, self.view.frame.size.width, self.view.frame.size.width);

[self.view.layer addSublayer:layer];

3.4 建立緩存區RenderBuffer(渲染緩存)和FrameBuffer(幀緩存)

GLuint renderBuffer;

GLuint frameBuffer;

glGenRenderbuffers(1, &renderBuffer);

glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);

[self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myLayer];

glGenFramebuffers(1, &frameBuffer);

glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);

glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBuffer);

3.5 設定視口,視口需要依賴緩存區的建立,是以放在建立緩存區後面

【更多音視訊學習資料,點選下方連結免費領取↓↓,先碼住不迷路~】

點選領取→音視訊開發基礎知識和資料包

GLint backingWidth;

glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);

GLint backingHeight;

glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);

glViewport(0, 0, backingWidth, backingHeight);

3.6 擷取紋理圖檔

self.textureID = [self createTextureWithImage:@"tupian"];

- (GLuint)createTextureWithImage:(NSString *)imageName

{

UIImage *image = [UIImage imageNamed:imageName];

CGImageRef cgImageRef = [image CGImage];

**if** (!cgImageRef) {

NSLog(@"Failed to load image");

}

//讀取圖檔的大小寬高

GLuint width = (GLuint)CGImageGetWidth(cgImageRef);

GLuint height = (GLuint)CGImageGetHeight(cgImageRef);

//擷取圖檔的rect

CGRect rect = CGRectMake(0, 0, width, height);

//擷取圖檔的顔色空間

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

//3.擷取圖檔位元組數 寬*高*4(RGBA)

**void** *imageData = malloc(width * height * 4);

//4.建立上下文

/*

參數1:data,指向要渲染的繪制圖像的記憶體位址

參數2:width,bitmap的寬度,機關為像素

參數3:height,bitmap的高度,機關為像素

參數4:bitPerComponent,記憶體中像素的每個元件的位數,比如32位RGBA,就設定為8

參數5:bytesPerRow,bitmap的沒一行的記憶體所占的比特數

參數6:colorSpace,bitmap上使用的顔色空間 kCGImageAlphaPremultipliedLast:RGBA

*/

CGContextRef context = CGBitmapContextCreate(imageData, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

//對圖檔進行重新繪制,得到一張新的解壓縮後的位圖

CGContextDrawImage(context, rect, cgImageRef);

//設定紋理

GLuint textureID;

glGenTextures(1, &textureID);

glBindTexture(GL_TEXTURE_2D, textureID);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);

//設定紋理屬性

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);

//綁定紋理

glBindTexture(GL_TEXTURE_2D, 0);

CGContextRelease(context);

free(imageData);

return textureID;

}

3.7 建立頂點和紋理坐标,此處為相對坐标,前面三個為頂點坐标(X,Y,Z),後面兩個為紋理坐标(U,V)

[self createVertices];

- (void)createVertices

{

GLfloat vertex[] =

{

1.f, -1.f, 0.f, 1.0f, 1.0f,

-1.f, 1.f, 0.f, 0.0f, 0.0f,

-1.f, -1.f, 0.f, 0.0f, 1.0f,

1.f, 1.f, 0.f, 1.0f, 0.0f,

-1.f, 1.f, 0.f, 0.0f, 0.0f,

1.f, -1.f, 0.f, 1.0f, 1.0f,

};

GLuint vertexBuffer;

glGenBuffers(1, &vertexBuffer);

glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);

glBufferData(GL_ARRAY_BUFFER, **sizeof**(vertex), vertex, GL_DYNAMIC_DRAW);

self.vertexBuffer = vertexBuffer;

}

3.8 建立頂點着色器Normal.vsh和片元着色器Normal.fsh,着色器代碼會被編譯為字元串

Normal.vsh

attribute vec4 Position;

attribute vec2 TextureCoords;

varying vec2 TextureCoordsVarying;

void main(void) {

gl_Position = Position;

TextureCoordsVarying = TextureCoords;

}

Normal.fsh

precision highp float;

uniform sampler2D Texture;

varying vec2 TextureCoordsVarying;

void main (void) {

gl_FragColor = texture2D(Texture, TextureCoordsVarying);

}

3.9 編譯我們剛剛寫的着色器代碼

GLuint vertexShader = [self compileShaderWithName:name type:GL_VERTEX_SHADER];

GLuint fragmentShader = [self compileShaderWithName:name type:GL_FRAGMENT_SHADER];

- (GLuint)compileShaderWithName:(NSString *)name type:(GLenum)shaderType

{

NSString *shaderPath = [[NSBundle mainBundle] pathForResource:name ofType:shaderType == GL_VERTEX_SHADER ? @"vsh" : @"fsh"];

NSError *error;

NSString *shaderString = [NSString stringWithContentsOfFile:shaderPath encoding:NSUTF8StringEncoding error:&error];

if (!shaderString) {

NSAssert(NO, @"讀取shader失敗");

}

//2. 建立shader->根據shaderType

GLuint shader = glCreateShader(shaderType);

//3.擷取shader source

const char *shaderStringUTF8 = [shaderString UTF8String];

int shaderStringLength = (int)[shaderString length];

glShaderSource(shader, 1, &shaderStringUTF8, &shaderStringLength);

//4.編譯shader

glCompileShader(shader);

//5.檢視編譯是否成功

GLint compileSuccess;

glGetShaderiv(shader, GL_COMPILE_STATUS, &compileSuccess);

if (compileSuccess == GL_FALSE) {

GLchar messages[256];

glGetShaderInfoLog(shader, sizeof(messages), 0, &messages[0]);

NSString *messageString = [NSString stringWithUTF8String:messages];

NSAssert(NO, @"shader編譯失敗:%@", messageString);

exit(1);

}

//6.傳回shader

return shader;

}

3.10 建立程式,把着色器代碼附着在程式上,連結程式,此處傳入的name為着色器的name(Normal)

【更多音視訊學習資料,點選下方連結免費領取↓↓,先碼住不迷路~】

點選領取→音視訊開發基礎知識和資料包

GLuint program = [self programWithShaderName:name];

- (GLuint)programWithShaderName:(NSString *)name

{

GLuint vertexShader = [self compileShaderWithName:name type:GL_VERTEX_SHADER];

GLuint fragmentShader = [self compileShaderWithName:name type:GL_FRAGMENT_SHADER];

GLuint program = glCreateProgram();

self.program = program;

glAttachShader(program, vertexShader);

glAttachShader(program, fragmentShader);

glLinkProgram(program);

GLint linkSuccess;

glGetProgramiv(program, GL_LINK_STATUS, &linkSuccess);

if (linkSuccess == GL_FALSE) {

GLchar messages[256];

glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]);

NSString *messageString = [NSString stringWithUTF8String:messages];

NSAssert(NO, @"program連結失敗:%@", messageString);

exit(1);

}

//5.傳回program

return program;

}

3.11 把對應的頂點坐标和紋理坐标傳入着色器,此處傳入的program為剛剛連結完成的program

//3. 擷取Position,Texture,TextureCoords 的索引位置

GLuint positionSlot = glGetAttribLocation(program, "Position");

GLuint textureSlot = glGetUniformLocation(program, "Texture");

GLuint textureCoordsSlot = glGetAttribLocation(program, "TextureCoords");

//激活紋理

glActiveTexture(GL_TEXTURE0);

glBindTexture(GL_TEXTURE_2D, self.textureID);

glUniform1i(textureSlot, 0);

//打開頂點坐标通道

glEnableVertexAttribArray(positionSlot);

//傳入頂點坐标

glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (float*)NULL);

//打開紋理坐标通道

glEnableVertexAttribArray(textureCoordsSlot);

//傳入紋理坐标

glVertexAttribPointer(textureCoordsSlot, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (float*)NULL + 3);

3.12 開始繪制,繪制之前最好再次執行綁定及清除的操作,避免别人的代碼會産生影響

//使用program

glUseProgram(self.program);

//綁定buffer

glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer);

// 清除畫布

glClear(GL_COLOR_BUFFER_BIT);

glClearColor(1, 1, 1, 1);

// 重繪

glDrawArrays(GL_TRIANGLES, 0, 6);

//渲染到螢幕上

[self.context presentRenderbuffer:GL_RENDERBUFFER];

如果你對音視訊開發感興趣,覺得文章對您有幫助,别忘了點贊、收藏哦!或者對本文的一些闡述有自己的看法,有任何問題,歡迎在下方評論區讨論!

繼續閱讀