ssRender中的Shader
ssRenderEdit工具中的Shader与OpenGL的Shader并无本质上的区别,是在OpenGLShader的基础上,对一些变量和方法进行了额外的封装。本篇文章对于想要学习OpenGL Shader 和ssRender工具Shader的同学都会有一定的帮助!
下面,我将会通过一些简单的小案例来分享一下个人对Shader初学的学习理解:
(一)认识Shader(理论)
如图:
打开SSrenderEdit工具,在资源(Resource)窗口下的材质(Material)子窗口中(New) Shader新建Shader着色器
映入眼帘的就是定点着色器(Vertex Shader)与(Fragment Shader),什么作用呢?
Vertex顶点着色器,是渲染的第一步,对每个顶点执行变换操作,计算顶点在不同模型空间的不同转换方式,还可以将后序的渲染数据传递给下一个着色器(就是Fragment片元着色器),决定着一个图元在屏幕中最终的显示位置。
Fragment片元着色器,接收顶点着色器传递过来的渲染数据,计算片元最终的颜色值和深度值。像我们常用的纹理采样、颜色汇总等就是Fragment片元着色器来计算的。它决定了一块片元的显示效果。
显而易见,Fragment着色器的执行次数远大于Vertex着色器,所以我们可以通过一定的算法优化,减少Fragment的调用次数以优化工程的渲染效率。
在正式介绍Shader的使用方法前,先提前说明一下Shader的关键字和需要注意的情况
①uniform:统一值,由于GPU的线程并行,且各个线程之间无数据交互与通信,所以所有线程的某些输入值要统一,且只读。一般在定义完精度之后就进行声明。它可以在vertex和fragment共享使用。
②varying:变化值,就像 GLSL 有个默认输出值 vec4 gl_FragColor
一样,它也有一个默认输入值( vec4 gl_FragCoord
)。gl_FragCoord
存储了活动线程正在处理的像素或屏幕碎片的坐标。有了它我们就知道了屏幕上的哪一个线程正在运转。为什么我们不声明 gl_FragCoord
为 uniform (统一值)呢?因为每个像素的坐标都不同,所以把它叫做 varying(变化值)。
③attribute:只能在vertex 中使用的变量(不能在fragment shader中声明attribute变量,也不能被fragment shader中使用)一般用attribute变量来表示一些顶点的数据,如:顶点坐标,法线,纹理坐标,顶点颜色等。
④void main:main
函数,无需多言,应该都知道是什么含义以及作用吧
⑤precision mediump float:精密度为中间浮点。这个比较重要,它影响了渲染效率,更低的精度会有更快的渲染速度,但是会以牺牲渲染质量为代价。你可以选择每一个浮点值的精度。(precision mediump float;
)就是设定了所有的浮点值都是中等精度。但我们也可以选择把这个值设为“低”(precision lowp float;
)或者“高”(precision highp float;
)。
⑥vec2、vec3、vec4:有n个分量的float型vectors(n 纬向量,数字值代表纬度),最高纬度就是4纬,是Shader中常用的变量,它们的初始化方法将会在文章后半部分的Shader应用实例中详细介绍。
⑦ivec*与bvec*:(*代表维度值,只能是2/3/4)代表int型的vector与bool型的vector,类比vec即可理解。
⑧mat*:(*代表维度值,只能是2/3/4)n乘n大小的浮点型矩阵。
注意①:GLSL 语言规范并不保证变量会被自动转换类别。就是数字1代表着int型的数字,并不支持自动转换为float型的1.0,千万要注意使用上的规范,要养成在float型数据中加一个 . 的好习惯!
接下来就是介绍ssRenderEdit中的Shader使用详解啦:
1.Vertex Shader内容详解
attribute vec3 ssVertexPosition:
定义了一个三维float型的向量,代表了顶点的位置。
attribute vec2 ssVertexUV:
定义了一个二维float型向量,代表顶点的纹理坐标(UV坐标),等价于Shader中的(gl_FragCoord.xy
/ u_resolution
,对坐标进行了规范化)
。
uniform mat4 ssMVP:
定义了一个4x4的矩阵的统一值,表示模型-视图-投影矩阵。
这个矩阵通常用于将顶点从模型空间转换到裁剪空间。
varying vec2 ssUV:
定义了一个变化值,上方介绍过,attribute定义的值只能在VertexShader中使用,而varying可用于Vertex Shader和Fragment Shader之间的传值。在这里,它用于向Fragment中传递纹理坐标。
gl_Position = ssMVP * vec4(ssVertexPosition, 1.0):
计算变换后的顶点位置,将模型-视图-投影矩阵(ssMVP)乘以顶点位置向量ssVertexPosition。gl_Position,表示顶点的裁剪空间坐标。
ssRenderEdit工具里Vertx中自带的代码基本无需改动,理解其大意即可,接下来介绍Fragment Shader中封装好的代码
2.Fragment Shader内容详解
precision mediump float:上面有过详细介绍,不再赘述
varying vec2 ssUV:接收从Vertex Shader中传递的同名(变化值)纹理坐标(UV坐标)。
uniform vec4 ssColor:统一值四纬变量,代表颜色。包含红(R)、绿(G)、蓝(B)和透明度(A)四个分量。
uniform float ssOpacity:统一值Float型变量,代表透明度。
uniform float ssTime:统一值Float型变量,代表运行时间,与Shader中的u_time用法一致。
gl_FragColor = vec4(color.rgb, ssOpacity * color.a): color.rgb 代表上方ssColor的红绿蓝变量,ssOpacity * color.a 代表透明度成绩。gl_FragColor 决定了最终显示效果。
ssRenderEdit工具中封装好的Vertex Shader和Fragment Shader就介绍到这了,下面将会通过几个实例来加深对Shader的理解:
(二)使用Shader(实践)
本篇文章举一些2D的实例:
第一步:
首先在SSRenderEdit工具中创建一个Item节点用来展示我们的最终效果:
第二步:在材质(Material)窗口下创建新材质,选择编写好的Shader,填写材质名称
第三步:
将新建的材质拖拽到Item节点下的Material的Source属性上,就能在页面中显示我们编写的Shader效果了!
1.最最最基础的Shader
此案例可以称为Shader里的"Hello World",看完上面的理论部分,并且Shader有着类似C语言的特征,这个实例应该很通俗易懂!
这里讲一下vec*(*代表纬度值,只能是2,3,4)的初始化方法,ivec*和bvec*的初始化方法与之类似:
(1)为各个分量提供标量值以供初始化:
例如vec2 avec2 = vec2(1.0 , 0.0);
vec4 avec4 = vec4(1.0 , 0.8 , 0.6 , 0.4);
(2)提供一个标量值,应用于各个分量的初始化:
例如vec2 bvec2 = vec2(1.0); 等价于 vec2 bvec2 = vec2(1.0 , 1.0); vec3、vec4同理
(3)提供其他向量与标量值的组合进行初始化(要注意分量个数上的对应)
例如 ①vec3 avec3 = vec3(1.0 , 0.5, 0.0);
vec2 bvec2 = vec2(avec3);
等价于vec2 bvec2 = vec2(avec3.x , avec3.y);
等价于vec2 bvec2 = vec2(1.0 , 0.5);
(更高维度的向量将会顺序的分配给低维度的向量)
②vec3 cvec3 = vec3(bvec2.yyx);
等价于vec3(bvec2.y , bvec2.y , bvec2.x);
(有没有很神奇的赶脚?赋值方式跟传统的set函数很像,但是又有些许不一样)
效果展示:
上述的Shader让我们得到了一个红色的小方块
我们也可以在此基础上加一些小小的,有关于ssTime(与u_time类似,记录系统运行时间)的三角函数,接下来请看
2.加了三角函数的Shader
对Red和Green分量上加上了sin函数和cos函数,他们的参数是ssTime(同u_time,代表着程序运行的时间),Blue设置为固定值0.5,透明度设为1.0。
这样,我们就得到了一个随时间规律变化的粉绿色块(如下图)
怎么样,是不是感觉越来越好玩,越来越感兴趣了呢?接下来上点难度,跟紧了!
3.变化的红色圆环
uv纹理坐标取值范围[0,1],所以将它*2 -1可以将纹理坐标范围调整到[-1,1],正中心的坐标就是[0,0];
vec3 col = vec3(1.00,0.00,0.00);应该是很熟悉的语法吧,就是将col染成红色;
就是将直角坐标转换为极坐标,length()函数计算极坐标的半径
atan()函数就是arctan(uv.y/uv.x)计算极坐标角度
col *= step(d,0.8);将半径从0到0.8范围的圆染色,得到了一个红色的圆:
col *= 1.0 - step(d,0.7);将半径小于0.7的圆除去染色,得到了一个宽度为0.1的圆环:
col *= step(sin(ssTime)*3.14,angle);这句话应该不会陌生了吧,极坐标的角度范围是[-π,π],sin()函数的取值为[-1,1],这样我们就能得到最终的效果,一个随时间变换的红色圆环啦:
4.转动十字
得到的效果如下:
解析:我虽然只是一个可能连入门都算不上的Shader菜鸟,这一段shader也是我从网上找的资料,但我还是想要发表一下我的一些观点:在我仔细看过这段shader代码,经过长时间的思考后:知道勾股定理吗?知道万有引力吗?其实跟这段Shader代码并没有什么关系!我其实也不知道这段代码是如何构思并实现的,正如我一开始说的:我只是一个可能连入门都算不上的Shader菜鸟。
小结:本篇文章是本人在使用ssRenderEdit工具中的Shader内容时的学习理解,并且编写了几个简短的Shader代码对一些小效果进行了实现,接下来本人会继续分享对Shader的理解,并且会继续深度探索ssRenderEdit工具其他实用功能!