OpenGL ES入门02-OpenGL ES着色器

前言

本文是关于OpenGL ES的系统性学习过程,记录了自己在学习OpenGL ES时的收获。
这篇文章的目标是学习OpenGL ES着色器语言。
环境是Xcode8.1+OpenGL ES 2.0
目前代码已经放到github上面,OpenGL ES入门02-OpenGL ES着色器

欢迎关注我的 OpenGL ES入门专题

概述

着色器(Shader)是运行在GPU上的小程序。这些小程序为图形渲染管线的某个特定部分而运行。从基本意义上来说,着色器只是一种把输入转化为输出的程序。着色器也是一种非常独立的程序,因为它们之间不能相互通信;它们之间唯一的沟通只有通过输入和输出。

效果

OpenGL ES入门02-OpenGL ES着色器_第1张图片
三角形绘制

着色器语言

着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。

着色器分类

顶点着色器 是一个可编程的处理单元,执行顶点变换、纹理坐标变换、光照、材质等顶点的相关操作,每顶点执行一次。替代了传统渲染管线中顶点变换、光照以及纹理坐标的处理。

attribute vec3 position;
attribute vec3 color;
varying vec3 outColor;

void main()
{
    gl_Position = vec4(position, 1.0);
    outColor = color;
}

片元着色器 是一个处理片元值及其相关联数据的可编程单元,片元着色器可执行纹理的访问、颜色的汇总、雾化等操作,每片元执行一次。

precision mediump float;
varying vec3 outColor;
void main()
{
    gl_FragColor = vec4(outColor, 1.0);
}

数据类型

类型 说明
float 浮点型
bool 布尔型
int 整形
vec2 包含了2个浮点数的向量
vec3 包含了3个浮点数的向量
vec4 包含了4个浮点数的向量
ivec2 包含了2个整数的向量
ivec3 包含了3个整数的向量
ivec4 包含了4个整数的向量
bvec2 包含了2个布尔数的向量
bvec3 包含了3个布尔数的向量
bvec4 包含了4个布尔数的向量
mat2 2*2维矩阵
mat3 3*3维矩阵
mat4 4*4维矩阵
sampler1D 1D纹理采样器
sampler2D 2D纹理采样器
sampler3D 3D纹理采样器
samplerCube Cube纹理采样器

常量

const 可以用来修饰任何基本数据类型。通常const变量在声明的同时要进行初始化,结构体字段不能使用const修饰吗,但是变量可以,并通过构造器进行初始化。包含数组的数组和结构体不能声明为常量,因为它们不能被初始化。

onst vec4 color = vec4 (1.0, 1.0, 1.0, 1.0);

存储修饰符

attribute 变量(属性变量)只能用于顶点着色器中,不能用于片元着色器。 一般用该变量来表示一些顶点数据,如:顶点坐标、纹理坐标、颜色等。

uniforms 是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。

samplers 一种特殊的 uniform,用于呈现纹理。sampler 可用于顶点着色器和片元着色器。

varying 变量(易变变量)是从顶点着色器传递到片元着色器的数据变量。顶点着色器可以使用易变变量来传递需要插值的颜色、法向量、纹理坐标等任意值。 在顶点与片元shader程序间传递数据是很容易的,一般在顶点shader中修改varying变量值,然后片元shader中使用该值,当然,该变量在顶点及片元这两段shader程序中声明必须是一致的 。例如:上面代码中应用程序中由顶点着色器传入片元着色器中的vColor变量。

精度修饰符

OpenGL ES入门02-OpenGL ES着色器_第2张图片
精度修饰符

precision 可以用来确定默认精度修饰符。类型可以是int或float或采样器类型,precision-qualifier可以是lowp, mediump, 或者highp。任何其他类型和修饰符都会引起错误。如果type是float类型,那么该精度(precision-qualifier)将适用于所有无精度修饰符的浮点数声明(标量,向量,矩阵)。如果type是int类型,那么该精度(precision-qualifier)将适用于所有无精度修饰符的整型数声明(标量,向量)。包括全局变量声明,函数返回值声明,函数参数声明,和本地变量声明等。没有声明精度修饰符的变量将使用和它最近的precision语句中的精度。

precision highp float;
precision highp int;
precision lowp sampler2D;
precision lowp samplerCube;

内建变量

gl_Position 顶点着色器内建变量,表示变换后点的空间位置。 顶点着色器从应用程序中获得原始的顶点位置数据,这些原始的顶点数据在顶点着色器中经过平移、旋转、缩放等数学变换后,生成新的顶点位置。新的顶点位置通过在顶点着色器中写入gl_Position传递到渲染管线的后继阶段继续处理。

*** gl_PointSize*** 顶点着色器内置变量,设置栅格化点的直径,也就是点的大小,通常用于点精灵,粒子等绘制。

gl_FragColor 片元着色器内置变量,用来保存片元着色器计算完成的片元颜色值,此颜色值将送入渲染管线的后继阶段进行处理。

加载着色器

static GLuint createGLShader(const char *shaderText, GLenum shaderType)
{
    GLuint shader = glCreateShader(shaderType);
    glShaderSource(shader, 1, &shaderText, NULL);
    glCompileShader(shader);
    
    int compiled = 0;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    if (!compiled) {
        GLint infoLen = 0;
        glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 1) {
            char *infoLog = (char *)malloc(sizeof(char) * infoLen);
            if (infoLog) {
                glGetShaderInfoLog (shader, infoLen, NULL, infoLog);
                GLlog("Error compiling shader: %s\n", infoLog);
                free(infoLog);
            }
        }
        glDeleteShader(shader);
        return 0;
    }
    return shader;
}

1、创建着色器,通过glCreateShader创建着色器,type为着色器的类型GL_VERTEX_SHADER 和 GL_FRAGMENT_SHADER

glCreateShader (GLenum type) //type: GL_VERTEX_SHADER 或者 GL_FRAGMENT_SHADER

2、添加着色器源程序,将着色器源码关联到一个着色器对象shader上。string是一个有count行GLchar类型的字符串组成的数组,用来表示着色器的源代码数据。string可以以NULL结尾,也可以不是。如果length为NULL则string给出的每行都是以NULL结尾,否则length中必须有count个表示string长度的元素。(也就是说字符串以NULL结尾我们不用指定长度,否则必须制定每行的长度)

glShaderSource (GLuint shader, GLsizei count, const GLchar* const *string, const GLint* length)

3、编译着色器源程序

glCompileShader (GLuint shader)

4、删除着色器

glDeleteShader (GLuint shader)

5、容错处理,通过glGetShaderiv获取编译状态,通过glGetShaderInfoLog获取错误信息。

#define GL_COMPILE_STATUS                                0x8B81
#define GL_INFO_LOG_LENGTH                               0x8B84
glGetShaderiv (GLuint shader, GLenum pname, GLint* params) 
glGetShaderInfoLog (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog)

创建着色器程序

GLuint createGLProgram(const char *vertext, const char *frag)
{
    GLuint program = glCreateProgram();
    
    GLuint vertShader = createGLShader(vertext, GL_VERTEX_SHADER);
    GLuint fragShader = createGLShader(frag, GL_FRAGMENT_SHADER);
    
    if (vertShader == 0 || fragShader == 0) {
        return 0;
    }
    
    glAttachShader(program, vertShader);
    glAttachShader(program, fragShader);
    
    glLinkProgram(program);
    GLint success;
    glGetProgramiv(program, GL_LINK_STATUS, &success);
    if (!success) {
        GLint infoLen;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 1) {
            GLchar *infoText = (GLchar *)malloc(sizeof(GLchar)*infoLen + 1);
            if (infoText) {
                memset(infoText, 0x00, sizeof(GLchar)*infoLen + 1);
                glGetProgramInfoLog(program, infoLen, NULL, infoText);
                GLlog("%s", infoText);
                free(infoText);
            }
        }
        glDeleteShader(vertShader);
        glDeleteShader(fragShader);
        glDeleteProgram(program);
        return 0;
    }
    
    glDetachShader(program, vertShader);
    glDetachShader(program, fragShader);
    glDeleteShader(vertShader);
    glDeleteShader(fragShader);
  
    return program;
}

1、创建着色器程序

 glCreateProgram (void)

2、装配着色器

glAttachShader (GLuint program, GLuint shader)

3、链接着色器程序

glLinkProgram (GLuint program)

4、卸载着色器程序

glDetachShader (GLuint program, GLuint shader)

5、使用着色器程序

 glUseProgram (GLuint program)

6、容错处理

#define GL_LINK_STATUS                                   0x8B82
#define GL_INFO_LOG_LENGTH                               0x8B84
glGetProgramiv (GLuint program, GLenum pname, GLint* params)
glGetProgramInfoLog (GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog)

三角形绘制

- (void)setupGLProgram
{
    NSString *vertFile = [[NSBundle mainBundle] pathForResource:@"vert.glsl" ofType:nil];
    NSString *fragFile = [[NSBundle mainBundle] pathForResource:@"frag.glsl" ofType:nil];
    _program = createGLProgramFromFile(vertFile.UTF8String, fragFile.UTF8String);
    
    glUseProgram(_program);
}

- (void)setupVertexData
{
   // 需要加static关键字,否则数据传输存在问题 
   static GLfloat vertices[] = {
        0.0f,  0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f
    };
    GLint posSlot = glGetAttribLocation(_program, "position");
    glVertexAttribPointer(posSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(posSlot);
    
   //颜色数据
    static GLfloat colors[] = {
        0.0f, 1.0f, 1.0f,
        1.0f, 0.0f, 1.0f,
        1.0f, 1.0f, 0.0f
    };
    GLint colorSlot = glGetAttribLocation(_program, "color");
    glVertexAttribPointer(colorSlot, 3, GL_FLOAT, GL_FALSE, 0, colors);
    glEnableVertexAttribArray(colorSlot);
}

- (void)render
{
    glClearColor(1.0, 1.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);

// 在这里初始化数据,可以加static关键字,也可以不加
//    GLfloat vertices[] = {
//         0.0f,  0.5f, 0.0f,
//        -0.5f, -0.5f, 0.0f,
//         0.5f, -0.5f, 0.0f
//    };
//    GLint posSlot = glGetAttribLocation(_program, "position");
//    glVertexAttribPointer(posSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
//    glEnableVertexAttribArray(posSlot);
//    
//    GLfloat colors[] = {
//        0.0f, 1.0f, 0.0f,
//        0.0f, 1.0f, 0.0f,
//        0.0f, 1.0f, 0.0f
//    };
//    GLint colorSlot = glGetAttribLocation(_program, "color");
//    glVertexAttribPointer(colorSlot, 3, GL_FLOAT, GL_FALSE, 0, colors);
//    glEnableVertexAttribArray(colorSlot);
    
    [self setupVertexData];
    
    glDrawArrays(GL_TRIANGLES, 0, 3);
    
    //将指定 renderbuffer 呈现在屏幕上,在这里我们指定的是前面已经绑定为当前 renderbuffer 的那个,在 renderbuffer 可以被呈现之前,必须调用renderbufferStorage:fromDrawable: 为之分配存储空间。
    [_context presentRenderbuffer:GL_RENDERBUFFER];
}
glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
indx 指定要修改的顶点着色器中顶点变量id;
size 指定每个顶点属性的组件数量。必须为1、2、3或者4。
type 指定数组中每个组件的数据类型。可用的符号常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值为GL_FLOAT;
normalized 指定当被访问时,固定点数据值是否应该被归一化(GL_TRUE)或者直接转换为固定点值(GL_FALSE);
stride 指定连续顶点属性之间的偏移量。如果为0,那么顶点属性会被理解为:它们是紧密排列在一起的。初始值为0;
ptr 顶点数据指针。

参考链接

https://www.khronos.org/opengles/sdk/docs/reference_cards/OpenGL-ES-2_0-Reference-card.pdf

http://www.cnblogs.com/kesalin/archive/2012/11/25/opengl_es_tutorial_02.html

你可能感兴趣的:(OpenGL ES入门02-OpenGL ES着色器)