OpenGL ES _ 入門_01
OpenGL ES _ 入門_02
OpenGL ES _ 入門_03
OpenGL ES _ 入門_04
OpenGL ES _ 入門_05
OpenGL ES _ 入門練習(xí)_01
OpenGL ES _ 入門練習(xí)_02
OpenGL ES _ 入門練習(xí)_03
OpenGL ES _ 入門練習(xí)_04
OpenGL ES _ 入門練習(xí)_05
OpenGL ES _ 入門練習(xí)_06
OpenGL ES _ 著色器 _ 介紹
OpenGL ES _ 著色器 _ 程序
OpenGL ES _ 著色器 _ 語法
OpenGL ES_著色器_紋理圖像
OpenGL ES_著色器_預(yù)處理
OpenGL ES_著色器_頂點著色器詳解
OpenGL ES_著色器_片斷著色器詳解
OpenGL ES_著色器_實戰(zhàn)01
OpenGL ES_著色器_實戰(zhàn)02
OpenGL ES_著色器_實戰(zhàn)03
學(xué)習(xí)目標
使用著色器渲染一張圖片
核心步驟
- 創(chuàng)建著色器程序
- 加載頂點坐標
- 加載紋理坐標
- 加載紋理
- 繪制
代碼講解
1.由于著色器編譯 鏈接過程較為繁瑣庐橙,我封裝了一下,文件名為"OSShaderManager.h" 和"OSShaderManager.m"
如果你對著色器程序加載過程不熟悉請參考OpenGL ES _ 著色器 _ 程序
代碼:
// 第一步.創(chuàng)建我們的對象
self.shaderManager = [[OSShaderManager alloc]init];
// 第二步.編譯連個shader 文件
GLuint vertexShader,fragmentShader;
NSURL *vertexShaderPath = [[NSBundle mainBundle]URLForResource:@"Shader" withExtension:@"vsh"];
NSURL *fragmentShaderPath = [[NSBundle mainBundle]URLForResource:@"Shader" withExtension:@"fsh"];
if (![self.shaderManager compileShader:&vertexShader type:GL_VERTEX_SHADER URL:vertexShaderPath]||![self.shaderManager compileShader:&fragmentShader type:GL_FRAGMENT_SHADER URL:fragmentShaderPath]){
return ;
}
//第三步. 注意獲取綁定屬性要在連接程序之前
[self.shaderManager bindAttribLocation:GLKVertexAttribPosition andAttribName:"position"];
[self.shaderManager bindAttribLocation:GLKVertexAttribTexCoord0 andAttribName:"texCoord0"];
//第四步 將編譯好的兩個對象和著色器程序進行連接
if(![self.shaderManager linkProgram]){
[self.shaderManager deleteShader:&vertexShader];
[self.shaderManager deleteShader:&fragmentShader];
}
// 第五步.獲取著色器全局屬性的索引
_textureBufferR = [self.shaderManager getUniformLocation:"sam2DR"];
// 第六步.程序編譯成功后瓷胧,刪除著色器對象
[self.shaderManager detachAndDeleteShader:&vertexShader];
[self.shaderManager detachAndDeleteShader:&fragmentShader];
2.加載頂點坐標
如果你對頂點坐標的概念不清楚請查看OpenGL ES _ 入門_02
先看一下我們的頂點坐標長什么樣子
static GLfloat vertex[8] = {
1,1, //1
-1,1,//0
-1,-1, //2
1,-1, //3
};
我們選擇的繪制模式為:GL_TRIANGLE_FAN
繪制方法:以V1為起始點桩引,逆時針以此繪制三角形,我們的排列順序為(V1,V0,V2,V3),那么我們繪制第一個三角形為 V1,V0,V2 繪制第二個三角形為 V1,V2,V3
下一個問題茉唉,我們?nèi)绾螌㈨旤c數(shù)據(jù)導(dǎo)入我們的GPU中去?
// 第一步.在GPU 中先申請一個內(nèi)存標識
glGenBuffers(1, &_vertexBuffer);
// 第二步.讓這個標識去綁定一個內(nèi)存區(qū)域,但是此時,這個內(nèi)存沒有大小.
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
// 第三步.根據(jù)頂點數(shù)組的大小海蔽,開辟內(nèi)存空間钥组,并將數(shù)據(jù)加載到內(nèi)存中
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), &vertex, GL_STATIC_DRAW);
// 第四步 .啟用這塊內(nèi)存输硝,標記為位置
glEnableVertexAttribArray(GLKVertexAttribPosition);
// 第五步.告訴GPU 頂點數(shù)據(jù)在內(nèi)存中的格式是怎么樣的,應(yīng)該如何去使用
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, 8, NULL);
3.加載紋理坐標系
同樣先看我們的紋理坐標長什么樣子
static GLfloat textureCoords[8] = {
1,1,// 對應(yīng)V1
0,1,// 對應(yīng)V0
0,0,// 對應(yīng)V2
1,0 // 對應(yīng)V3
};
紋理坐標系S 軸和 T 軸的取值范圍都為[0,1] 程梦,這張圖應(yīng)該能夠明白大體怎么使用吧!
問題:如何加載紋理坐標數(shù)據(jù)?(過程和加載頂點數(shù)據(jù)過程一樣)
// 第一步.在GPU 中先申請一個內(nèi)存標識
glGenBuffers(1, &_textureCoordBuffer);
// 第二步.讓這個標識去綁定一個內(nèi)存區(qū)域点把,但是此時橘荠,這個內(nèi)存沒有大小.
glBindBuffer(GL_ARRAY_BUFFER, _textureCoordBuffer);
// 第三步.根據(jù)頂點數(shù)組的大小,開辟內(nèi)存空間,并將數(shù)據(jù)加載到內(nèi)存中
glBufferData(GL_ARRAY_BUFFER, sizeof(textureCoords), textureCoords, GL_STATIC_DRAW);
// 第四步 .啟用這塊內(nèi)存咧七,標記為位置
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
// 第五步.告訴GPU 頂點數(shù)據(jù)在內(nèi)存中的格式是怎么樣的速缨,應(yīng)該如何去使用
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 8, NULL);
4.加載紋理圖片像素數(shù)據(jù)
我們先用下面下面這段代碼獲取圖片的像素數(shù)據(jù)
- (void*)getImageData:(UIImage*)image{
CGImageRef imageRef = [image CGImage];
size_t imageWidth = CGImageGetWidth(imageRef);
size_t imageHeight = CGImageGetHeight(imageRef);
GLubyte *imageData = (GLubyte *)malloc(imageWidth*imageHeight*4);
memset(imageData, 0,imageWidth *imageHeight*4);
CGContextRef imageContextRef = CGBitmapContextCreate(imageData, imageWidth, imageHeight, 8, imageWidth*4, CGImageGetColorSpace(imageRef), kCGImageAlphaPremultipliedLast);
CGContextTranslateCTM(imageContextRef, 0, imageHeight);
CGContextScaleCTM(imageContextRef, 1.0, -1.0);
CGContextDrawImage(imageContextRef, CGRectMake(0.0, 0.0, (CGFloat)imageWidth, (CGFloat)imageHeight), imageRef);
CGContextRelease(imageContextRef);
return imageData;
}
然后開始加載我們的圖片像素數(shù)據(jù):
-(void)loadTexture{
//第一步.將我們著色器中的紋理采樣器和紋理區(qū)域0進行關(guān)聯(lián).
glUniform1i(_textureBufferR, 0); // 0 代表GL_TEXTURE0
GLuint tex1;
//第二步.激活紋理區(qū)域0
glActiveTexture(GL_TEXTURE0);
//第三步. 申請內(nèi)存標識
glGenTextures(1, &tex1);
//第四步. 將內(nèi)存和激活的紋理區(qū)域綁定
glBindTexture(GL_TEXTURE_2D, tex1);
UIImage *image = [UIImage imageNamed:@"2.png"];
GLubyte *imageData = [self getImageData:image];
//第五步.將圖片像素數(shù)據(jù),加載到紋理區(qū)域0 中去
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA , image.size.width, image.size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
//第六步.設(shè)置圖片在渲染時的一些配置
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
5.最后一步繪制
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
// 設(shè)置清除顏色
glClearColor(1, 1, 1, 1);
// 清除顏色緩沖區(qū)
glClear(GL_COLOR_BUFFER_BIT);
// 使用著色器程序
[self.shaderManager useProgram];
// 繪制 GL_TRIANGLE_FAN 這個是繪制方式贮懈,我們上面講過了.
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
著色器程序源碼講解
Shader.vsh 頂點著色器,后綴隨便起的(vertex Shader)你也可以這是text沒有關(guān)系,不糾結(jié)哈,我們看代碼:
attribute vec4 position; // 頂點位置
attribute vec2 texCoord0;// 紋理坐標
varying vec2 texCoordVarying;//片段著色器的輸入變量
void main (){
gl_Position = position;
texCoordVarying = texCoord0;
}
attribute 代表變量是頂點著色器的輸入變量
vec4 4維向量
varying 代表這個變量是片段著色器的輸入變量
我們看看片段著色器的代碼(Shader.fsh)
precision mediump float;//mediump
varying vec2 texCoordVarying;
uniform sampler2D sam2DR;
void main(){
lowp vec4 rgba = vec4(0,0,0,1);
rgba = texture2D(sam2DR,texCoordVarying);
gl_FragColor = rgba;
}
precision 設(shè)置float精度影暴,mediump 表示中等還有l(wèi)owp 和highp 可選
uniform 代表這個變量是需要從程序外部错邦,即應(yīng)用程序中輸入的,uniform 只能輸入全局變量,切記.
總結(jié)
講解了如何使用著色器加載一樣圖片型宙,把代碼放在這里了,希望你能下載下來看看撬呢。提供一個群號(578734141)給需要幫助的小伙伴!