OpenGL之着色器

着色器语言(GLSL)

  着色器(Shader)是在GPU上运行程序。这些小程序为图形渲染管线的某个特定部分而运行。着色器程序只是一种把输入转化为输出的程序,也是一种非常独立的程序,着色器之间不能通信,只能通过输入和输出进行数据传递

  着色器是一个程序,想要着色器按照需要进行处理数据需要使用GLSL脚本语言调用着色器。

    着色器语言的主要实现的功能是:
        1、位置变化
        2、法线变化
        3、纹理坐标的产生和计算
        4、颜色计算

  GLSL是一种类C的语言,典型的格式如下:

    #version version_number
    in type in_variable_name;
    in type in_variable_name;
    out type out_variable_name;
    uniform type uniform_name;
    int main(){
      out_variable_name = weird_stuff_we_processed;
    }

  开头使用version声明版本,然后就是声明输入或输出的变量以及uninfrom和main函数,着色器的入口函数也是main,在函数中处理输入函数,并将需要输出的数据写入到输出变量中,而uninfrom数据是从应用程序向着色器发送数据的方式,uniform是全局的,uninfrom数据在每个着色器中的对象都是独一无二的。

基本变量类型

类型说明
void空类型
bool布尔类型
int整数类型
float浮点型
bvec2两个布尔成分的向量
bvec3三个布尔成分的向量
bvec4四个布尔成分的向量
ivec2两个整型成分的向量
ivec3三个整型成分的向量
ivec4四个整型成分的向量
mat2mat2x22×2的浮点数矩阵类型
mat3mat3x33×3的浮点数矩阵类型
mat4x44×4的浮点矩阵
mat2x32X3的浮点矩阵
mat2x42X4的浮点矩阵
mat3x23X2的浮点矩阵
mat3x43X4的浮点矩阵
mat4x24X2的浮点矩阵
mat4x34X3的浮点矩阵
sampler1D用于内建的纹理函数中引用指定的1D纹理的句柄。
只可以作为一致变量或者函数参数使用
sampler2D二维纹理句柄
sampler3D三维纹理句柄
samplerCubecube map 纹理句柄
sampler1DShadow一维深度纹理句柄
sampler2DShadow二维深度纹理句柄

向量中的成员选择

  向量中单独的成分可以通过{x,y,z,w},{r,g,b,a}或者{s,t,p,q}的记法来表示。这些不同的记法用于顶点,颜色,纹理坐标。在成员选择中,不可以混合使用这些记法。其中{s,t,p,q}中的p替换了纹理的r坐标,因为与颜色r重复了。
  也可以通过使用下标来访问向量或矩阵中的元素。如果越界那行为将是未定义的。在矩阵中,可以通过一维的下标来获得该列的向量(OpenGL的矩阵是列主顺序的)。二维的小标来获得向量中的元素。

结构体类型

  结构体可以组合基本类型和数组来形成用户自定义的类型。在定义一个结构体的同时,你可以定义一个结构体实例。或者后面再定义。

		struct A {
		float a;
		vec3 b;
		float c;
		} MyStruct;  //定义时同时创建实例
		A MyStruct;	 //定义后创建实例

  结构体至少包含一个成员。固定大小的数组也可以被包含在结构体中。GLSL的结构体不支持嵌套定义。只有预先声明的结构体可以嵌套其中。

	struct myStruct {
  		vec3 a[3];//固定大小的数组是合法的
  		A b;  
  		struct s { 
    			vec3 v;
  		} c;
  		s vv; //不合法,没有预先声明;};struct subSurface {  int id;
	};

  可以使用=为结构体赋值,或者使用 ==,!=来判断两个结构体是否相等。

	      mySurface = A;
	      mySurface == A;

只有结构体中的每个成分都相等,那么这两个结构体才是相等的。
访问结构体的内部成员使用. 来访问。

		vec3 color = mySurface.a+ secondSurface.color;

数组

  再GLSL中只能使用一维的数组。数组的元素类型只能是基本数据类型或者结构体

	MyStruct a[];
	vec4 lightPositions[8];
	vec4 lightPos[] = lightPositions;
	const int num = 5;
	MyStruct buf[num];
	float[5] values;

  指定显示大小的数组可以作为函数的参数或者使返回值,也可以作为结构体的成员.数组类型内建了一个length()函数,可以返回数组的长度。

	lightPositions.length() //返回数组的大小 8

  数组的下标从0开始。合理的范围是0到 .length() - 1。跟C语言一样,如果数组访问越界了,那行为是未定义的。如果着色器的编译器在编译时知道数组访问越界了,就会提示编译失败。

构造函数

  构造函数用于初始化包含多个成员的变量,包括数组和结构体。

  构造函数也可以用在表达式中。调用方式如下:

		vec3 myNormal = vec3(1.0, 1.0, 1.0);
		greenTint = myColor + vec3(0.0, 1.0, 0.0);
		ivec4 myColor = ivec4(255);

  还可以使用混合标量和向量的方式来构造,只要你的元素足以填满该向量。

		vec4 color = vec4(1.0, vec2(0.0, 1.0), 1.0);
		vec3 v = vec3(1.0, 10.0, 1.0);
		vec3 v1 = vec3(v);
		vec2 fv = vec2(5.0, 6.0);
		float f = float(fv); //用x值2.5构造,y值被舍弃

  对于矩阵,OpenGL中矩阵是列主顺序的。如果只传了一个值,则会构造成对角矩阵,其余的元素为0.

		mat3 m3 = mat3(1.0);
		/*
		构造出来的矩阵式:
		1.0 0.0 0.0
		0.0 1.0 0.0
		0.0 0.0 1.0
		*/
		mat2 matrix = mat2(1.0, 0.0, 0.0, 1.0);
		mat2 matrix = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0));
		mat2 matrix = mat2(1.0); 
		mat2 matrix = mat2(mat4(2.0)); //会取 4×4矩阵左上角的2×2矩阵。

  构造函数可以用于标量数据类型的转换。GLSL不支持隐式或显示的转换,只能通过构造函数来转。
  int转为float值是一样的,float转为int则小数部分被丢弃。
  int或float转为bool,0和0.0转为false,其余的值转为true,
  bool转为int或float,false值转为0和0.0,true转为1和1.0.

		float f = 1.7;
		int I = int(f); // I = 1

  数组的初始化,可以在构造函数中传入值来初始化数组中对应的每一个值。

		ivec2 position[3] = ivec2[3]((0,0), (1,1), (2,2));
		ivec2 pos2[3] = ivec2[]((3,3), (2,1), (3,1));

构造函数也可以对结构体进行初始化。其中顺序和类型要一一对应。

		struct mystruct{ 
			int  index;
 			vec3 color; 
		 	float rotate;
		};
		mystruct myStruct = mystruct(3, vec3(red, green, blue), 0.5);

修饰符

GLSL中也有类似于 const static这样的修饰符

修饰符描述
const常量值必须在声明是初始化。它是只读的不可修改的。
attribute表示只读的顶点数据,只用在顶点着色器中。
数据来自当前的顶点状态或者顶点数组。
它必须是全局范围声明的,不能再函数内部。
attribute可以是浮点数类型的标量,向量,或者矩阵。不可以是数组或则结构体
uniform一致变量。
在着色器执行期间一致变量的值是不变的。与const常量不同的是,这个值在编译时期是未知的是由着色器外部初始化的。
一致变量在顶点着色器和片段着色器之间是共享的。它也只能在全局范围进行声明。
varying顶点着色器的输出。例如颜色或者纹理坐标,(插值后的数据)作为片段着色器的只读输入数据。必须是全局范围声明的全局变量。可以是浮点数类型的标量,向量,矩阵。不能是数组或者结构体。
centorid varying在没有多重采样的情况下,与varying是一样的意思。
在多重采样时,centorid varying在光栅化的图形内部进行求值而不是在片段中心的固定位置求值。
invariant(不变量)用于表示顶点着色器的输出和任何匹配片段着色器的输入,在不同的着色器中计算产生的值必须是一致的。所有的数据流和控制流,写入一个invariant变量的是一致的。编译器为了保证结果是完全一致的,需要放弃那些可能会导致不一致值的潜在的优化。除非必要,不要使用这个修饰符。在多通道渲染中避免z-fighting可能会使用到。
in用在函数的参数中,表示这个参数是输入的,在函数中改变这个值,并不会影响对调用的函数产生副作用。(相当于C语言的传值),这个是函数参数默认的修饰符
out用在函数的参数中,表示该参数是输出参数,值是会改变的。
inout用在函数的参数,表示这个参数即是输入参数也是输出参数。

运算符

  GLSL语言的操作符与C语言相似。只有以下运算符(操作符的优先级从高到低排列)

操作符描述
()用于表达式组合,函数调用,构造
[]数组下标,向量或矩阵的选择器
.结构体和向量的成员选择
++ –前缀或后缀的自增自减操作符
+ – !一元操作符,表示正 负 逻辑非
* /乘 除操作符
+ -二元操作符 表示加 减操作
<> <= >= == !=小于,大于,小于等于, 大于等于,等于,不等于 判断符
&&^^ 逻辑与 ,或, 异或
?:条件判断符
= += –= *= /=赋值操作符
,表示序列

函数

  在每个着色器中都要有一个 main函数,main函数的参数可以自定义,但是返回值必须是void。
  GLSL中的函数,必须是在全局范围定义和声明的。不能在函数定义中声明或定义函数。函数必须有返回类型,参数是可选的。参数的修饰符(in, out, inout, const等)是可选的。
  结构体和数组也可以作为函数的参数。如果是数组作为函数的参数,则必须制定其大小。在调用传参时,只传数组名就可以了。
  GLSL的函数是支持重载的。函数可以同名但其参数类型或者参数个数不同即可。
  递归函数是不被允许的。
  GLSL中提供了许多内建的函数。http://www.opengl.org/sdk/docs/man/

顶点着色器(Vertex Shader)

  顶点着色器分为输入和输出两部分,负责的功能是把输入的数据进行矩阵变换位置,计算光照公式生成逐顶点颜⾊,⽣成/变换纹理坐标.并且把位置和纹理坐标这样的参数发送到片段着色器.

内置变量

  内置变量可以与固定函数功能进行交互,在使用前不需要声明。
  顶点着色器可用的内置变量如下表:

名称类型描述
gl_Colorvec4输入属性-表示顶点的主颜色
gl_SecondaryColorvec4输入属性-表示顶点的辅助颜色
gl_Normalvec3输入属性-表示顶点的法线值
gl_Vertexvec4输入属性-表示物体空间的顶点位置
gl_MultiTexCoordnvec4输入属性-表示顶点的第n个纹理的坐标
gl_FogCoordfloat输入属性-表示顶点的雾坐标
gl_Positionvec4输出属性-变换后的顶点的位置,用于后面的固定的裁剪等操作。所有的顶点着色器都必须写这个值。
gl_ClipVertexvec4输出坐标,用于用户裁剪平面的裁剪
gl_PointSizefloat点的大小
gl_FrontColorvec4正面的主颜色的varying输出
gl_BackColorvec4背面主颜色的varying输出
gl_FrontSecondaryColorvec4正面的辅助颜色的varying输出
gl_BackSecondaryColorvec4背面的辅助颜色的varying输出
gl_TexCoord[]vec4纹理坐标的数组varying输出
gl_FogFragCoordfloat雾坐标的varying输出

  绘制的3D

片段着色器(Fragment Shader)

  片元着色器的作用是处理由光栅化阶段生成的每个片元,最终计算出每个像素的最终颜色。归根结底,实际上就是数据的集合。这个数据集合包含每一个像素的各个颜色分量和像素透明度的值。

内置变量

名称类型描述
gl_Colorvec4输入属性-表示顶点的主颜色
gl_SecondaryColorvec4输入属性-表示顶点的辅助颜色
gl_Normalvec3输入属性-表示顶点的法线值
gl_Vertexvec4输入属性-表示物体空间的顶点位置
gl_MultiTexCoordnvec4输入属性-表示顶点的第n个纹理的坐标
gl_FogCoordfloat输入属性-表示顶点的雾坐标
gl_Positionvec4输出属性-变换后的顶点的位置,用于后面的固定的裁剪等操作。所有的顶点着色器都必须写这个值。
gl_ClipVertexvec4输出坐标,用于用户裁剪平面的裁剪
gl_PointSizefloat点的大小
gl_FrontColorvec4正面的主颜色的varying输出
gl_BackColorvec4背面主颜色的varying输出
gl_FrontSecondaryColorvec4正面的辅助颜色的varying输出
gl_BackSecondaryColorvec4背面的辅助颜色的varying输出
gl_TexCoord[]vec4纹理坐标的数组varying输出
gl_FogFragCoordfloat雾坐标的varying输出

几何着色器

  OpenGL 3.2及更新的版本支持几何着色器,一般情况下只使用了顶点和片段着色器,这也是基本和必须的两个着色器,而几何着色器是一个可选的着色器,其位于顶点和片段着色器之间。几何着色器接收来自顶点着色器的一个片元的一组顶点,通过高效的几何运算,对其进行变换,可以输出新的不同类型的片元,也可以增加顶点数,将数据输出到片段着色器。使用几何着色器可以实现很多特殊的效果
  几何着色器做的事情也可以在CPU中实现,不过通过几何着色器,你只需要传递很少的数据到显卡,后面的事情就交给GPU做了,减少了CPU的负载、内存到显卡数据的传输压力,同时也提升了程序运行效率(GPU运算更快)。

  为了可移植性,最新的WebGL和OpenGL ES标准不在支持几何着色器,开发移动应用和web应用请不要使用几何着色器。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值