快乐不是因为得到的多,而是因为计较的少
- Android OpenGLES开发:EGL环境搭建
- Android OpenGLES2.0开发(一):艰难的开始
- Android OpenGLES2.0开发(二):环境搭建
- Android OpenGLES2.0开发(三):绘制一个三角形
- Android OpenGLES2.0开发(四):矩阵变换和相机投影
- Android OpenGLES2.0开发(五):绘制正方形和圆形
- Android OpenGLES2.0开发(六):着色器语言GLSL
- Android OpenGLES2.0开发(七):纹理贴图之显示图片
- Android OpenGLES2.0开发(八):Camera预览
- Android OpenGLES2.0开发(九):图片滤镜
- Android OpenGLES2.0开发(十):FBO离屏渲染
- Android OpenGLES2.0开发(十一):渲染YUV
这篇文章我们来简单介绍下OpenGL着色语言(OpenGL Shading Language)
,我们在前面的文章中都直接使用了顶点着色器和片段着色器代码,但是并没有过多的介绍。一方面是为了文章的流畅性,一方面这块知识需要单独拿出来讲。
一. 概述
着色器
是用来实现图像渲染的,用来替代固定渲染管线的可编程程序。着色器替代了传统的固定渲染管线,可以实现3D图形学计算中的相关计算,由于其可编程性,可以实现各种各样的图像效果而不用受显卡的固定渲染管线限制。这极大的提高了图像的画质。
二. GLSL简介
GLSL(OpenGL Shading Language)是用于编写顶点着色器
和片元着色器
的语言。OpenGL ES的着色器语言GLSL是一种高级的图形化编程语言,其源自应用广泛的C语言。与传统的C语言不同的是,它提供了更加丰富的针对于图像处理的原生类型,诸如向量、矩阵之类。OpenGLES 主要包含以下特性:
- GLSL是一种面向过程的语言,和Java的面向对象是不同的。
- GLSL的基本语法与C/C++基本相同。
- 它完美的支持向量和矩阵操作。
- 它是通过限定符操作来管理输入输出类型的。
- GLSL提供了大量的内置函数来提供丰富的扩展功能。
在渲染图形时,主程序会将顶点数据发送到 GPU,然后 GPU 会使用图形着色器来计算每个像素的最终颜色。图形着色器的输入是顶点数据,输出是像素颜色。
着色器代码和主程序之间的关系就在于着色器代码是在GPU上执行的,主程序是在CPU上执行的。主程序会把数据传给着色器,例如顶点数据,着色器代码就能够处理这些数据,并将结果返回给主程序。
三. Shader简介
OpenGL ES 2.0 对应的 Shader 有两种:
顶点着色器(vertex shader):处理图形中每个顶点的位置。
片元着色器(fragment shader):处理每个像素的颜色和透明度。
Vertex shader
的输入为顶点相关的信息数据,输出分为两部分,必须输出的部分是该顶点最终显示在屏幕上的坐标信息,可选输出的是该顶点对应的其他信息(比如颜色、纹理坐标等)。Vertex shader 一次只能操作一个顶点。
Fragment shader
的输入为屏幕上像素点的信息(坐标以及坐标对应像素从 Vertex Shader 传过来的颜色、纹理坐标等信息)。Fragment shader 不能修改一个像素的位置,在操作一个像素点的时候,也不能访问旁边的像素点。Fragment Shader 通过对顶点信息进行操作,输出每个像素点的颜色。输出值用于更新绘制 buffer 中的 color buffer 或者其他目标 buffer。
Shader 文件看上去其实和一个普通的.c 或者.cpp 文件很像。有预处理,会定义一些变量,有 main 函数,会根据变量进行一些运算,并得到一些结果。
- 在 Vertex shader 中 main 函数中最终得到的结果是顶点坐标 gl_Position
- Fragment shader 结构和 VS (pixel shader)基本相同。在 main 函数中计算得到的最终结果是像素点的颜色值 gl_FragColor
四. GLSL基础
GLSL虽然是基于C/C++的语言,但是它和C/C++还是有很大的不同的,比如在GLSL中没有double
、long
等类型,没有union
、enum
、unsigned
以及位运算等特性。
1.数据类型
GLSL中的数据类型主要分为标量、向量、矩阵、采样器、结构体、数组、空类型七种类型:
- 标量:bool、int、float,标量表示的是只有大小没有方向的量。对于int和C一样,可以写为十进制(16)、八进制(020)或者十六进制(0x10)。对于标量的运算,我们最需要注意的是精度,防止溢出问题。
- 向量:向量我们可以看做是数组,在GLSL通常用于储存
颜色
、坐标
等数据,针对维数,可分为二维、三维和四维向量。针对存储的标量类型,可以分为bool、int和float。共有vec2、vec3、vec4,ivec2、ivec3、ivec4、bvec2、bvec3和bvec4九种类型,数组代表维数、i表示int类型、b表示bool类型。需要注意的是,GLSL中的向量表示竖向量,所以与矩阵相乘进行变换时,矩阵在前,向量在后(与DirectX正好相反)。向量在GPU中由硬件支持运算,比CPU快的多
。- 作为颜色向量时,用rgba表示分量,就如同取数组的中具体数据的索引值。三维颜色向量就用rgb表示分量。比如对于颜色向量vec4 color,color[0]和color.r都表示color向量的第一个值,也就是红色的分量。其他相同。
- 作为位置向量时,用xyzw表示分量,xyz分别表示xyz坐标,w表示向量的模。三维坐标向量为xyz表示分量,二维向量为xy表示分量。
- 作为纹理向量时,用stpq表示分量,三维用stp表示分量,二维用st表示分量。
- 矩阵:在GLSL中矩阵拥有2*2、3*3、4*4三种类型的矩阵,分别用mat2、mat3、mat4表示。我们可以把矩阵看做是一个二维数组,也可以用二维数组下表的方式取里面具体位置的值。
- 采样器:sample2D和sampleCube,在 OpenGL ES 中有一个名词叫做 texture,中文名是纹理贴图。
采样器是专门用来对纹理进行采样工作的
,在GLSL中一般来说,一个采样器变量表示一副或者一套纹理贴图。所谓的纹理贴图可以理解为我们看到的物体上的皮肤。 - 结构体:struct,和C语言中的结构体相同,用struct来定义结构体,关于结构体参考C语言中的结构体。
- 数组:数组知识也和C中相同,不同的是数组声明时可以不指定大小,但是建议在不必要的情况下,还是指定大小的好。
- 空类型:空类型用void表示,仅用来声明不返回任何值得函数。
变量声明示例:
float a=1.0;
int b=1;
bool c=true;
vec2 d=vec2(1.0,2.0);
vec3 e=vec3(1.0,2.0,3.0)
vec4 f=vec4(vec3,1.2);
vec4 g=vec4(0.2); //相当于vec(0.2,0.2,0.2,0.2)
vec4 h=vec4(a,a,1.3,a);
mat2 i=mat2(0.1,0.5,