OpenGL ES GLSL简介

1、着色器渲染过程

着色器渲染过程

tips:

  • 顶点着色器:只完成了顶点的计算,然后交给下一步进行图元装配和光栅化
  • 光栅化:确定图形在屏幕上显示的像素点
  • 片元着色器:只完成了颜色值的计算,并没有完成颜色填充
  • 顶点着色器和片元着色器是在GPU上并发执行

2、顶点着色器和片元着色器GLSL代码

  • 顶点着色器代码
attribute vec4 position; //attribute属性变量修饰符,vec4是数据类型表示四维向量,顶点坐标
attribute vec2 texCoordinate; //vec2二维向量,纹理坐标是二维纹理(x, y),其中x和y是GLFloat类型
uniform mat4 rotateMatrix; //uniform统一通道修饰符,mat4四行四列的矩阵,旋转矩阵
varying lowp vec2 varyTexCoord; //纹理坐标传递到片元着色器,通过varying通道传递,lowp表示低精度,表示GLFloat的精度
void main() {
    varyTexCoord = texCoordinate; //赋值后传递给片元着色器

    vec4 vPos = position;
    vPos  = vPos * rotateMatrix; //每一个顶点与旋转矩阵相乘得到新的顶点

    gl_Position = vPos; //赋值最终计算好的新顶点,gl_Position是内建变量是四维向量,不需要定义只需要赋值
}
  • 片元着色器代码
varying lowp vec2 varyTexCoord; //从顶点着色器传过来的纹理坐标,需要与顶点着色器的定义完全一致
uniform sampler2D colorMap; //由客户端通过uniform传递过来的纹理采样器,拿到对应的纹理颜色值,取得纹素,uniform传递进来的数据不允许调整,sampler2D是数据类型表示纹理
void main() {
    //texture2D(纹理采样器, 纹理坐标);  内建函数,获取对应位置对应坐标上的颜色值,取得纹素,进行纹理颜色填充
    gl_FragColor = texture2D(colorMap, varyTexCoord); //gl_FragColor内建变量赋值,类型是vec4四维向量(r, g, b, a)
}

3、OpenGL ES 错误处理

  • OpenGL ES 错误指令列表


    OpenGL ES 错误指令列表

4、OpenGL ES版本

OpenGL ES 1.X :针对固定功能流水管线硬件,存储了很多着色器供开发者调用
OpenGL ES 2.X :针对可编程流水管线硬件
OpenGL ES 3.X :OpenGL ES 2.0的扩展

5、GLSL编译与加载

  • 关于GLSL编译

在OpenGL ES中,每个program对象有且仅有一个Vertex Shader对象和一个Fragment Shader对象连接到它。
Shader:类似于C编译器
Program:类似于C链接器
glLinkProgram:操作产生最后的可执行程序,它包含最后可以在硬件上执行的硬件指令。

  • 着色器与程序加载流程
  1. 创建一个顶点着⾊器对象和一个片段着⾊器对象
  2. 将源代码链接到每个着⾊器对象
  3. 编译着⾊器对象
  4. 创建⼀个程序对象
  5. 将编译后的着⾊器对象连接到程序对象
  6. 链接程序对象
  1. 创建Shader
  • 创建Vertex Shader 和 Fragment Shader 文件并编写相应代码;
  • 创建两个shader实例:glCreateShader;
  • 给Shader指定对应代码:glShaderSource;
  • 编译shader代码:glCompileShader。
  1. 创建Program
  • 创建program:glCreateProgram;
  • 将shader绑定到program:glAttachShader(GLuint program, GLuint shader);每个program都必须绑定Vertex Shader和Fragment Shader;
  • 链接program:glLinkProgram(GLuint program);
    - 使用porgram:glUseProgram(GLuint program)。
  • 创建与编译一个着色器
//创建一个着色器,指定其类型(顶点/片元)
GLuint glCreateShader(GLenum type);
/*
type — 创建着⾊器的类型,GL_VERTEX_SHADER 或者GL_FRAGMENT_SHADER
返回值 — 是指向新着⾊器对象的句柄.可以调用glDeleteShader 删除 
*/

//删除着色器
void glDeleteShader(GLuint shader);
/*
shader — 要删除的着⾊器对象句柄
*/

//将GLSL代码链接到shader
void glShaderSource(GLuint shader , GLSizei count ,const GLChar * const *string, const GLint *length);
/*
shader — 指向着⾊器对象的句柄
count — 着⾊器源字符串的数量,着⾊器可以由多个源字符串组成,但是每个着⾊器只有一个main函数 
string — 指向保存数量的count 的着⾊器源字符串的数组指针
length — 指向保存每个着⾊器字符串⼤小且元素数量为count 的整数数组指针.
*/

//编译!!!重要!!!
void glCompileShader(GLuint shader); 
/*
shader — 需要编译的着⾊器对象句柄
*/

//获取编译之后的信息(成功/失败)
void glGetShaderiv(GLuint shader , GLenum pname , GLint *params );
/*
shader — 需要编译的着⾊器对象句柄
pname — 获取的信息参数,可以为 GL_COMPILE_STATUS/GL_DELETE_STATUS/ GL_INFO_LOG_LENGTH/GL_SHADER_SOURCE_LENGTH/ GL_SHADER_TYPE
params — 指向查询结果的整数存储位置的指针.
*/

//获取shader信息
void glGetShaderInfolog(GLuint shader , GLSizei maxLength, GLSizei *length , GLChar *infoLog);
/*
shader — 需要获取信息日志的着⾊器对象句柄
maxLength — 保存信息日志的缓存区⼤小
length — 写⼊的信息日志的长度(减去null 终止符); 如果不需要知道⻓度. 这个参数可以为Null 
infoLog — 指向保存信息日志的字符缓存区的指针.
*/
  • 创建与链接程序
//创建一个空的program
GLUint glCreateProgram();
/*
创建一个程序对象
返回值: 返回一个执⾏新程序对象的句柄
*/

//用完之后删除program
void glDeleteProgram( GLuint program );
/*
program : 指向需要删除的程序对象句柄
*/

//着⾊器与程序连接/附着,顶点着色器和片元着色器最终会链接成一个program
void glAttachShader( GLuint program , GLuint shader );
/*
program : 指向程序对象的句句柄
shader : 指向程序连接的着⾊器对象的句柄
*/

//断开连接,清除着色器
void glDetachShader(GLuint program);
/*
program : 指向程序对象的句句柄
shader : 指向程序断开连接的着⾊色器器对象句句柄
*/

//链接program
glLinkProgram(GLuint program);
/*
program: 指向程序对象句句柄
*/

//链接程序之后, 需要检查链接是否成功. 你可以使⽤glGetProgramiv 检查链接状态
void glGetProgramiv (GLuint program,GLenum pname, GLint *params);
/*
program: 需要获取信息的程序对象句柄 
pname : 获取信息的参数,可以是:
GL_ACTIVE_ATTRIBUTES GL_ACTIVE_ATTRIBUTES_MAX_LENGTH 
GL_ACTIVE_UNIFORM_BLOCK 
GL_ACTIVE_UNIFORM_BLOCK_MAX_LENGTH 
GL_ACTIVE_UNIFROMS GL_ACTIVE_UNIFORM_MAX_LENGTH 
GL_ATTACHED_SHADERS GL_DELETE_STATUS
GL_INFO_LOG_LENGTH
GL_LINK_STATUS GL_PROGRAM_BINARY_RETRIEVABLE_HINT 
GL_TRANSFORM_FEEDBACK_BUFFER_MODE 
GL_TRANSFORM_FEEDBACK_VARYINGS 
GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH GL_VALIDATE_STATUS
params : 指向查询结果整数存储位置的指针
*/

//从程序信息日志中获取信息
void glGetPorgramInfoLog( GLuint program ,GLSizei maxLength, GLSizei *length , GLChar *infoLog );
/*
program : 指向需要获取信息的程序对象句柄
maxLength : 存储信息日志的缓存区⼤小
length : 写⼊的信息日志长度(减去null 终止符),如果不需要知道长度,这个参数可以为Null. 
infoLog : 指向存储信息日志的字符缓存区的指针
*/

//链接program成功后使用program
void glUseProgram(GLuint program);
/*
program: 设置为活动程序的程序对象句柄.
*/
  • 顶点着色器和片元着色器实现编译、绑定和连接的常用流程代码
  1. 指定属性
myIdentityShader = gltLoadShaderPairWithAttributes("ShadedIdentity.vp", "ShadedIdentity.fp", 2, GLT_ATTRITUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_COLOR, "vColor");
  1. gltLoadShaderPairWithAttributes 函数底层实现
GLuint gltLoadShaderPairWithAttributes(const char *szVertexProg, const char *szFragmentProg) {
    //创建2个着色器ID来加载着色器源码
    //临时着色器对象
    GLuint hVertexShader;
    GLuint hFragmentShader;
    GLuint hReturn = 0;
    GLint testVal;

    //创建着色器对象
    hVertexShader = glCreateShader(GL_VERTEX_SHADER);
    hFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

    //将着色器源文件送入着色器对象中
    //顶点着色器加载
    if (gltLoadShaderFile(szVertexProg, hVertexShader) == false) {
        gltDeleteShader(hVertexShader);
        gltDeleteShader(hFragmentShader);

        printf("Not Found Shader");
        return 0;
    }

    //片元着色器加载
    if (gltLoadShaderFile(szFragmentProg, hFragmentShader) == false) {
        gltDeleteShader(hFragmentShader);
        gltDeleteShader(hVertexShader);

        printf("Not Found Shader");
        return 0;
    }

    //编译着色器,然后判断是否有误
    //编译顶点着色器程序和片元着色器程序
    glCompileShader(hVertexShader);
    glCompileShader(hFragmentShader);

    //检查顶点着色器程序错误
    glGetShaderiv(hVertexShader, GL_COMPILE_STATUS, &testVal);
    if (testVal == GL_FALSE) {
        char infolog[1024];
        glGetShaderInfoLog(hVertexShader, 1024, NULL, infolog);
        printf("The shader at %s failed", infolog);
        glDeleteShader(hVertexShader);
        glDeleteShader(hFragmentShader);
        return 0;
    }

    //检查片元着色器程序错误
    glGetShaderiv(hFragmentShader, GL_COMPILE_STATUS, &testVal);
    if (testVal == GL_FALSE) {
        char infolog[1024];
        glGetShaderInfoLog(hFragmentShader, 1024, NULL, infolog);
        printf("The shader at %s failed", infolog);
        glDeleteShader(hVertexShader);
        glDeleteShader(hFragmentShader);
        return 0;
    }

    //进行连接与绑定
    //创建最终的程序对象,连接着色器
    hReturn = glCreateProgram();
    glAttachShader(hReturn, hVertexShader);
    glAttachShader(hReturn, hFragmentShader);

    //将参数名绑定指定的参数位置列表上
    va_list attributeList;
    va_start(attributeList, szFragmentProg);

    //重复迭代参数列表
    char *szNextArg;
    int iArgCount  = va_arg(attributeList, int);
    //循环绑定参数列表
    for (int i = 0; i < iArgCount; i++) {
        int index = va_arg(attributeList, int); //获取属性个数
        szNextArg = va_arg(attributeList, char *); //遍历属性列表
        glBindAttribLocation(hReturn, index, szNextArg); //绑定属性
    }
    va_end(attributeList);

    //连接着色器
    glLinkProgram(hReturn);

    //生成运行文件后可以删除
    glDeleteShader(hVertexShader);
    glDeleteShader(hFragmentShader);

    //确认连接有效
    glGetProgramiv(hReturn, GL_LINK_STATUS, &testVal);
    if (testVal == GL_FALSE) {
        char infolog[1024];
        glGetShaderInfoLog(hReturn, 1024, NULL, infolog);
        prinf("The shader at %s failed", infolog);
        glDeleteProgram(hReturn);
        return 0;
    }
    
    return 0;
}

6、缓冲区

  • FrameBuffer帧缓冲区 FBO FrameBufferObject,管理RenderBuffer,有三个颜色附着点Color Attachment(颜色、纹理)、Depth Attachment(颜色、深度)、Stencil Attachment(模板)

  • RenderBuffer渲染缓冲区,存储缓冲区数据,包含深度缓冲区、模板缓冲区和纹理

FrameBuffer与RenderBuffer的关系
  • Apple关于FrameBuffer和RenderBuffer的官方解释

A renderbuffer object is a 2D image buffer allocated by the application. The renderbuffer can be used to allocate and store color, depth, or stencil values and can be used as a color, depth, or stencil attachment in a framebuffer object. A renderbuffer is similar to an off-screen window system provided drawable surface, such as a pbuffer. A renderbuffer, however, cannot be directly used as a GL texture.
一个renderbuffer 对象是通过应用分配的一个2D图像缓存区。renderbuffer 能够被用来分配和存储颜色、深度或者模板值。也能够在一个framebuffer被用作颜色、深度、模板的附件。一个renderbuffer是一个类似于屏幕窗口系统提供可绘制的表面。⽐如pBuffer。⼀个renderbuffer,它并不能直接的用于⼀个GL 纹理。

A framebuffer object (often referred to as an FBO) is a
collection of color, depth, and stencil buffer attachment points;
state that describes properties such as the size and format of
the color, depth, and stencil buffers attached to the FBO; and
the names of the texture and renderbuffer objects attached to
the FBO. Various 2D images can be attached to the color
attachment point in the framebuffer object. These include a
renderbuffer object that stores color values, a mip-level of a 2D
texture or a cube map face, or even a mip-level of a 2D slice in
a 3D texture. Similarly, various 2D images contain- ing depth
values can be attached to the depth attachment point of an
FBO. These can include a renderbuffer, a mip-level of a 2D
texture or a cubemap face that stores depth values. The only 2D
image that can be attached to the stencil attachment point of
an FBO is a renderbuffer object that stores stencil values.
一个 frameBuffer 对象(通常被称为一个FBO)。是一个收集颜色、深
度和模板缓存区的附着点。描述属性的状态,例如颜⾊、深度和模板
缓存区的⼤小和格式,都关联到FBO(Frame Buffer Object)。并且
纹理的名字和renderBuffer 对象也都是关联于FBO。各种各样的2D
图形能够被附着framebuffer对象的颜色附着点。它们包含了
renderbuffer对象存储的颜色值、一个2D纹理或⽴方体贴图。或者⼀个mip-level的二维切⾯在3D纹理。同样,各种各样的2D图形包含了了当时的深度值可以附加到⼀个FBO的深度附着点中去。唯一的二维图像,能够附着在FBO的模板附着点,是一个renderbuffer对象存储模
板值。

7、OpenGL ES 3种变量修饰符(uniform, attribute, varying)

  1. uniform 外部(客户端,OC代码调用CPU)传递到vertex和fragment(GLSL调用GPU)
  • glUniform*** 提供赋值功能,传递相关数据
  • 类似于const,被uniform修饰的变量不会怎么修改,只能用不能修改,一般用于修饰变换矩阵
    uniform mat4 viewP;修饰四行四列的矩阵
  1. attribute 只能在顶点着色器使用,从外部传递到vertex和fragment
    attribute vec4 position;修饰四维向量的顶点

  2. varying 中间传递者,传递顶点和片元着色器的数据

//顶点着色器shaderv.vsh
attribute vec4 position;
varying vec4 vPosition;

void main() {
    vPosition = position;
}
//片元着色器shaderf.fsh
varying vec4 vPosition;//使用vPosition 

8、GLSL精度限定符解析

GLSL精度
lowp float color;
varying mediump vec2 Coord;
highp mat4 m;
  • 精度范围

Floating Point Range 浮点数范围
highp (-2^62, 2^62);
mediump (-2^14, 2^14);
lowp (-2,2);

Integer Range 整数范围
highp (-2^16, 2^16);
mediump (-2^10, 2^10);
lowp (-28,28);

对于高精度和中精度,整型范围必须可以准确地转化成相应的相同精度修饰符所表示的float型。
例如:highp int 可以被转换成 highp float,mediump int 可以被转换成 mediump float,但是 lowp int 不能转换成相应的 lowp float。
字符常量和布尔型没有精度修饰符,当浮点数和整数构造器不含带有精度修饰符的参数时也不需要精度修饰符。

  • 指定精度
//指定变量精度(放在数据类型之前,修饰数据类型),指定某一个变量
highp vec4 position; 
varying lowp vec4 color; 
mediump float specularExp;

//指定默认精度(放在Vertex Shader 和Fragment Shader 源码开始处),指定全局变量精度
precision precision-qualifier type;
/*
precision:用来确定默认精度修饰符;
precision-qualifier:lowp, mediump, highp, 其他类型和修饰符会引起错误;
*/
/*
如果type是float类型,该精度(precision-qualifier)将适用于所有无精度修饰符的浮点数声明(标量、向量、矩阵);
如果type是int类型,该精度(precision-qualifier)将适用于所有无精度修饰符的整数声明(标量、向量);
全局变量声明、函数返回值声明、函数参数声明和本地变量声明等,没有声明精度修饰符的变量将使用和它最近的precision语句中的精度。
*/
precision highp float; 
precision mediump int;

在Vertex Shader中,如果没有默认精度,则float和int精度都为highp;
在Fragment Shader中,float没有默认的精度,所以必须为float指定一个默认精度或为每个float变量指定精度。

  • 预定义精度
//顶点着色器中预定义的全局默认精度语句
precision highp float; 
precision highp int; 
precision lowp sampler2D; 
precision lowp samplerCube;

//片元着色器中预定义的全局默认精度语句
precision mediump int; 
precision lowp sampler2D; 
precision lowp samplerCube;

片元着色器没有默认的浮点数精度修饰符,对于浮点数、浮点数向量和矩阵变量声明,要么声明必须包含一个精度修饰符,要么默认的精度修饰符在之前已经被声明过了。

你可能感兴趣的:(OpenGL ES GLSL简介)