GLSL纹理绘制

249 阅读4分钟

OpenGL ES下我们使用GLKitView作为显示视图做过关于纹理的绘制,使用GLKBaseEffect进行着色器处理的相关事件,现在我们使用编译链接自定义着色器,用glsl语言来实现顶点与片元着色器,并进行简单的图形变换。绘制和OpenGL ES下的绘制流程一致,这里主要记录如何链接程序和着色器。

1、读取顶点着色器和片元着色器程序
// 顶点着色器
NSString *vertFile = [[NSBundle mainBundle]pathForResource:@"shaderv" ofType:@"vsh"];
// 片元着色器
NSString *fragFile = [[NSBundle mainBundle]pathForResource:@"shaderf" ofType:@"fsh"];   
1、顶点着色器
// 顶点坐标
attribute vec4 position;
// 纹理坐标
attribute vec2 textCoordinate;
/ 纹理作坐标
varying lowp vec2 varyTextCoord;
void main()	
{
	//通过varying 修饰varyTextCoord,将纹理坐标传递到片元着色器
	varyTextCoord = textCoordinate;
    // 给内建变量gl_position 赋值
 	gl_Position = position;
}
2、片元着色器实现
// 传入的纹理坐标
varying lowp vec2 varyTextCoord;
// 纹理采样器(获取对应的纹理ID)
uniform sampler2D colorMap;
void main()
{
	// gl_FragColor GLSL 内建变量(赋值像素点颜色值)
    // 纹理颜色添加到对应的像素点上
	 gl_FragColor = texture2D(colorMap, varyTextCoord);
}
2、加载shader
//1.定义2个零时着色器对象
GLuint verShader, fragShader;
//创建program
GLint program = glCreateProgram();

//2.编译顶点着色程序、片元着色器程序
//参数1:编译完存储的底层地址
//参数2:编译的类型,GL_VERTEX_SHADER(顶点)、GL_FRAGMENT_SHADER(片元)
//参数3:文件路径
[self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
[self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];

//3.创建最终的程序
glAttachShader(program, verShader);
glAttachShader(program, fragShader);

//4.释放不需要的shader
glDeleteShader(verShader);
glDeleteShader(fragShader);
3、编译shader
- (void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file{

//1.读取文件路径字符串
NSString* content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
const GLchar* source = (GLchar *)[content UTF8String];
//2.根据类型创建一个着色器
*shader = glCreateShader(type);
//3.将着色器源码附加到着色器对象上。
glShaderSource(*shader, 1, &source,NULL);
//4.把着色器源代码编译成目标代码
glCompileShader(*shader);
}
4、链接program并判断链接状态
glLinkProgram(self.myPrograme);
GLint linkStatus;
//获取链接状态
glGetProgramiv(self.myPrograme, GL_LINK_STATUS, &linkStatus);
if (linkStatus == GL_FALSE) {
    GLchar message[512];
    glGetProgramInfoLog(self.myPrograme, sizeof(message), 0, &message[0]);
    NSString *messageString = [NSString stringWithUTF8String:message];
    NSLog(@"Program Link Error:%@",messageString);
    return;
}
5、使用program
glUseProgram(self.myPrograme);

需要注意的点是:在使用glsl进行绘制是,顶点数据和纹理数据在着色器传递时,定义名称要保持一致。

6、顶点数据处理
 //(1)顶点缓存区
GLuint attrBuffer;
//(2)申请一个缓存区标识符
glGenBuffers(1, &attrBuffer);
//(3)将attrBuffer绑定到GL_ARRAY_BUFFER标识符上
glBindBuffer(GL_ARRAY_BUFFER, attrBuffer);
//(4)把顶点数据从CPU内存复制到GPU上
glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);

//8.将顶点数据通过myPrograme中的传递到顶点着色程序的position
//(1)注意:第二参数字符串必须和shaderv.vsh中的输入变量:position保持一致
GLuint position = glGetAttribLocation(self.myPrograme, "position");
//(2).设置合适的格式从buffer里面读取数据
glEnableVertexAttribArray(position);
//(3).设置读取方式
//参数1:index,顶点数据的索引
//参数2:size,每个顶点属性的组件数量,1,2,3,或者4.默认初始值是4.
//参数3:type,数据中的每个组件的类型,常用的有GL_FLOAT,GL_BYTE,GL_SHORT。默认初始值为GL_FLOAT
//参数4:normalized,固定点数据值是否应该归一化,或者直接转换为固定值。(GL_FALSE)
//参数5:stride,连续顶点属性之间的偏移量,默认为0;
//参数6:指定一个指针,指向数组中的第一个顶点属性的第一个组件。默认为0
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL);
//----处理纹理数据-------
//(1).glGetAttribLocation,用来获取vertex attribute的入口的.
//注意:第二参数字符串必须和shaderv.vsh中的输入变量:textCoordinate保持一致
GLuint textCoor = glGetAttribLocation(self.myPrograme, "textCoordinate");
//(2).设置合适的格式从buffer里面读取数据
glEnableVertexAttribArray(textCoor);
//(3).设置读取方式
//参数1:index,顶点数据的索引
//参数2:size,每个顶点属性的组件数量,1,2,3,或者4.默认初始值是4.
//参数3:type,数据中的每个组件的类型,常用的有GL_FLOAT,GL_BYTE,GL_SHORT。默认初始值为GL_FLOAT
//参数4:normalized,固定点数据值是否应该归一化,或者直接转换为固定值。(GL_FALSE)
//参数5:stride,连续顶点属性之间的偏移量,默认为0;
//参数6:指定一个指针,指向数组中的第一个顶点属性的第一个组件。默认为0
glVertexAttribPointer(textCoor, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, (float *)NULL + 3);
//10.加载纹理
[self setupTexture:@"模特切图"];
//11. 设置纹理采样器 sampler2D
glUniform1i(glGetUniformLocation(self.myPrograme, "colorMap"), 0);
//12.绘图
glDrawArrays(GL_TRIANGLES, 0, 6); 
//13.从渲染缓存区显示到屏幕上
[self.myContext presentRenderbuffer:GL_RENDERBUFFER];

为什么会是倒置如何处理该问题将在下文中给出答案。