如果我们要在OpenGLES2.0中显示纹理的话, 要依赖于Fragment Shader.
OpenGL要有两个Shader, Vertex和Fragment
Vertex用来管理顶点, 你要绘制什么形状的图形, 都是由Vertex来控制的, 如三角形, 四边形, 立方体, 球形
Fragment用来管理颜色, 用gl_FragColor来控制输出的颜色, 这个颜色可以是自定的颜色RGBA, 如vec4(0, 0, 0,1),也可以是外部传递进来的颜色, 还可以是从一张纹理上某个点取出来的颜色texture2D(texture, point).
假如我们画一个三角形, 三个顶点A(-1, -1), B(1, -1), C(1, 1)
这时候gl_FragColor只会对ABC三个点赋颜色值, 至于其他点的颜色, 只能靠”猜”
如果是普通颜色的话, 就按照颜色插值, 按照与ABC的比重abc来计算Aa + Bb + Cc = 最终颜色, 如(1,1,1,1) * 0.5 + (0, 0, 0,1) * 0.5 = (0.5, 0.5, 0.5, 1)
如果是纹理的话, 假设A点是取纹理上的(0, 0)这个点, B点是取纹理上的(0, 1)这个点, 那么AB的中点会取纹理上的(0, 0.5)这个点
加载纹理的过程和RenderBuffer, FrameBuffer很类似
首先先创建一个纹理
glGenTextures (GLsizei n, GLuint *textures);
然后绑定
glBindTexture (GLenum target, GLuint texture);
绑定完后可以设置参数
glTexParameteri (GLenum target, GLenum pname, GLint param);
参数设置完后, 就可以给这张纹理加载数据了.
glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
第一个参数target, 必须为GL_TEXTURE_2D
第二个参数level为执行细节级别, 通常为0
第三个参数internalformat, 纹理的颜色格式, 必须与后面的format相同
第四个参数width, 纹理的宽度, 必须是2的n次方, 必须>=64
第五个参数width, 纹理的高度, 必须是2的n次方, 必须>=64
第六个参数border, 边框的值, 必须为0
第七个参数format, 像素数据的颜色格式, 必须与前面的internalformat一样, 常见的有GL_RGBA
第八个参数type, 像素数据的数据格式, 常见的有GL_UNSIGNED_BYTE
第九个参数pixels, 像素数据的首地址
这样我们就加载了一张纹理, 纹理的使用方式, 与[前一篇]绘制彩色三角形很像(http://blog.csdn.net/u010963658/article/details/52692201)
因为本质都是给顶点”上色”,
绘制彩色三角形是指明上什么颜色, 如RGBA(1, 0, 0, 1)
绘制纹理是指明要上纹理哪个点的颜色, 如XY(0, 0)
显示纹理的6个步骤
1.配置OpenGL
- (void) setupOpenGL {
_eaglLayer = (CAEAGLLayer *) self.layer;
_eaglLayer.opaque = YES;
_eaglLayer.drawableProperties = @{kEAGLDrawablePropertyRetainedBacking: @YES,
kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8};
_eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:_eaglContext];
glGenRenderbuffers(1, &_renderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
glGenFramebuffers(1, &_frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderBuffer);
[_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
}
2.清屏
- (void) clear {
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
glClearColor(0, 0, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
[EAGLContext setCurrentContext:self.eaglContext];
}
3.创建OpenGL Program
- (GLuint) compileShaderSource : (NSString *) shaderSource shaderType : (GLenum) shaderType {
GLuint shader = glCreateShader(shaderType);
const char *source = shaderSource.UTF8String;
int length = (int)shaderSource.length;
glShaderSource(shader, 1, &source, &length);
glCompileShader(shader);
#ifdef DEBUG
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];
NSLog(@"%@", messageString);
return 0;
}
#endif
return shader;
}
/**
编译Program + Vertex Shader + Fragment Shader
*/
- (void) createProgram {
NSString *vertexPath = [[NSBundle mainBundle] pathForResource:@"VertexShader" ofType:@"glsl"];
NSString *vertexShaderSource = [NSString stringWithContentsOfFile:vertexPath encoding:NSUTF8StringEncoding error:nil];
NSString *fragmentPath = [[NSBundle mainBundle] pathForResource:@"FragmentShader" ofType:@"glsl"];
NSString *fragmentShaderSource = [NSString stringWithContentsOfFile:fragmentPath encoding:NSUTF8StringEncoding error:nil];
//编译Vertex Shader和Fragment Shader
_vertexShader = [self compileShaderSource:vertexShaderSource shaderType:GL_VERTEX_SHADER];
_fragmentShader = [self compileShaderSource:fragmentShaderSource shaderType:GL_FRAGMENT_SHADER];
//创建program并与Vertex Shader和Fragment Shader关联
_program = glCreateProgram();
glAttachShader(_program, _vertexShader);
glAttachShader(_program, _fragmentShader);
//链接program
glLinkProgram(_program);
#ifdef DEBUG
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];
NSLog(@"%@", messageString);
return;
}
#endif
//开始使用program
glUseProgram(_program);
_positionAttribute = glGetAttribLocation(_program, "position");
_textureCoordAttribute = glGetAttribLocation(_program, "inputTexCoord");
_textureUnifrom = glGetUniformLocation(_program, "texture");
}
4.创建顶点与纹理显示位置的数据
static int vertexCount = 6;
static GLfloat vertexData[] = {
-0.9, -0.9,
0.9, -0.9,
0.9, 0.9,
0.9, 0.9,
-0.9, 0.9,
-0.9, -0.9
};
static int textureCoordCount = 6;
static GLfloat textureCoordData[] = {
//纹理的范围是[0, 1]
0, 0,
1, 0,
1, 1,
1, 1,
0, 1,
0, 0
};
//Vertex And TextureCoord Data
- (void) createData {
glGenBuffers(1, &_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
//激活_positionAttribute
glEnableVertexAttribArray(_positionAttribute);
//因为上面激活了_positionAttribute, 所以这边是告诉OpenGL这些数据怎么用, 给_positionAttribute用
glVertexAttribPointer(_positionAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 2, NULL);
glGenBuffers(1, &_textureCoordBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _textureCoordBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(textureCoordData), textureCoordData, GL_STATIC_DRAW);
glEnableVertexAttribArray(_textureCoordAttribute);
glVertexAttribPointer(_textureCoordAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 2, NULL);
}
5.加载纹理
- (GLuint) loadTextureFromCGImage : (CGImageRef) imageRef {
//copy rgba data from imageRef
int width = (int)CGImageGetWidth(imageRef);
int height = (int)CGImageGetHeight(imageRef);
GLubyte * imageData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));
CGContextRef spriteContext = CGBitmapContextCreate(imageData, width, height, 8, width * 4, CGImageGetColorSpace(imageRef), kCGImageAlphaPremultipliedLast);
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), imageRef);
CGContextRelease(spriteContext);
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
free(imageData);
return texture;
}
- (void) createTexture {
UIImage *image = [UIImage imageNamed:@"finsh.png"];
GLuint finshTexture = [self loadTextureFromCGImage:image.CGImage];
//激活GL_TEXTURE0
glActiveTexture(GL_TEXTURE0);
//将finshTexture绑定到GL_TEXTURE_2D上, 因为这边激活的是GL_TEXTURE0, 所以finshTexture是绑定到GL_TEXTURE0上
glBindTexture(GL_TEXTURE_2D, finshTexture);
//让_textureUnifrom这个使用GL_TEXTURE0, 如果使用GL_TEXTURE1, 对应的参数为1, 使用GL_TEXTUREi对应的参数为i
glUniform1i(_textureUnifrom, 0);
}
6绘制
- (void) render {
//绑定_vertexBuffer到GL_ARRAY_BUFFER
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
//上面绑定了_vertexBuffer, 所以这边的DrawArrays是针对_vertexBuffer的操作
glDrawArrays(GL_TRIANGLES, 0, vertexCount);
//绑定_textureCoordBuffer到GL_ARRAY_BUFFER
glBindBuffer(GL_ARRAY_BUFFER, _textureCoordBuffer);
//上面绑定了_textureCoordBuffer, 所以这边的DrawArrays是针对_textureCoordBuffer
glDrawArrays(GL_TRIANGLES, 0, textureCoordCount);
[self.eaglContext presentRenderbuffer:GL_RENDERBUFFER];
}
最后的代码在这