本案例是在OpenGL ES案例04_1-GLSL使用索引绘图基础上增加纹理颜色混合。
最终效果如下:
本案例与之前案例最大的区别是增加了纹理颜色混合。其它绘制流程一致,可参考之前案例描述或文末的完整代码。接下来描述一下如何增加纹理颜色混合。
顶点着色器
在顶点着色器中,需要增加纹理坐标属性,并且将纹理坐标传递给片元着器
attribute vec4 position;
attribute vec4 positionColor;
attribute vec2 textCoor;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
varying lowp vec2 vTextCoor;
varying lowp vec4 varyColor;
void main(){
vTextCoor = textCoor;
varyColor = positionColor;
gl_Position = projectionMatrix * modelViewMatrix * position;
}
片元着色器
- 设置float为高精度。在代码中去掉该行,无法正常绘制图片。
- 增加获取纹理坐标的属性,
该属性与在顶点着色器中必须一模一样
。 - 使用texture2D函数,通过采样器,获取当前纹理坐标的颜色值。
- 对传入的顶点颜色值和纹理坐标对应的颜色值进行混合计算。
- 将最终计算的颜色值赋值给gl_FragColor。
precision highp float;
varying lowp vec2 vTextCoor;
varying lowp vec4 varyColor;
uniform sampler2D colorMap;
void main(){
vec4 weakMask = texture2D(colorMap, vTextCoor);
vec4 mask = varyColor;
float alpha = 0.3;
vec4 tempColor = mask * (1.0 - alpha) + weakMask * alpha;
gl_FragColor = tempColor;
}
读取纹理数据
- 解压缩png/jpg图片,将UIImage转换为CGImageRef。
CGImageRef image = [[UIImage imageNamed:@"nn.jpg"] CGImage];
- 根据CGImageRef属性获取图片的宽和高,并开辟一段空间用于存放解压缩后的位图信息。
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
GLubyte* spriteData = (GLubyte*)calloc(width*height*4, sizeof(GLubyte));
- 创建CGContextRef上下文
CGContextRef context = CGBitmapContextCreate(spriteData, width, height, 8, width*4, CGImageGetColorSpace(image), kCGImageAlphaPremultipliedLast);
CGRect rect = CGRectMake(0, 0, width, height);
- 在CGContextRef上将图片绘制出来,调用CGContextDrawImage函数,使用默认方式绘制,这样就得到了纹理数据。
CGContextDrawImage(context, rect, image);
- 得到纹理数据之后就不需要CGContextRef上下文了,可以将CGContextRef这个上下文释放掉。
CGContextRelease(context);
- 绑定一个纹理ID,默认纹理ID为0。
glBindTexture(GL_TEXTURE_2D, 0);
- 设置纹理的参数。
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_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- 载入纹理数据。
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (float)width, (float)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
- 释放位图信息
free(spriteData);
- 设置纹理采样器
主要是用来获取纹理中对应像素点的颜色值,即纹素。此处使用glUniform1i函数告诉采样器,要获取纹理ID为0的纹素。
glUniform1i(glGetUniformLocation(self.myProgram, "colorMap"), 0);
设置纹理坐标
由于添加了纹理,因此,在设置顶点坐标的时候需要添加纹理坐标。
- 获取顶点着色器中定义纹理坐标属性通道。
- 打开纹理坐标属性通道。
- 将数据传入纹理坐标属性。
-(void)setupVertexData{
//(1)顶点数组 前3顶点值(x,y,z),后3位颜色值(RGB)
GLfloat attrArr[] = {
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 1.0f,//左上
0.5f, 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 1.0f, 1.0f,//右上
-0.5f, -0.5f, 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f,//左下
0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 1.0f, 0.0f,//右下
0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f,//顶点
};
GLuint bufferID;
glGenBuffers(1, &bufferID);
glBindBuffer(GL_ARRAY_BUFFER, bufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
GLuint position = glGetAttribLocation(self.myProgram, "position");
glEnableVertexAttribArray(position);
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), NULL);
GLuint positionColor = glGetAttribLocation(self.myProgram, "positionColor");
glEnableVertexAttribArray(positionColor);
glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), (GLfloat*)NULL+3);
GLuint textCoor = glGetAttribLocation(self.myProgram, "textCoor");
glEnableVertexAttribArray(textCoor);
glVertexAttribPointer(textCoor, 2, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), (GLfloat*)NULL+6);
}
到此,整个Demo完成。具体代码请看GLSL三角形变换_纹理颜色混合
网友评论