@qidiandasheng
        
        2020-07-16T08:56:27.000000Z
        字数 5948
        阅读 2156
    音视频
着色器(Shader)是运行在GPU上的小程序。这些小程序为图形渲染管线的某个特定部分而运行。从基本意义上来说,着色器只是一种把输入转化为输出的程序。着色器也是一种非常独立的程序,因为它们之间不能相互通信;它们之间唯一的沟通只有通过输入和输出。
GLSL是OpenGL着色器语言,GLSL是一门特殊的有着类似于C语言的语法,在图形管道(graphic pipeline)中直接可执行的OpenGL着色语言。
最常用到的两种着色器分别为顶点着色器(Vertex Shader)和片元着色器(Fragment Shader)。
什么是顶点呢?
比如你用 OpenGL 画一个三角形,那就是创建了三个顶点。
而顶点着色器就是每个顶点调用一次的程序。
在顶点着色器中,可以访问到顶点的三维位置、颜色、法向量等信息。可以通过修改这些值,或者将其传递到片元着色器中,实现特定的渲染效果。
顶点着色器其目的是设置 gl_Position 变量 -- 这是一个特殊的全局内置变量, 它是用来存储当前顶点的位置。
attribute vec4 position;attribute vec4 inputTextureCoordinate;varying vec2 textureCoordinate;void main(){gl_Position = position;textureCoordinate = inputTextureCoordinate.xy;}
这个 void main() 函数是定义全局gl_Position变量的标准方式. 所有在这个函数里面的代码都会被着色器执行.  如果将3D空间中的位置投射到2D屏幕上这些信息都会保存在计算结果的变量中。
“片元”的概念大家可能相对陌生一些。但是一个相似的概念是“像素”,这你一定听说过。
场景渲染到显示器的过程中,有一个步骤叫光栅化(Rasterization)。由于我们现在的显示器绝大多数是基于像素的(就是由一个个非常小的红绿蓝 LED 组成的显示单元),所以“连续”的三维场景,要显示到“离散”的显示器上,需要经过的变化操作就叫光栅化。
光栅化后得到的就得到了一个个“片元”。片元和像素已经非常接近了,但两者仍是有区别的。用一种通俗的说法来解释的话,就是比如三维空间内有两个从摄像机角度看过去一前一后的三角形,它们重叠部分的显示区域,每个像素对应两个片元;不重叠的部分,像素和片元一一对应。当然,这个例子是我简化过的,真实的对应关系可能更复杂一些。
这里我们并不需要了解片元的概念太深刻,只要知道它是非常接近像素,但是又不等同于像素的就可以了。
同样,片元着色器就是每个片元调用一次的程序。
在片元着色器中,可以访问到片元在二维屏幕上的坐标、深度信息、颜色等信息。通过改变这些值,可以实现特定的渲染效果。
片段 (或者纹理) 着色器 在计算时定义了每像素的 RGBA 颜色 — 每个像素只调用一次片段着色器. 这个着色器的作用是设置 gl_FragColor 变量, 也是一个GLSL内置变量:
varying highp vec2 textureCoordinate;uniform sampler2D inputImageTexture;void main(){gl_FragColor = texture2D(inputImageTexture, textureCoordinate);}
计算结果包含RGBA颜色信息。
attribute 修饰符只存在于顶点着色器中,用于储存每个顶点信息的输入,比如这里定义了 Position 和 TextureCoords ,用于接收顶点的位置和纹理信息。
varying 修饰符指顶点着色器的输出,同时也是片段着色器的输入,要求顶点着色器和片段着色器中都同时声明,并完全一致,则在片段着色器中可以获取到顶点着色器中的数据。
uniform 用来保存传递进来的只读值,该值在顶点着色器和片段着色器中都不会被修改。顶点着色器和片段着色器共享了 uniform 变量的命名空间,uniform 变量在全局区声明,同个 uniform 变量在顶点着色器和片段着色器中都能访问到。
和其他编程语言一样,GLSL有数据类型可以来指定变量的种类。GLSL中包含C等其它语言大部分的默认基础数据类型:int、float、double、uint和bool。GLSL也有两种容器类型,分别是向量(Vector)和矩阵(Matrix)。
GLSL中的向量是一个可以包含有1、2、3或者4个分量的容器,分量的类型可以是前面默认基础类型的任意一个。它们可以是下面的形式(n代表分量的数量):
| 类型 | 含义 | 
|---|---|
| vecn | 包含n个float分量的默认向量 | 
| bvecn | 包含n个bool分量的向量 | 
| ivecn | 包含n个int分量的向量 | 
| uvecn | 包含n个unsigned int分量的向量 | 
| dvecn | 包含n个double分量的向量 | 
大多数时候我们使用vecn,因为float足够满足大多数要求了。
一个向量的分量可以通过vec.x这种方式获取,这里x是指这个向量的第一个分量。你可以分别使用.x、.y、.z和.w来获取它们的第1、2、3、4个分量。GLSL也允许你对颜色使用rgba,或是对纹理坐标使用stpq访问相同的分量。
向量这一数据类型也允许一些有趣而灵活的分量选择方式,叫做重组(Swizzling)。重组允许这样的语法:
vec2 someVec;vec4 differentVec = someVec.xyxx;vec3 anotherVec = differentVec.zyw;vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
你可以使用上面4个字母任意组合来创建一个和原来向量一样长的(同类型)新向量,只要原来向量有那些分量即可;然而,你不允许在一个vec2向量中去获取.z元素。我们也可以把一个向量作为一个参数传给不同的向量构造函数,以减少需求参数的数量:
vec2 vect = vec2(0.5f, 0.7f);vec4 result = vec4(vect, 0.0f, 0.0f);vec4 otherResult = vec4(result.xyz, 1.0f);
方法可以根据纹理坐标,获取对应的颜色信息。
//inputImageTexture:纹理 textureCoordinate:纹理坐标vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
顶点着色器:
attribute vec4 Position;attribute vec2 TextureCoords;varying vec2 TextureCoordsVarying;void main (void) {gl_Position = Position;TextureCoordsVarying = TextureCoords;}
片元着色器:
precision mediump float;uniform sampler2D Texture;varying vec2 TextureCoordsVarying;void main (void) {vec4 mask = texture2D(Texture, TextureCoordsVarying);gl_FragColor = vec4(mask.rgb, 1.0);}
GL_VERTEX_SHADER:顶点着色器,GL_FRAGMENT_SHADER:片元着色器
// 创建一个 shader 对象GLuint shader = glCreateShader(shaderType);// 获取 shader 的内容const char *shaderStringUTF8 = [shaderString UTF8String];int shaderStringLength = (int)[shaderString length];glShaderSource(shader, 1, &shaderStringUTF8, &shaderStringLength);// 编译shaderglCompileShader(shader);
// 根据上一步的编译着色器得到顶点着色器和片元着色器GLuint vertexShader = [self compileShaderWithShaderString:vertexShaderString type:GL_VERTEX_SHADER];GLuint fragmentShader = [self compileShaderWithShaderString:fragmentShaderString type:GL_FRAGMENT_SHADER];// 挂载 shader 到 program 上GLuint program = glCreateProgram();glAttachShader(program, vertexShader);glAttachShader(program, fragmentShader);// 链接 programglLinkProgram(program);// 使用 programglUseProgram(program);
获取着色器中的参数
// 顶点坐标GLuint positionSlot = glGetAttribLocation(program, "Position");// 纹理坐标GLuint textureCoordsSlot = glGetAttribLocation(program, "TextureCoords");// 纹理GLuint textureSlot = glGetUniformLocation(program, "Texture");
将纹理传给着色器
// 激活纹理单元0glActiveTexture(GL_TEXTURE0);// 绑定纹理到纹理单元下的target:GL_TEXTURE_2DglBindTexture(GL_TEXTURE_2D, textureID);// 设置着色器textureSlot的值为纹理单元0glUniform1i(textureSlot, 0);
将顶点坐标数据传给着色器
static const GLfloat vertices[] = {-1.0f, -1.0f, 0.0f,1.0f, -1.0f, 0.0f,-1.0f, 1.0f, 0.0f,1.0f, 1.0f, 0.0f,};glEnableVertexAttribArray(positionSlot);glVertexAttribPointer(positionSlot, 3, GL_FLOAT, 0, 0, vertices);
将纹理坐标数据传给着色器
static const GLfloat textureCoordinates[] = {0.0f, 1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f, 0.0f,};glEnableVertexAttribArray(textureCoordsSlot);glVertexAttribPointer(textureCoordsSlot, 2, GL_FLOAT, 0, 0, textureCoordinates);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
顶点着色器:
attribute vec4 position;attribute vec4 inputTextureCoordinate;varying vec2 textureCoordinate;void main(){gl_Position = position;textureCoordinate = inputTextureCoordinate.xy;}
片元着色器:
varying highp vec2 textureCoordinate;uniform sampler2D inputImageTexture;uniform lowp mat4 colorMatrix;uniform lowp float intensity;void main(){lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);lowp vec4 outputColor = textureColor * colorMatrix;gl_FragColor = (intensity * outputColor) + ((1.0 - intensity) * textureColor);}
position)、纹理坐标(inputTextureCoordinate)gl_Position用来渲染顶点,纹理坐标赋值给textureCoordinate用来传给片元着色器inputImageTexture),颜色变换矩阵(colorMatrix)、强度(intensity)inputImageTexture)和顶点着色器传进来的纹理坐标获对应的纹理颜色:vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);outputColoroutputColor)再跟强度值(intensity)进行计算获得颜色向量赋值给gl_FragColor来渲染纹理
R G B A| 1 0 0 0 | red| 0 1 0 0 | green| 0 0 1 0 | blue| 0 0 0 1 | alphaR G B A| a b c d | red| e f g h | green| i j k l | blue| m n o p | alphaR' = a*R + b*G + c*B + d*AG' = e*R + f*G + g*B + h*AB' = i*R + j*G + k*B + l*AA' = m*R + n*G + o*B + p*A| a b c d | |R| |R'|| e f g h | x |G| = |G'|| i j k l | |B| |B'|| m n o p | |A| |A'|
深褐色滤镜的颜色转换矩阵如下:
{{0.3588, 0.7044, 0.1368, 0.0},{0.2990, 0.5870, 0.1140, 0.0},{0.2392, 0.4696, 0.0912 ,0.0},{0,0,0,1.0},};