用Unity怎样做unity ugui边缘流光光的效果?

unity中shader制作流光效果_中华文本库
第1页/共10页
在unity中使用shader制作流光效果
本文转自—蓝鸥Unity培训流光,关键在于流动。
从画法的角度看,流光效果的成本很低,一张流光图,一张过滤图,一个渲染遍,即可实现效果。
但是效果很令人印象深刻。
你跟随着这篇东西去一步步操作,就能得到一些实际可用的Shader。
在开始写一个简单的流光效果之前,我们先普及一些shader的知识
这是一个新建的shader,我只是添加了一些注释
第1页/共10页
寻找更多 ""Unity Shader编程之-模拟Highlighting System给物体边缘加高光轮廓的实现 - 简书
Unity Shader编程之-模拟Highlighting System给物体边缘加高光轮廓的实现
用过Highlighting System插件的童鞋都知道,该款可以给物体轮廓外发光高亮特效,那么本篇文章我们来动手学习下自己实现,下面开始正文。
1.边缘光方法(Rim Light):Unity官方教程里有例子,其中核心是这两句代码:
half rim = 1.0 – saturate(dot (normalize(IN.viewDir), IN.worldNormal));o.Emission = _RimColor.rgb * pow (rim, _RimPower);IN.viewDir是当前视角向量,IN.worldNormal是物体的法线。dot是计算视角和法线的点积,等于视角和法线夹角的cos值,如下图:
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
Cos的值域是1-0,1-cos就成了0-1,在夹角90度时达到最大值,正好用来模拟侧光的强度(与视角成90度的部分光线最强,就是边缘光了)把这个值的变化率用一个pow函数(rim的_rimPower次方)进行放大,就能强化边缘发亮的效果。比较一下:
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
没有经过Pow放大变化率的边缘光,cos函数的变化是比较平缓的,造成大片区域被染色。
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
经过Pow函数放大变化率,就有了边缘发亮的效果。这个图大致体现了放大前后变化率的曲线:
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
这种边缘光在复杂几何形体的时候效果还是不错的。
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
但是在平直的物体上,边缘光就不见了
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
到了方形物体,几乎很难看见了
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
很好理解,正方形每个面法线都是一个方向的,没法体现出变化和轮廓了。另外这种方法在描绘凹的几何体时,凹的部分(包括法线贴图造成的凹凸)的边缘也都会被画出来,并不是真正意义上的边缘轮廓,就是一种侧光效果。该方法的好处是简单,只要把官方的shader改写,加上计算边缘光的几句,就能实现。要显示的时候动态切换shader就可以。基本不需要代码干预,效率高。
2.单个物体轮廓渲染方法这个方法实现比较复杂,只能介绍大致思路:1.把要渲染轮廓的物体放在一个单独的层里2.在层里设置一个disable的摄像机,culling mask是渲染物体所在层。3.主摄像机保持culling mask是everything4.生成1个renderTexture5.把那个disable的辅助摄像机用RenderWithShader方法,指定一个单色渲染的shader(只需要轮廓,不需要光照计算)渲染物体的轮廓到一个renderTexture6.继续用单色Shader,用Unity自带Blur类似的方法,把物体轮廓图上下左右移动几个像素,叠加在一起,得到一个比原来轮廓大,边缘模糊的轮廓图,存到一个临时renderTexture7.把大的轮廓图和原始轮廓图叠加,把中间清晰轮廓部分消除掉,就能得到一个完整带透明度的轮廓图。8.在主摄像机的OnRenderImage里,把这个透明轮廓图和主摄像机渲染的图像进行alpha混合,就能产生一个完整并且不被遮挡的轮廓效果了。效果如图:
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
明显效果比侧光好多了。不过这种方法开销比较大,而且需要很多代码的支持。
附上一个小Demo,包含了上述两种方法。鼠标可以控制镜头平移、旋转、缩放。鼠标划过物体显示侧光效果,点击物体显示清晰轮廓效果,再次点击效果消除。
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
可以设置边缘颜色 和宽度 模糊度目标按照材质进行分组以方便处理
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
变换成细线 绿色
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
———————————————————————————————————————优化了代码,减少了一组RenderTexture,这个东西还是很耗资源的,减少了计算的次数。另外增加了一组模糊、裁剪一次完成的shader,资源上更节省,不过边缘较模糊。两种算法效果比较:
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
多次模糊再裁剪的算法,边缘光滑,模型的锯齿在边缘线被光滑了,开销略大
[unity Shader 着色器]给物体边缘加高光轮廓的办法,付Demo(增加一组算法) - 踏浪星空 - 踏浪的编程小屋
一次同时模糊、裁剪算法,边缘较模糊,锯齿也被等比放大,但开销较省。根据需要使用。总体上因为是全屏效果,与被渲染物体数量无关,所以基本上显示轮廓物体的多少不会影响效率。附件里包含两种方法的代码和shader。RimLight方法一个shader就能对应一种材质效果,不需要代码支持。后一种方法需要代码加shader,类似官方的Image Effect效果。
下面是下载地址
密码: vasp
VR设计云课堂(微信号:Bauhaus_design)——全宇宙最好玩的VR知识分享计划正式启动啦!专为VR、AR、MR、AI等领域爱好者提供系统的学习资源,零基础学习相关知识,润物细无声,让你不知不觉沉浸于引领未来的人机交互革命之中。
转自:http://m.blog.csdn.net/puppet_master/article/details/ 简介 写了两篇简单光照模型的shader的文章,虽然Unity自带的shader就有diffuse和specular,效果还比自己写的好,然而要...
Unity shader 官网文档全方位学习(一)What?? Shader,看起来好高级的样子,是的,这是Unity中高级进阶的必备。因此,兄弟我就在此记下我学习官网的一些心得。此为一。主要介绍些Surface Shaders的知识。具体的大家也可去官网(如下)学习。ht...
什么是Shader Shader(着色器)是一段能够针对3D对象进行操作、并被GPU所执行的程序。Shader并不是一个统一的标准,不同的图形接口的Shader并不相同。OpenGL的着色语言是GLSL, NVidia开发了Cg,而微软的Direct3D使用高级着色器语言(...
游戏里常出现的一种需求,就是鼠标触碰到角色或物体时高亮。正在学习unity的shader,看官方的shader示例,其中有一个就是Rim Lighting,但是官方示例那个模型并没有把这个效果凸显出来,然后查了下Rim Lighting,看到了这个例子,才明白是物体边缘高亮...
转载注明出处:点击打开链接 Shader(着色器)是一段能够针对3D对象进行操作、并被GPU所执行的程序。Shader并不是一个统一的标准,不同的图形接口的Shader并不相同。OpenGL的着色语言是GLSL, NVidia开发了Cg,而微软的Direct3D使用高级着色...
在这个自由和开放的年代,越来越多的年轻人遇见爱情,往往是一见钟情和一往情深,然后不能自拔就闪婚。就连迪士尼动画《冰雪奇缘》里的公主安娜就是对于一个异国的王子一见钟情就马上想闪婚,还好被她的姐姐艾尔莎阻止了。闪婚,的确很浪漫,也很有效率。在电光火石间,已经将自己变为人夫人妇了...
你活在我的梦里 很美好 一切关于你的幻想 很美好
王天林是20世纪,香港电影最有影响力的导演之一,被称为香港影坛教父。同时,他是香港著名导演王晶的父亲,也是导演杜琪峰启蒙老师。 1928年,王天林出生于上海,成年后的王天林在剧组打工,由杂工、助理、编导,22岁正式担任导演,导演了第一部作品《峨眉飞剑侠》,当年在香港引起了很...
能够有幸参加班委会是非常荣幸非常激动的事儿,嗯,在我的生命当中,曾经有一段刻骨铭心的历程就是教练技术,走过那个旅程终生难忘,现在参加易效能重新出发,期待更深的挖掘自己,道术器用全面武装,创造自己要的生活。 林剑教练提出一个问题:各位来此作甚?我自己也想想到底什么出发点赶到这...很多游戏Logo中可以看到这种流光效果,一般的实现方案就是对带有光条的图片uv根据时间进行移动,然后和原图就行叠加实现,不过实现过程中稍稍有点需要注意的地方。之前考虑过的实现方式,但是考虑到shader中太多的计算,还是放弃了。
Shader &UICustom/ImageFlashEffect2&
Properties
_MainTex (&Main Texture&, 2D) = &white& {}
_LightTex (&Light Texture&, 2D) = &white& {}
_LightColor(&Light Color&,Color) = (1,1,1,1)
_LightPower(&Light Power&,Range(0,5)) = 1
//每次持续时间,受Angle和Scale影响
_LightDuration(&Light Duration&,Range(0,10)) = 1
//时间间隔,受Angle和Scale影响
_LightInterval(&Light Interval&,Range(0,20)) = 3
&Queue&=&Transparent&
&IgnoreProjector&=&True&
&RenderType&=&Transparent&
Lighting Off
ZWrite Off
Fog { Mode Off }
Offset -1, -1
Blend SrcAlpha OneMinusSrcAlpha
AlphaTest Greater 0.1
#pragma vertex vert
#pragma fragment frag
#include &UnityCG.cginc&
struct appdata
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
struct v2f
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float2 lightuv : TEXCOORD1;
sampler2D _MainT
float4 _MainTex_ST;
sampler2D _LightT
_LightTex_ST;
half _LightI
half _LightD
half4 _LightC
half _LightP
half _LightOffSetX ;
half _LightOffSetY ;
v2f vert (appdata v)
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
fixed currentTimePassed = fmod(_Time.y,_LightInterval);
//uv offset, Sprite wrap mode need &Clamp&
fixed offsetX = currentTimePassed / _LightD
fixed offsetY = currentTimePassed / _LightDuration;
offset.x = offsetX - 0.5f;
offset.y = offsetY - 0.5f;
o.lightuv = v.uv
= TRANSFORM_TEX(v.uv, _MainTex);
= TRANSFORM_TEX(o.lightuv, _LightTex);
fixed4 frag (v2f i) : SV_Target
fixed4 mainCol
= tex2D(_MainTex, i.uv);
fixed4 lightCol
= tex2D(_LightTex, i.lightuv);
lightCol *= _LightC
//need blend
//lightCol.rgb *= mainCol.
fixed4 fininalC
fininalCol.rgb
= mainCol.rgb +
lightCol.rgb * _LightP
fininalCol.a =
mainCol.a * lightCol.
return fininalC
需要注意的点:
时间间隔问题
fixed currentTimePassed = fmod(_Time.y,_LightInterval);
//uv offset, Sprite wrap mode need &Clamp&
fixed offsetX = currentTimePassed / _LightD
fixed offsetY = currentTimePassed / _LightDuration;
offsetX 、offsetY其实是0~_LightInterval的数值,需要设置图片为光线图片的Wrap模式为Clamp,才能实现时间间隔控制
Pixel着色器中frag()函数是通过原始颜色和光线颜色叠加的方式实现的,也有将光线颜色和原图混合后再叠加的做法,这个我觉得看实际应用。注意alpha的控制
1. 增加角度和大小控制
基本版本的实现中光线只能根据流光图片中亮线的宽度和方向决定实际的滚动方向和大小。有时候如果需要经常调节方向和大小,可以考虑加入相关因素。流光的Uv是通过原图当前Uv加上时间轴参数获得,可以考虑通过修改流光uv的计算方式来实现。如下面代码。不过这种方式增加了一定计算量,不需要的话则直接跳过。还有一点就是流光贴图必须是垂直或者水平。
float2 base = v.
base.x -= _LightOffSetX ;
base.y -= _LightOffSetX ;
base = base / _LightS
float2 base2 = v.
= base.x * cosInRad - base.y * sinInR
= base.y * cosInRad + base.x * sinInR
o.lightuv = base2 +
只是想不同的图片都是使用相同的材质,应该保证每个图片的效果都可以独立进行修改保存,可以考虑每张图片都创建一份材质,然后修改其参数。
void UpdateParam()
if (Material == null)
Debug.LogWarning(&Metarial is miss&);
if (mGraphic == null)
mGraphic = GetComponent&MaskableGraphic&();
if (mGraphic is Text)
Debug.LogError(&FlashEffec need component type of Image、RawImage&);
if (mDynaMaterial == null)
mDynaMaterial = new Material(Material);
mDynaMaterial.name = mDynaMaterial.name + &(Copy)&;
mDynaMaterial.hideFlags = HideFlags.DontSave | HideFlags.NotE
if (mDynaMaterial == null)
mDynaMaterial.mainTexture =
if (OverrideTexture != null)
mDynaMaterial.mainTexture = OverrideT
if (mGraphic is RawImage)
RawImage img = mGraphic as RawI
img.texture =
else if (mGraphic is Image)
Image img = mGraphic as I
img.sprite =
mDynaMaterial.mainTexture = mGraphic.mainT
if (Duration & Interval)
Debug.LogWarning(&ImageFlashEffect.UpdateParam:Duration need less Interval&);
Interval = Duration + 0.5f;
mDynaMaterial.SetColor(&_LightColor&, Color);
mDynaMaterial.SetFloat(&_LightPower&, Power);
mDynaMaterial.SetFloat(&_LightScale&, Scale);
mDynaMaterial.SetFloat(&_LightAngle&, Angle);
mDynaMaterial.SetFloat(&_LightDuration&, Duration);
mDynaMaterial.SetFloat(&_LightInterval&, Interval);
mDynaMaterial.SetFloat(&_LightOffSetX&, OffSet);
mGraphic.material = mDynaM
mGraphic.SetMaterialDirty();
阅读(...) 评论()coding can not change the world, but I will create my own world!
Unity Shader-边缘光(RimLight)效果
写了两篇简单光照模型的shader的文章,虽然Unity自带的shader就有diffuse和specular,效果还比自己写的好,然而要想学好shader,基础还是很重要的。不然到网上到处找shader,扔到项目里,能用就好,完全不看性能的话,迟早会出事的。今天不看光照模型了,物理渲染还没搞懂,所以只好先来个简单的shader玩一玩。正好最近在和某基友玩黑魂,这货一出来总是自带一个特效-边缘光。于是本人强迫症发作,决定研究一下这个怎么实现。
RimLight--边缘发光效果,是一个比较常用的效果,实现简单,在普通的光照计算后只需要两步操作,就可以实现边缘光效果。看下面一幅图,简单介绍一下RimLight的原理:
所谓RimLight边缘发光,也就是说对应我们当前视角方向,物体上位于边缘的地方额外加一个光的效果。那么,怎么判断一个点是否在物体的边缘呢?就是通过法线方向和视线方向的夹角来判断。当视线方向V与法线方向N垂直时,这个法线对应的面就与视线方向平行,说明当前这个点对于当前视角来说,就处在边缘;而视线方向与法线方向一致时,这个法线对应的面就垂直于视线方向,说明当前是直视这个面。所以,我们就可以根据dot(N,V)来获得视线方向与法线方向的余弦值,通过这个值来区分该像素是否处在边缘,进而判断是否需要增加以及增加边缘光的强弱。
边缘光效果Unity下的实现
上面已经说完了边缘光效果的实现原理,下面附上完整的边缘光效果shader,基于之前介绍过的,增加了边缘光效果:
//边缘发光Shader
//by:puppet_master
Shader "ApcShader/RimLight"
Properties{
_Diffuse("Diffuse", Color) = (1,1,1,1)
_RimColor("RimColor", Color) = (1,1,1,1)
_RimPower("RimPower", Range(0..0)) = 0.1
_MainTex("Base 2D", 2D) = "white"{}
//子着色器
//定义Tags
Tags{ "RenderType" = "Opaque" }
//引入头文件
#include "Lighting.cginc"
//定义Properties中的变量
sampler2D _MainT
//使用了TRANSFROM_TEX宏就需要定义XXX_ST
float4 _MainTex_ST;
fixed4 _RimC
float _RimP
//定义结构体:vertex shader阶段输出的内容
struct v2f
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float2 uv : TEXCOORD1;
//在vertex shader中计算观察方向传递给fragment shader
float3 worldViewDir : TEXCOORD2;
//定义顶点shader,参数直接使用appdata_base(包含position, noramal, texcoord)
v2f vert(appdata_base v)
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
//通过TRANSFORM_TEX宏转化纹理坐标,主要处理了Offset和Tiling的改变,默认时等同于o.uv = v.texcoord.
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldNormal = mul(v.normal, (float3x3)_World2Object);
//顶点转化到世界空间
float3 worldPos = mul(_Object2World, v.vertex).
//可以把计算计算ViewDir的操作放在vertex shader阶段,毕竟逐顶点计算比较省
o.worldViewDir = _WorldSpaceCameraPos.xyz - worldP
//定义片元shader
fixed4 frag(v2f i) : SV_Target
//unity自身的diffuse也是带了环境光,这里我们也增加一下环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Diffuse.
//归一化法线,即使在vert归一化也不行,从vert到frag阶段有差值处理,传入的法线方向并不是vertex shader直接传出的
fixed3 worldNormal = normalize(i.worldNormal);
//把光照方向归一化
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
//根据半兰伯特模型计算像素的光照信息
fixed3 lambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5;
//最终输出颜色为lambert光强*材质diffuse颜色*光颜色
fixed3 diffuse = lambert * _Diffuse.xyz * _LightColor0.xyz +
//进行纹理采样
fixed4 color = tex2D(_MainTex, i.uv);
//以下为本篇主题:计算RimLight
//把视线方向归一化
float3 worldViewDir = normalize(i.worldViewDir);
//计算视线方向与法线方向的夹角,夹角越大,dot值越接近0,说明视线方向越偏离该点,也就是平视,该点越接近边缘
float rim = 1 - max(0, dot(worldViewDir, worldNormal));
//计算rimLight
fixed3 rimColor = _RimColor * pow(rim, 1 / _RimPower);
//输出颜色+边缘光颜色
color.rgb = color.rgb* diffuse + rimC
return fixed4(color);
//使用vert函数和frag函数
#pragma vertex vert
#pragma fragment frag
//前面的Shader失效的话,使用默认的Diffuse
FallBack "Diffuse"
下面看一下加了边缘光后的效果,普通diffuse shader:
白色边缘光效果:
黄色边缘光效果:
增加效果和效果,Bloom可以把亮的部分处理得更亮,有一种光线溢出的效果,而景深效果可以突出我们要表现的重点。唉,最近发现自己身陷后处理不能自拔啊....
通过Mask图控制边缘光
我们把边缘光效果加大到一定程度,就快成了自发光效果,但是上面的shader有个问题,就是全身都会自发光。如下图所示:
如果只希望盔甲部分有自发光效果,而其他部分没有自发光,我们就需要用Mask图来控制了,我们用一张Alpha8的灰度图来控制是否开启边缘光效果,将上面的shader简单修改一下,增加Mask图的通道:
//边缘发光Shader
//by:puppet_master
Shader "ApcShader/RimLight"
Properties{
_Diffuse("Diffuse", Color) = (1,1,1,1)
_RimColor("RimColor", Color) = (1,1,1,1)
_RimPower("RimPower", Range(0..0)) = 0.1
_RimMask("RimMask", 2D) = "white"{}
_MainTex("Base 2D", 2D) = "white"{}
//子着色器
//定义Tags
Tags{ "RenderType" = "Opaque" }
//引入头文件
#include "Lighting.cginc"
//定义Properties中的变量
sampler2D _MainT
//使用了TRANSFROM_TEX宏就需要定义XXX_ST
float4 _MainTex_ST;
sampler2D _RimM
fixed4 _RimC
float _RimP
//定义结构体:vertex shader阶段输出的内容
struct v2f
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float2 uv : TEXCOORD1;
//在vertex shader中计算观察方向传递给fragment shader
float3 worldViewDir : TEXCOORD2;
//定义顶点shader,参数直接使用appdata_base(包含position, noramal, texcoord)
v2f vert(appdata_base v)
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
//通过TRANSFORM_TEX宏转化纹理坐标,主要处理了Offset和Tiling的改变,默认时等同于o.uv = v.texcoord.
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldNormal = mul(v.normal, (float3x3)_World2Object);
//顶点转化到世界空间
float3 worldPos = mul(_Object2World, v.vertex).
//可以把计算计算ViewDir的操作放在vertex shader阶段,毕竟逐顶点计算比较省
o.worldViewDir = _WorldSpaceCameraPos.xyz - worldP
//定义片元shader
fixed4 frag(v2f i) : SV_Target
//unity自身的diffuse也是带了环境光,这里我们也增加一下环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Diffuse.
//归一化法线,即使在vert归一化也不行,从vert到frag阶段有差值处理,传入的法线方向并不是vertex shader直接传出的
fixed3 worldNormal = normalize(i.worldNormal);
//把光照方向归一化
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
//根据半兰伯特模型计算像素的光照信息
fixed3 lambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5;
//最终输出颜色为lambert光强*材质diffuse颜色*光颜色
fixed3 diffuse = lambert * _Diffuse.xyz * _LightColor0.xyz +
//进行纹理采样
fixed4 color = tex2D(_MainTex, i.uv);
//以下为本篇主题:计算RimLight
//把视线方向归一化
float3 worldViewDir = normalize(i.worldViewDir);
//计算视线方向与法线方向的夹角,夹角越大,dot值越接近0,说明视线方向越偏离该点,也就是平视,该点越接近边缘
float rim = 1 - max(0, dot(worldViewDir, worldNormal));
//计算RimLight
fixed3 rimColor = _RimColor * pow(rim, 1 / _RimPower);
//通过RimMask控制是否有边缘光,Rim目前存在一张Alpha8类型的图片中
fixed rimMask = tex2D(_RimMask, i.uv).a;
//输出颜色+边缘光颜色
color.rgb = color.rgb* diffuse + rimColor * (1 - rimMask);
return fixed4(color);
//使用vert函数和frag函数
#pragma vertex vert
#pragma fragment frag
//前面的Shader失效的话,使用默认的Diffuse
FallBack "Diffuse"
我们把下面这张自发光Mask图赋给材质:
通过采样这张Mask就能控制那部分开启自发光,那部分关闭自发光了:
调整Mask图,也可以得到一些比较好玩的效果:
动态RimLight效果
看了一篇博文,其中的边缘光使用了一张动态图,达到了流动的效果,感谢的无私分享,于是我也尝试一下动态RimLight的效果。其实思路跟上面的Mask图一样,只不过这次改成一张扫面线类型的纹理,然后通过这张纹理的采样滚动,达到动态的效果。一提到动态效果,我们第一个想到的应该就是_Time向量,这个向量中几个值都是跟时间相关的,我们如果做动态效果,肯定少不了和这个变量打交道,关于_Time及其变种,可以参考,我们这里用_Time.y就可以获得时间了。
我们用一张Mask纹理,白色代表有边缘光,黑色无边缘光,通过采样这张Mask纹理,控制模型上显示边缘光的部分:
动态RimLight的shader如下:
//边缘发光Shader
//by:puppet_master
Shader "ApcShader/RimLight"
Properties{
_Diffuse("Diffuse", Color) = (1,1,1,1)
_RimColor("RimColor", Color) = (1,1,1,1)
_RimPower("RimPower", Range(0..0)) = 0.1
_RimMask("RimMask", 2D) = "white"{}
_MainTex("Base 2D", 2D) = "white"{}
_RimSpeed("RimSpeed", Range(-10, 10)) = 1.0
//子着色器
//定义Tags
Tags{ "RenderType" = "Opaque" }
//引入头文件
#include "Lighting.cginc"
//定义Properties中的变量
sampler2D _MainT
//使用了TRANSFROM_TEX宏就需要定义XXX_ST
float4 _MainTex_ST;
sampler2D _RimM
fixed4 _RimC
float _RimP
float _RimS
//定义结构体:vertex shader阶段输出的内容
struct v2f
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float2 uv : TEXCOORD1;
//在vertex shader中计算观察方向传递给fragment shader
float3 worldViewDir : TEXCOORD2;
//定义顶点shader,参数直接使用appdata_base(包含position, noramal, texcoord)
v2f vert(appdata_base v)
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
//通过TRANSFORM_TEX宏转化纹理坐标,主要处理了Offset和Tiling的改变,默认时等同于o.uv = v.texcoord.
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldNormal = mul(v.normal, (float3x3)_World2Object);
//顶点转化到世界空间
float3 worldPos = mul(_Object2World, v.vertex).
//可以把计算计算ViewDir的操作放在vertex shader阶段,毕竟逐顶点计算比较省
o.worldViewDir = _WorldSpaceCameraPos.xyz - worldP
//定义片元shader
fixed4 frag(v2f i) : SV_Target
//unity自身的diffuse也是带了环境光,这里我们也增加一下环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Diffuse.
//归一化法线,即使在vert归一化也不行,从vert到frag阶段有差值处理,传入的法线方向并不是vertex shader直接传出的
fixed3 worldNormal = normalize(i.worldNormal);
//把光照方向归一化
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
//根据半兰伯特模型计算像素的光照信息
fixed3 lambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5;
//最终输出颜色为lambert光强*材质diffuse颜色*光颜色
fixed3 diffuse = lambert * _Diffuse.xyz * _LightColor0.xyz +
//进行纹理采样
fixed4 color = tex2D(_MainTex, i.uv);
//以下为本篇主题:计算RimLight
//把视线方向归一化
float3 worldViewDir = normalize(i.worldViewDir);
//计算视线方向与法线方向的夹角,夹角越大,dot值越接近0,说明视线方向越偏离该点,也就是平视,该点越接近边缘
float rim = 1 - max(0, dot(worldViewDir, worldNormal));
//计算RimLight
fixed3 rimColor = _RimColor * pow(rim, 1 / _RimPower);
//通过RimMask控制是否有边缘光,Rim目前存在一张Alpha8类型的图片中
fixed rimMask = tex2D(_RimMask, i.uv + float2(0 , _Time.y * _RimSpeed)).r;
//输出颜色+边缘光颜色
color.rgb = color.rgb* diffuse + rimColor * (1-rimMask);
return fixed4(color);
//使用vert函数和frag函数
#pragma vertex vert
#pragma fragment frag
//前面的Shader失效的话,使用默认的Diffuse
FallBack "Diffuse"
来一张Gif截图(旁边好像有什么抢镜了):
Unity Shader中可用的变量类型整理
在传递参数时总是忘了能传递哪些类型的参数,记录一下,方便以后查找,这种东西也没必要死记硬背下来,需要的时候几秒钟之内能找到就好了。
//Float类型,下面对应变量可以用flaot,half,fixed
_Name("Inspector Name", Float) = defaultValue
//Float类型,可以用一个滑动条控制范围,下面对应变量可以用float,half,fixed
_Name("Inspector Name", Range(min, max)) = defaultValue
//颜色类型,下面对应变量可以用float4,half4,fixed4,如果是颜色,尽量fixed4
_Name("Inspector Name", Color) = (defaultValue.r, defaultValue.g, defaultValue.b, defaultValue.a)
//2D纹理类型,默认纹理可以为空,白,黑,灰,凹凸,下面对应变量sampler2D
_Name("Inspector Name", 2D) = "" / "white" / "black" / "gray" / "bump"{options}
//长方形纹理,非2次方大小的纹理,其同上
_Name("Inspector Name", Rect) = "" / "white" / "black" / "gray" / "bump"{options}
//立方体贴图CubeMap
_Name("Inspector Name", Cube) = "" {options}
//传递一个Vector4向量
_Name("Inspector Name", Vector) = (defaultValue.x, defaultValue.y, defaultValue.z, defaultValue.w)
//注:上面纹理后面{}里面是一些纹理TexGen,LightmapMode光照模式等内容
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!}

我要回帖

更多关于 ae做边缘流光效果 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信