學習之路系列
本篇主要內容
多重紋理渲染
效果展示
- 這個效果看起來有點詭異
實現過程
- 我這里先把上期的效果Copy過來, 我直接在這上面添加紋理
1.頂點著色器
首相我們在頂點著色器(TextureVertex.glsl)
里面添加一段聲明
attribute vec2 TexCoordIn;
varying vec2 TexCoordOut;
把下面這段代碼添加到main
函數的末尾
TexCoordOut = TexCoordIn;
修改完成后如下圖:
接著我們在片段著色器(TextureFragment.glsl)
里面添加代碼
uniform sampler2D ourTexture1;
varying lowp vec2 TexCoordOut;
把下面這段代碼
gl_FragColor = OutColor;
改為
gl_FragColor = OutColor * texture2D(ourTexture1, TexCoordOut);
2.設置紋理數據
/*
* 通過UIImage的方式獲取紋理對象
*/
+ (GLuint)getTextureImageName:(NSString *)imageName {
// 獲取UIImage并轉換成CGImage
CGImageRef spriteImage = [UIImage imageNamed:imageName].CGImage;
if(!spriteImage) {
return 0;
}
// 獲取圖片的大小
GLsizei width = (GLsizei)CGImageGetWidth(spriteImage);
GLsizei height = (GLsizei)CGImageGetHeight(spriteImage);
// 分配內存,并初始化該內存空間為零, 因為一個像素有4個通道(RGBA)所以乘4
GLubyte * spriteData = (GLubyte *)calloc(width * height * 4, sizeof(GLubyte));
/*
* 創建位圖上下文
*/
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4,
CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
// 在上下文中繪制
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
// 釋放上下文
CGContextRelease(spriteContext);
// 創建紋理對象并且綁定, 紋理對象用無符號整數表示, 這個紋理對象相當于我們在C語言文件操作里面的句柄
GLuint texName;
glGenTextures(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);
// 加載圖像數據, 并上傳紋理
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
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);
// 釋放分配的內存空間
free(spriteData);
return texName;
}
上面的這些函數講解可以看這篇文章 LearnOpenGL??, 里面還有例子, 講解的很詳細
2.獲取著色器里的變量
我們在compileShaders
方法下新增一段代碼
_textureSlot = glGetAttribLocation(_program, "TexCoordIn");
glEnableVertexAttribArray(_textureSlot);
_textureUniform = glGetUniformLocation(_program, "ourTexture1");
//獲取紋理對象
_texture1 = [TextureManager getTextureImageName:@"Texture1.jpg"];
3. 開始渲染
在render
方法中新增一段代碼
static const float Texture[] = {
0, 0,
1, 0,
0, 1,
1, 1,
};
glVertexAttribPointer(_textureSlot, 2, GL_FLOAT, GL_FALSE, 0, Texture);
上面這段代碼可以參照上一篇文章
//使用紋理單元
glActiveTexture(GL_TEXTURE0);
//綁定紋理對象
glBindTexture(GL_TEXTURE_2D, _texture1);
//這里的參數要對應紋理單元(如果紋理單元為0,這里也要給0)
glUniform1i(_textureUniform, 0);
這里講到了兩個東西紋理單元
和紋理對象
-
紋理對象:
也就是上面我們提到的類似于C語言中文件操作的句柄
-
紋理單元:
紋理單元
就是將程序中的紋理貼圖反應到像素位置的運算單元,顯卡會劃分N個紋理存儲區域,多重紋理可以開啟多個紋理單元
。上面的glUniform1i(_textureUniform, 0);
這段代碼對應的就是紋理單元的
位置。
紋理單元的數量跟硬件有關,蘋果給用的紋理單元數量如下:
/* TextureUnit */
#define GL_TEXTURE0 0x84C0
#define GL_TEXTURE1 0x84C1
#define GL_TEXTURE2 0x84C2
#define GL_TEXTURE3 0x84C3
#define GL_TEXTURE4 0x84C4
#define GL_TEXTURE5 0x84C5
#define GL_TEXTURE6 0x84C6
#define GL_TEXTURE7 0x84C7
#define GL_TEXTURE8 0x84C8
#define GL_TEXTURE9 0x84C9
#define GL_TEXTURE10 0x84CA
#define GL_TEXTURE11 0x84CB
#define GL_TEXTURE12 0x84CC
#define GL_TEXTURE13 0x84CD
#define GL_TEXTURE14 0x84CE
#define GL_TEXTURE15 0x84CF
#define GL_TEXTURE16 0x84D0
#define GL_TEXTURE17 0x84D1
#define GL_TEXTURE18 0x84D2
#define GL_TEXTURE19 0x84D3
#define GL_TEXTURE20 0x84D4
#define GL_TEXTURE21 0x84D5
#define GL_TEXTURE22 0x84D6
#define GL_TEXTURE23 0x84D7
#define GL_TEXTURE24 0x84D8
#define GL_TEXTURE25 0x84D9
#define GL_TEXTURE26 0x84DA
#define GL_TEXTURE27 0x84DB
#define GL_TEXTURE28 0x84DC
#define GL_TEXTURE29 0x84DD
#define GL_TEXTURE30 0x84DE
#define GL_TEXTURE31 0x84DF
#define GL_ACTIVE_TEXTURE 0x84E0
運行效果
發現圖像是反的,解決辦法有兩個
- 1、修改頂點數據
- 2、修改著色器中的紋理數據
這里使用第二種辦法, 將頂點著色器(TextureVertex.glsl)
中的
TexCoordOut = TexCoordIn;
改為
TexCoordOut = vec2(TexCoordIn.x, 1. - TexCoordIn.y);
把Y軸顛倒一下以后在運行看看效果
新增紋理
在片段著色器(TextureFragment.glsl)
中新增一個變量
uniform sampler2D ourTexture2;
把
gl_FragColor = OutColor * texture2D(ourTexture1, TexCoordOut);
改為
gl_FragColor = OutColor * mix(texture2D(ourTexture1, TexCoordOut), texture2D(ourTexture2, TexCoordOut), 0.7);
mix
函數是一個混合線性函數,聲明及算法如下
genType mix (genType x, genType y, genType a)
x ? ( 1 ? a ) + y ? a
在中compileShaders
方法的底部新增下面兩端代碼
_textureUniform2 = glGetUniformLocation(_program, "ourTexture2");
_texture2 = [TextureManager getTextureImageName:@"Texture2.jpg"];
在渲染方法render
新增
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _texture2);
glUniform1i(_textureUniform2, 1);
運行效果
- (void)dealloc {
//刪除綁定的渲染緩沖區
if(_renderBuffer) {
glDeleteRenderbuffers(GL_RENDERBUFFER, &_renderBuffer);
}
//刪除綁定的幀緩沖區
if(_frameBuffer) {
glDeleteFramebuffers(GL_FRAMEBUFFER, &_frameBuffer);
}
//釋放著色器
if(_vertexShader) {
//刪除頂點著色器連接
glDetachShader(_program, _vertexShader);
//刪除頂點著色器
glDeleteShader(_vertexShader);
}
if(_fragmentShader) {
//刪除片段著色器連接
glDetachShader(_program, _fragmentShader);
//刪除片段著色器
glDeleteShader(_fragmentShader);
}
if(_program) {
glDeleteProgram(_program);
}
glDisableVertexAttribArray(_positionSlot);
glDisableVertexAttribArray(_colorSlot);
glDisableVertexAttribArray(_textureSlot);
glDeleteTextures(1, &_texture1);
glDeleteTextures(1, &_texture2);
glBindTexture(GL_TEXTURE_2D, 0);
}
最后記得將資源釋放
Demo鏈接:LearnOpenGLES