Unity Shader-纹理采样流光,溶解,隐身效果(转)

简介

今天来研究几个效果,最近比较忙,所以来弄几个比较简单的效果玩一下。不过也是游戏中常用的一些效果,流光效果,按照方向的溶解效果。其实这几个效果主要运用的就是世界空间坐标或者模型空间坐标用于采样的一个方法,总之就是有些非主流的纹理采样方式。不多说,下面进入正题。

流光效果

首先来看一下流光效果。流光效果是一个非常常见的效果,不仅仅是游戏,一些广告之类的也都会有这种效果。流光的原理还是比较简单的:首先就是需要一张流光图,这张流光图的大部分都是黑色,然后有一条亮线,然后我们在采样的时候,最终输出叠加上这张图的采样值,并根据时间调整采样的UV就可以有流光的效果啦。下面是一个比较简单的流光效果实现:

//流光效果

//by:puppet_master

//2017.7.29

Shader "ApcShader/FlashEffect"

{

Properties

{

_MainTex("MainTex(RGB)", 2D) = "white" {}

_FlashTex("FlashTex", 2D) = "black" {}

_FlashColor("FlashColor",Color) = (1,1,1,1)

_FlashSpeedX("FlashSpeedX", Range(-5, 5)) = 0

_FlashSpeedY("FlashSpeedY", Range(-5, 5)) = 0.5

_FlashFactor ("FlashFactor", Range(0, 5)) = 1

}

CGINCLUDE

#include "Lighting.cginc"

uniform sampler2D _MainTex;

uniform float4 _MainTex_ST;

uniform sampler2D _FlashTex;

uniform fixed4 _FlashColor;

uniform fixed _FlashSpeedX;

uniform fixed _FlashSpeedY;

uniform fixed _FlashFactor;

struct v2f

{

float4 pos : SV_POSITION;

float3 worldNormal : NORMAL;

float2 uv : TEXCOORD0;

float3 worldLight : TEXCOORD1;

};

v2f vert(appdata_base v)

{

v2f o;

o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

o.worldNormal = UnityObjectToWorldNormal(v.normal);

o.worldLight = UnityObjectToWorldDir(_WorldSpaceLightPos0.xyz);

return o;

}

fixed4 frag(v2f i) : SV_Target

{

half3 normal = normalize(i.worldNormal);

half3 light = normalize(i.worldLight);

fixed diff = max(0, dot(normal, light));

fixed4 albedo = tex2D(_MainTex, i.uv);

//通过时间将采样flash的uv进行偏移

half2 flashuv = i.uv + half2(_FlashSpeedX, _FlashSpeedY) * _Time.y;

fixed4 flash = tex2D(_FlashTex, flashuv) * _FlashColor * _FlashFactor;

fixed4 c;

//将flash图与原图叠加

c.rgb = diff * albedo + flash.rgb;

c.a = 1;

return c;

}

ENDCG

SubShader

{

Pass

{

Tags{ "RenderType" = "Opaque" }

CGPROGRAM

#pragma vertex vert

#pragma fragment frag

ENDCG

}

}

FallBack "Diffuse"

}

shader比较简单,flashuv是随着时间逐渐增大的,这个值肯定会大于1,而正常纹理的范围是0-1,所以,要想让贴图在采样时大于0-1也有效果,我们就必须要把贴图的WrapMode设置为Repeat,否则当这个值大于1之后,边缘就被截断了,我们也就不会看到流光效果了。注意,流光的贴图是一个方向,采样uv偏移是另一个方向,比如我的流光图是水平方向的,那么流光运动的方向就是竖直方向,效果如下面动图所示:

更通用的流光效果

我们把流光shader用于一个3D模型,效果却并不像我们预期的那样会出现一条扫描线,而是亮起来的地方让人捉摸不透,如下图所示:

为什么会这样呢?其实主要是我们采样的方式导致的,正常的纹理采样都是使用uv坐标进行采样的,也就是说这个坐标是与模型有关,在这个模型上这个点需要采样纹理的哪部分是由美术展uv时决定的。我们如果用uv来进行采样,对于正常的diffuse贴图或者法线贴图等是对的,但是对于一些其他特殊的效果,uv采样不能达到我们的需求了,所以我们就需要研究一下,用一个其他的东东作为采样的坐标,这也是本篇文章主要研究的内容。

如果不用uv进行采样,那么我们就需要一些其他的值作为采样值,之前的文章我们也有使用过类似的方法,比如在热空气扭曲效果中我们使用了屏幕空间采样,采样时计算该点在屏幕空间的坐标值反过来去采样全屏GrabPass图。不过这里我们不需要屏幕空间,毕竟这样的话这个流动效果就会随着我们观察的角度而变化,所以我们选择用世界空间采样:

//流光效果

//by:puppet_master

//2017.7.30

Shader "ApcShader/FlashEffect"

{

Properties

{

_MainTex("MainTex(RGB)", 2D) = "white" {}

_FlashTex("FlashTex", 2D) = "black" {}

_FlashColor("FlashColor",Color) = (1,1,1,1)

_FlashFactor("FlashFactor", Vector) = (0, 1, 0.5, 0.5)

_FlashStrength ("FlashStrength", Range(0, 5)) = 1

}

CGINCLUDE

#include "Lighting.cginc"

uniform sampler2D _MainTex;

uniform float4 _MainTex_ST;

uniform sampler2D _FlashTex;

uniform fixed4 _FlashColor;

//改为一个vector4,减少传参次数消耗

uniform fixed4 _FlashFactor;

uniform fixed _FlashStrength;

struct v2f

{

float4 pos : SV_POSITION;

float3 worldNormal : NORMAL;

float2 uv : TEXCOORD0;

float3 worldLight : TEXCOORD1;

float4 worldPos : TEXCOORD2;

};

v2f vert(appdata_base v)

{

v2f o;

o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

//顶点转化到世界空间

o.worldPos = mul(unity_ObjectToWorld, v.vertex);

o.worldNormal = UnityObjectToWorldNormal(v.normal);

o.worldLight = UnityObjectToWorldDir(_WorldSpaceLightPos0.xyz);

return o;

}

fixed4 frag(v2f i) : SV_Target

{

half3 normal = normalize(i.worldNormal);

half3 light = normalize(i.worldLight);

fixed diff = max(0, dot(normal, light));

fixed4 albedo = tex2D(_MainTex, i.uv);

//通过时间偏移世界坐标对flashTex进行采样

half2 flashuv = i.worldPos.xy * _FlashFactor.zw + _FlashFactor.xy * _Time.y;

fixed4 flash = tex2D(_FlashTex, flashuv) * _FlashColor * _FlashStrength;

fixed4 c;

//将flash图与原图叠加

c.rgb = diff * albedo + flash.rgb;

c.a = 1;

return c;

}

ENDCG

SubShader

{

Pass

{

Tags{ "RenderType" = "Opaque" }

CGPROGRAM

#pragma vertex vert

#pragma fragment frag

ENDCG

}

}

FallBack "Diffuse"

}

好了,下面我们找个帅帅哒模型,看一下修改之后的流光效果:

然后我们也可以换一张贴图,再调整一下参数,让流光换个方向:

按照方向消失或重现效果

我们再来看一个用模型空间坐标作为采样的uv的栗子,也是一种比较好玩的效果。比如我们需要一个模型身体按照一定的方向逐渐消失,直至全部消失掉的一个效果。下面说一下思路,与世界空间采样的流光效果一样,我们在vertex阶段记录一下vertex坐标,传递给fragment阶段,在fragment阶段用这个值和一个设定好的阈值进行比较,不满足条件的像素点直接discard,逐渐调整阈值,就可以得到让模型按照某个方向消失的效果了。代码如下:

//按照方向消失的效果

//by:puppet_master

//2017.8.10

Shader "ApcShader/DissolveEffectX"

{

Properties

{

_MainTex("MainTex(RGB)", 2D) = "white" {}

_DissolveVector("DissolveVector", Vector) = (0,0,0,0)

}

CGINCLUDE

#include "Lighting.cginc"

uniform sampler2D _MainTex;

uniform float4 _MainTex_ST;

uniform float4 _DissolveVector;

struct v2f

{

float4 pos : SV_POSITION;

float3 worldNormal : NORMAL;

float2 uv : TEXCOORD0;

float3 worldLight : TEXCOORD1;

float4 objPos : TEXCOORD2;

};

v2f vert(appdata_base v)

{

v2f o;

o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

//顶点转化到世界空间

o.objPos = v.vertex;

o.worldNormal = UnityObjectToWorldNormal(v.normal);

o.worldLight = UnityObjectToWorldDir(_WorldSpaceLightPos0.xyz);

return o;

}

fixed4 frag(v2f i) : SV_Target

{

half3 normal = normalize(i.worldNormal);

half3 light = normalize(i.worldLight);

fixed diff = max(0, dot(normal, light));

fixed4 albedo = tex2D(_MainTex, i.uv);

//不满足条件的discard

clip(i.objPos.xyz - _DissolveVector.xyz);

fixed4 c;

c.rgb = diff * albedo;

c.a = 1;

return c;

}

ENDCG

SubShader

{

Pass

{

Tags{ "RenderType" = "Opaque" }

CGPROGRAM

#pragma vertex vert

#pragma fragment frag

ENDCG

}

}

FallBack "Diffuse"

}

还是上面的模型,我们逐渐调整X,Y,Z轴三个方向的阈值,就可以有逐渐消失或者出现的效果啦,如下面动图所示:

在这里,我们没有像流光效果那样使用世界空间坐标采样,而是使用了模型空间坐标采样,其实还可以使用屏幕空间坐标或者视口空间坐标采样等等,几种方式各有各的优点和缺点。使用世界空间采样,消失的方向是绝对的,比如是从上向下,那么这个模型如果趴在地上,消失的方向就会是从模型后背到前胸的方向,而且坐标阈值会随着模型处于世界中的位置不同而不同;使用模型空间采样,消失的方向与模型本身有关,比如站着的话消失方向是从头到脚,那么趴着也是从头到脚,而且坐标的阈值与模型的高矮胖瘦有关(也与模型的原点位置有点关系);使用屏幕空间采样的话,消失的方向就可能会与我们观察的方向有关,这种可能不太可控。

还有一个小问题,其实上图中的例子里面,模型从上到下,理想情况应该是调整Y轴,不过例子里面调整的确实X轴,原因应该与Unity导入之后会绕着X轴旋转90度有关,也就是原本在max里面的Y轴变成Unity里面的X轴。

下面,我们再看一下增加了边缘高亮的消失效果,为了让模消失的型边缘高亮,我们通过将用于clip的factor值与另一个高亮阈值值进行比较,如果factor小于高亮阈值,则返回一个高亮的颜色值,否则正常渲染。这样模型就总共有三种显示状态:clip状态,高亮状态,正常状态。代码如下:

//消失效果

//by:puppet_master

//2017.8.11

Shader "ApcShader/DissolveEffectX"

{

Properties{

_Diffuse("Diffuse", Color) = (1,1,1,1)

_DissolveColor("Dissolve Color", Color) = (0,0,0,0)

_MainTex("Base 2D", 2D) = "white"{}

_ColorFactor("ColorFactor", Range(0,1)) = 0.7

_DissolveThreshold("DissolveThreshold", Float) = 0

}

CGINCLUDE

#include "Lighting.cginc"

uniform fixed4 _Diffuse;

uniform fixed4 _DissolveColor;

uniform sampler2D _MainTex;

uniform float4 _MainTex_ST;

uniform float _ColorFactor;

uniform float _DissolveThreshold;

struct v2f

{

float4 pos : SV_POSITION;

float3 worldNormal : TEXCOORD0;

float2 uv : TEXCOORD1;

float4 objPos : TEXCOORD2;

};

v2f vert(appdata_base v)

{

v2f o;

o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);

o.objPos = v.vertex;

return o;

}

fixed4 frag(v2f i) : SV_Target

{

float factor = i.objPos.x - _DissolveThreshold;

clip(factor);

//Diffuse + Ambient光照计算

fixed3 worldNormal = normalize(i.worldNormal);

fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

fixed3 lambert = saturate(dot(worldNormal, worldLightDir));

fixed3 albedo = lambert * _Diffuse.xyz * _LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.xyz;

fixed3 color = tex2D(_MainTex, i.uv).rgb * albedo;

//等价于下面注释代码的操作

fixed lerpFactor = saturate(sign(_ColorFactor - factor));

return lerpFactor * _DissolveColor + (1 - lerpFactor) * fixed4(color, 1);

/*

if (factor < _ColorFactor)

{

return _DissolveColor;

}

return fixed4(color, 1);*/

}

ENDCG

SubShader

{

Tags{ "RenderType" = "Opaque" }

Pass

{

//不让模型穿帮,关掉了背面裁剪

Cull Off

CGPROGRAM

#pragma vertex vert

#pragma fragment frag

ENDCG

}

}

FallBack "Diffuse"

}

这一次我们只让模型在上下方向上逐渐消失,调整ColorFactor可以控制高亮区域的高度:

溶解效果进阶版

之前的文章里,我们研究过溶解效果,不过这个效果是基于全身的,我们来尝试一下,把上面按照方向消失的效果与溶解效果结合起来,做成一个按照某个方向逐渐溶解的效果。要得到随机的溶解效果,我们需要采样一张噪声图,然后在原本会直接clip掉的部分根据采样的噪声图进行clip,就能得到按照方向的溶解效果啦。

//溶解效果

//by:puppet_master

//2017.8.11

Shader "ApcShader/DissolveEffectX"

{

Properties{

_Diffuse("Diffuse", Color) = (1,1,1,1)

_DissolveColor("Dissolve Color", Color) = (1,1,1,1)

_MainTex("Base 2D", 2D) = "white"{}

_DissolveMap("DissolveMap", 2D) = "white"{}

_DissolveThreshold("DissolveThreshold", Range(0,1)) = 0

_DissolveSpeedFactor("DissolveSpeed", Range(0,5)) = 2

_DissolveControl("ColorFactorB", Float) = 0

}

CGINCLUDE

#include "Lighting.cginc"

uniform fixed4 _Diffuse;

uniform fixed4 _DissolveColor;

uniform sampler2D _MainTex;

uniform float4 _MainTex_ST;

uniform sampler2D _DissolveMap;

uniform float _DissolveThreshold;

uniform float _DissolveSpeedFactor;

uniform float _DissolveControl;

struct v2f

{

float4 pos : SV_POSITION;

float3 worldNormal : TEXCOORD0;

float2 uv : TEXCOORD1;

float4 objPos : TEXCOORD2;

};

v2f vert(appdata_base v)

{

v2f o;

o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);

o.objPos = v.vertex;

return o;

}

fixed4 frag(v2f i) : SV_Target

{

fixed4 dissolve = tex2D(_DissolveMap, i.uv);

//Diffuse + Ambient光照计算

fixed3 worldNormal = normalize(i.worldNormal);

fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

fixed3 lambert = saturate(dot(worldNormal, worldLightDir));

fixed3 albedo = lambert * _Diffuse.xyz * _LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.xyz;

fixed3 color = tex2D(_MainTex, i.uv).rgb * albedo;

float factor = i.objPos.x - _DissolveControl;

if(factor < 0)

{

clip(_DissolveThreshold - dissolve.r * abs(factor) * _DissolveSpeedFactor);

}

return fixed4(color, 1);

}

ENDCG

SubShader

{

Tags{ "RenderType" = "Opaque" }

Pass

{

Cull Off

CGPROGRAM

#pragma vertex vert

#pragma fragment frag

ENDCG

}

}

FallBack "Diffuse"

}

效果如下:

当然,也可以参考之前溶解效果,增加溶解描边高亮的效果:

转自:http://m.blog.csdn.net/puppet_master/article/details/76359838

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容