看着群里的小伙伴们都很热衷于Shader的编写,麒麟子刚好做到了这个需求:焦点对象高亮、受击变色、霸体、特殊技能角色周身氛围增强,所以在此分享一下Cocos Creator 3D版本下,Shader的编写。
在此也提醒一下各位小萌新,Shader的编写于图形引擎而言,就好比SQL的编写于数据库一样。
如果不去理解数据库存储原理,快照方式,查询机制。是写不出好SQL的。
Shader编写也是如此,我们应该对图形渲染管线、图形API接口特性、常见的光照算法、向量运算可利用的特点等加以掌握,才能写出符合需求且不损效率的好Shader.
推荐大家学习《WebGL编程指南》、《3D游戏中的数学基础》这两本书,想白嫖的可以私信我
1、添加一个可以在Inspector面板操作的shader变量
2、添加一个vs到fs传递的变量
3、法线转换
4、点乘的妙用
5、让fs根据自己的意愿进行颜色输出
5、完整的边缘光实现
坐稳了,开始给油了。
群里有不少朋友在问,为什么自己的材质没办法改颜色,改贴图。这个问题虽然比较简单,但对于刚接触3D的朋友来说,不容易找到方法,在这里就科谱一下。
我们常说的Shader其实不仅包含了GLSL代码,还包含了一些渲染状态,和参数管理。因此引擎一般不会直接叫Shader,比如Cocos和Unity3D把它叫Effect,也有一些引擎叫FX。
要制作一个复杂点的Effect,我们一般不去新建effect,而是直接复制builtin-standard来修改。先简单介绍一下Cocos Creator 3D的Effect文件。 《更详细的介绍在这里》
每一个Effect由多个techniques组成,每一个technique由多个passes组成。每个effect同一时间只会激活一个technique。一个technique里面会有多个pass,通常情况下只会有一个。但我们有时候也会做一些特殊处理,这个时候就会用到多个pass了。
properties:&props用于定义Inspector面板上可操作的参数。我们可以定义向量、颜色、贴图、整数。在我们平时写Shader的时候,想添加什么样的类型,复制一份来改就行了。
边缘光和雾化效果一样,是性价比超级高的表现手法。本文开头的图,大家应该已经看到了。它可以用于游戏中各类情景。有某些场合,我们还能够利用边缘光增加物体与场景融入感。
边缘光的原理非常简单,模型上的三角面,与我们摄像机正对着的,我们认为它的边缘光因子0,与我们摄像机垂直的,我们认为它的边缘光因子为1。
而在Shader中,我们要判断一个面是否与我们的摄像机正对着,最简单的就是使用顶点法线。 我们利用视线反方向与法线方向的点乘值来判断。
因为点乘值 = cos(向量夹角)。 我们根据cos值的值域可以发现, 当夹角为0的时候,法线和摄像机正对,此时cos值为1,反之,当夹角为90度的时候,法线和摄相机垂直,此时cos值为0。刚好与我们要的结果相反。最终我们的公式如下。
float fRim = 1.0 - dot(-cameraDir,normal);
最后,将这个因子用于物体颜色和边缘光颜色的插值,即可得到我们想要的结果。
边缘光有两个属性:1、颜色 2:强度。
正好我们可以用一个vec4来作为边缘光参数。 rgb作为颜色,a作为强度。
接下来我们说一下如何添加边缘光。
1、properties:&props新增一个边缘光属性,使我们能够通过Inspector面板和代码控制边缘光参数
rimLightColor: { value: [1.0, 1.0, 1.0, 1.0], target: rimColor, editor: { displayName: Rim Color, type: color } }
rimLightColor:是属性定义,名称叫啥并无实际意义,但麒麟子做的时候,发现不能和rimColor相同,否则属性面板上出不来。
target:rimColor:表示这个值要和下面的rimColor uniform绑定。
editor: { displayName: Rim Color, type: color }:表示在面板上显示为Rim Color,类型是颜色类型
2、在下方CCProgram shared-ubos 中的 uniform Constants 域添加一个 vec4 rimColor 常量。
到此,我们要添加的外部变量就设置好。我们既可以通过Inspector面板调节rimColor参数,也可以在代码中使用setProperties来设置参数。
如果添加成功,它应该在下图所示这个位置
3、在vs中转换法线到摄相机空间,并传递给fs
shader编写的时候,我们最重要的就是要考虑清楚,在哪一个空间计算是最方便的。在这个例子中,麒麟子决定在摄像机空间进行计算,在摄像机空间进行计算,可以以 引擎的默认方向(0,0,-1)作为摄像机方向。(因为所有物体已经被转换到这个空间中后,摄像机被视为无旋转、无缩放、无平移的)
我们找到out vec3 v_normal,并在下方添加一个 out vec3 v_view_normal;
将法线计算部分改为如下语句:
vec4 normal = vec4(In.normal,0.0);
v_normal = normalize((matWorldIT * normal).xyz);
v_view_normal = normalize(((cc_matView * matWorldIT) * normal).xyz);
v_view_normal就是我们要的摄像机空间的法线方向。
4、在fs中计算边缘光因子
在standard-fs部分,in vec3 v_normal;下面添加in vec3 v_view_normal;
在return CCFragOutput(color);之前 #endif 之后添加下面两条语句
float fRim = (1.0 - dot(normalize(v_view_normal),vec3(0,0,1.0))) * rimColor.w;
color.rgb = mix(color.rgb,rimColor.rgb,fRim);
到此,就边缘光添加完毕。
如果要实现本文图1的边缘光因子检查。只需要改为color.rgb = mix(vec3(0.0,0.0,0.0),rimColor.rgb,fRim);即可
边缘光虽然是一种很简单的实现,但它巧妙地利用了视线与法线的关系。从而达到了我们意想不到的效果。这就是科学的力量。
当然,我们也能明显感受到,边缘光对立方体等表面不平滑的物体效果并不好,用的时候要注意场合。
如果觉得光看文章不够清楚的朋友,麒麟子还准备了本项目完整源码。欢迎查看。
https://gitee.com/qilinzi/qfw
喜欢我,就关注我,后面的文章更精彩。