1. 先来明确几个概念
1.1 OpenGL
OpenGL
全称为 Open Graphics Library
(开放图形库)。
是用于渲染 2D 或 3D 图像的跨语言跨平台的应用程序编程接口,用于CPU
控制GPU
做图像渲染,是一套API
。
提供设计人员一个共同的硬件驱动标准,让开发者不必为每一品牌的硬件来写不同的驱动程序。
1.1.1 历史
- 1980年代,开发可以用在各种各样图形硬件上的软件是个真正的挑战。通常,软件开发人员为每种硬件编写自定义的接口和驱动程序。但这非常昂贵并会导致大量工作的重复。
- 20世纪90年代初,SGI成为工作站3D图形领域的领导者。其IRISGL的API被认为是最先进的科技并成为事实上的行业标准,而基于开放标准的PHIGS则相形见绌。IRIS GL更容易使用,而且还支持即时模式的渲染。相比之下,PHIGS难于使用并且功能老旧。
- SGI的竞争对手(包括Sun、惠普和IBM)通过扩展PHIGS标准也能将3D硬件投入市场。这反过来导致SGI市场份额的削弱,因为有越来越多的3D图形硬件供应商进入市场。为攻占市场,SGI决定把IRIS GL API转变为一项开放标准,即OpenGL。
- 1992年,SGI公司领导了OpenGL架构审查委员会(OpenGL ARB)的创建。该委员会由若干公司组成,负责未来OpenGL规范的维护和扩展。
- 微软在1995年发布Direct3D,Direct 3D最终成为OpenGL的主要竞争对手
- 2002年微软的DirectX 9提出了全新的Shader绘图功能以及高端着色语言(HLSL),OpenGL霸主地位开始被瓦解。这使得OpenGL了解到必须开发全新的OpenGL 2.0版本,加入支持GLSL的功能。
- 2008年推出OpenGL 3,2010年3月10日, OpenGL同时推出了3.3和4.0版本,同年7月26日又发布了4.1版本。2011年8月8日发布4.2版本。2013年发布4.3版。
1.1.2 Direct3D
另一种程序接口系统是仅用于Microsoft Windows上的Direct3D
DirectX
是由很多API
组成的,Direct3D
是directX
中的一个功能,主要负责3D
效果的显示。
1.1.3 CPU、OpenGL/DirectX、显卡驱动和GPU之间的关系
OpenGL
和DirectX
这些图像应用编程接口,用于渲染二维或三维图形。
可以说,这些接口架起了上层应用程序和底层GPU
的沟通桥梁。
一个应用程序向这些接口发送渲染命令,而这些接口会依次向显卡驱动(Graphics Driver
)发送渲染命令,这些显卡驱动是真正知道如何和GPU
通信的角色,正是它们把OpenGL
或者DirectX
的函数调用翻译成了GPU
能够听懂的语言,同时它们也负责把纹理等数据转换成GPU
所支持的格式。
1.2 OpenGL ES
OpenGL ES
全称为 OpenGL for Embedded Systems
(嵌入式系统开放图形库)。
OpenGL ES 是 OpenGL 的子集,主要针对**嵌入式系统(设备)**设计,去除了 Open GL
中非必要的特性。
1.3 GLSL
GLSL
全称为 OpenGL Shading Language
(OpenGL
着色语言),它是一种 C 语言的变体,是一款在 OpenGL
着色器(Shader
)中使用的编程语言,在GPU
上执行。
在渲染图形时,主程序会将顶点等数据发送到 GPU
,然后 GPU
会使用图形着色器来计算每个像素的最终颜色。图形着色器的输入是顶点数据,输出是像素颜色。
1.4 GLSL ES
GLSL ES
全称为 OpenGL ES Shading Language
(OpenGL ES
着色语言),是在 OpenGL ES
着色器(Shader
)中使用的编程语言。
2.几种着色器类型
在2004年,固定管线中只有两个部分能够被替代,顶点处理单元和片段处理单元。这两个可编程处理单元被称为顶点着色器和片段着色器。之后又增加了两个新的部分:几何着色器和计算着色器,这两个新的部分分别是2008年和2012年加入到官方的OpenGL
规范当中。
OpenGL 3.0
中有四种着色器
-
顶点着色器(
Vertex Shader
) -
片段着色器(
Fragment Shader
-
几何着色器(
Geometry Shader
) -
计算着色器(
Compute Shader
)
OpenGL 2.0
中只有顶点着色器和片段着色器
- 顶点着色器(Vertex Shader):用于处理顶点(
Vertex
)变换,即图形上每个顶点的变换(旋转、平移、缩放),主要作用是指定形状。 - 片段着色器(Fragment Shader):用于处理片段(
Fragment
)变换,即图形上每个像素点的颜色计算和填充,主要作用是指定颜色。
在OpenGL ES 2.0
中,顶点着色器和片段着色器是相互独立的,分别由不同的着色器程序实现。着色器程序是OpenGL ES 2.0
中的核心计算单元,负责管理图形上每个顶点和像素的变换,以及对每个像素进行颜色计算和填充。
3. GLSL的修饰符与基本数据类型
GLSL
的语法与C
语言非常类似,对于GLSL
,其数据类型表示具体如下
3.1 修饰符
-
const
:用于声明非可写的编译时常量变量。 -
attribute
:用于经常更改的信息,只能在顶点着色器中使用。 -
uniform
:用于不经常更改的信息,可用于顶点着色器和片元着色器。 -
varying
:用于修饰从顶点着色器向片元着色器传递的变量。
3.2 基本数据类型
int、float、bool
,这些与C
语言都是一致的。
需要强调的一点就是,这里面的float
是有一个修饰符的,即可以指定精度。三种修饰符的范围(范围一般视显卡而定)和应用情况具体如下。
highp
:32bit
,一般用于顶点坐标(vertex Coordinate
)。medium
:16bit
,一般用于纹理坐标(texture Coordinate
)。lowp
:8bit
,一般用于颜色表示(color
)。
3.3 向量类型
向量类型是Shader
中非常重要的一个数据类型,因为在做数据传递的时候需要经常传递多个参数,相较于写多个基本数据类型,使用向量类型是非常好的选择。
vec2
: 是一个二维向量容器,表示一个包含两个浮点数的向量vec3
: 是一个三维向量容器,表示一个包含三个浮点数的向量vec4
: 是一个四维向量容器,表示一个包含四个浮点数的向量
列举一个最经典的例子,要将物体坐标和纹理坐标传递到Vertex Shader
中,用的就是向量类型,每一个顶点都是一个四维向量,在Vertex Shader
中利用这两个四维向量即可完成自己的纹理坐标映射操作。声明方式如下:
attribute vec4 position;
3.4 矩阵类型
矩阵类型在Shader的语法中也是一个非常重要的类型,有一些效果需要开发者传入矩阵类型的数据。声明方式如下
uniform lowp mat4 colorMatrix;
上面的代码表示了一个4×4的浮点矩阵,如果是mat2就是2×2的浮点矩阵,如果是mat3就是3×3的浮点矩阵。
若要传递一个矩阵到实际的Shader中,则可以直接调用如下函数(客户端代码):
glUniformMatrix4fv(mColorMatrixLocation, 1, false, mColorMatrix);
3.5 纹理类型
一般仅在片段着色器(Fragment Shader)中使用这个类型,二维纹理的声明方式如下 :
uniform sampler2D texSampler;
当客户端接收到这个句柄时,就可以为它绑定一个纹理,代码如下(客户端代码):
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texId);
glUniform1i(mGLUniformTexture, 0);
注意上述代码中第一行激活的是哪一个纹理句柄,第三行代码中的第二个参数需要传递对应的Index
,就像代码中激活的纹理句柄是GL_TEXTURE0
,对应的Index
就是0
,如果激活的纹理句柄是GL_TEXTURE1
,那么对应的Index
就是1
,在不同的平台上句柄的个数也不一样,但是一般都会在32
个以上。
3.6 传递类型
在GLSL
中有一个特殊的修饰符就是varying
,这个修饰符修饰的变量均用于在Vertex Shader
和Fragment Shader
之间传递参数。
首先在顶点着色器中声明这个类型的变量代表纹理的坐标点,并且对这个变量进行赋值
attribute vec2 texcoord;
varying vec2 v_texcoord;
void main(void)
{
// 计算顶点坐标
v_texcoord = texcoord;
}
紧接着在Fragment Shader中也声明同名的变量,然后使用texture2D方法取出二维纹理中该纹理坐标点上的纹理像素值
varying vec2 v_texcoord;
vec4 texel = texture2D(texSampler, v_texcoord);
取出了该坐标点上的像素值之后,就可以进行像素变化操作了,比如说提高对比度,最终将改变的像素值赋值给gl_FragColor
。
3.7 GLSL的内置函数与内置变量
3.7.1 顶点着色器的内置变量
vec4 gl_position;
上述代码用来设置顶点转换到屏幕坐标的位置,Vertex Shader一定要去更新这个数值。另外还有一个内置变量,代码如下
float gl_pointSize;
在粒子效果的场景下,需要为粒子设置大小,改变该内置变量的值就是为了设置每一个粒子矩形的大小。
3.7.2 片段着色器的内置变量
vec4 gl_FragColor;
上述代码用于指定当前纹理坐标所代表的像素点的最终颜色值。
4. 实现的滤镜效果
原图
温暖
precision mediump float;
uniform sampler2D uTextureSampler;
varying vec2 vTextureCoord;
const highp vec3 WARM = vec3(0.1, 0.1, 0.0);
void main()
{
vec4 tempColor = texture2D(uTextureSampler, vTextureCoord);
gl_FragColor = tempColor+vec4(WARM,0.0);
}
寒冷
precision mediump float;
uniform sampler2D uTextureSampler;
varying vec2 vTextureCoord;
const highp vec3 COOL = vec3(0.0, 0.0, 0.1);
void main()
{
vec4 tempColor = texture2D(uTextureSampler, vTextureCoord);
gl_FragColor = tempColor+vec4(COOL,0.0);
}
底片
precision mediump float;
uniform sampler2D uTextureSampler;
varying vec2 vTextureCoord;
void main()
{
vec4 tempColor = texture2D(uTextureSampler, vTextureCoord);
gl_FragColor = vec4((1.0 - tempColor.rgb), tempColor.w);
}
旧照
precision mediump float;
uniform sampler2D uTextureSampler;
varying vec2 vTextureCoord;
const lowp float intensityone = 1.0;
const lowp mat4 colorMatrix = mat4(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);
void main()
{
vec4 tempColor = texture2D(uTextureSampler, vTextureCoord);
lowp vec4 outputColor = tempColor * colorMatrix;
gl_FragColor = (intensityone * outputColor) + ((1.0 - intensityone) * tempColor);
}
梦幻
precision mediump float;
uniform sampler2D uTextureSampler;
varying vec2 vTextureCoord;
void main() {
vec4 textureColor = texture2D(uTextureSampler, vTextureCoord);
float redCurveValue = texture2D(uTextureSampler, vec2(textureColor.r, 0.0)).r;
float greenCurveValue = texture2D(uTextureSampler, vec2(textureColor.g, 0.0)).g;
float blueCurveValue = texture2D(uTextureSampler, vec2(textureColor.b, 0.0)).b;
gl_FragColor = vec4(redCurveValue, greenCurveValue, blueCurveValue, textureColor.a);
}
浮雕
precision mediump float;
uniform sampler2D uTextureSampler;
varying vec2 vTextureCoord;
const vec2 texSize = vec2(1920,1080);
void main() {
vec4 textureColor = texture2D(uTextureSampler, vTextureCoord);
vec2 tex = vTextureCoord;
vec2 upLeftUV = vec2(tex.x - 1.0/texSize.x, tex.y - 1.0/texSize.y);
vec4 upLeftColor = texture2D(uTextureSampler,upLeftUV);
vec4 delColor = textureColor - upLeftColor;
float h = 0.3*delColor.x + 0.59*delColor.y + 0.11*delColor.z;
vec4 bkColor = vec4(0.5, 0.5, 0.5, 1.0);
gl_FragColor = vec4(h,h,h,0.0) + bkColor;
}
黑白
precision mediump float;
uniform sampler2D uTextureSampler;
varying vec2 vTextureCoord;
void main()
{
vec4 tempColor = texture2D(uTextureSampler, vTextureCoord);
// Get the grayscale value of each pixel
float luminance = tempColor.r * 0.299 + tempColor.g * 0.584 + tempColor.b * 0.114;
gl_FragColor = vec4(vec3(luminance), tempColor.a);
}
5. 参考
一张图搞懂CPU、OpenGL/DirectX、显卡驱动和GPU之间的关系_opengl和gpu的关系_王 炸的博客-CSDN博客
GLSL基础概念(绝对看得懂)_我想要身体健康的博客-CSDN博客
GLSL基础(上)(OpenGL Shading Language) - 知乎 (zhihu.com)
GLSL简介 - 哔哩哔哩 (bilibili.com)
OpenGL(一) OpenGL入门 - 简书 (jianshu.com)
GLSL - 百度百科
《音视频开发进阶指南》