Unity Shader-遮挡处理(X-Ray,遮挡描边,遮挡半透,遮挡溶解)
简介
第三人称游戏,我们经常会遇到相机被场景中的建筑物遮挡的情况。今天,本人就来研究一下相机被遮挡之后的处理。最简单的就是传说中的“鸵鸟法”,假装看不见,在一些游戏里面也有一些玩法设定,或者是本身遮挡较少,影响不大的情况,也可以直接不进行处理。
当然,更好一些的遮挡处理,就是X光的效果。在人物被遮挡的部分会透过遮挡物,用一个其他的颜色渲染出来。《火炬之光》中就使用过这个效果:
类似的,《耻辱2》中的透视效果也是游戏中经常使用的,这种暂且叫其遮挡高亮或者遮挡描边吧,对于刺杀类型的游戏,这种透视技能简直是神技,比如在柱子后面就能瞄见这货:
还有一种对于遮挡的处理,就是遮挡半透,这个在很多游戏里面都有出现,比如《黑魂2》,《奥瑞与黑暗森林》,下面是Ori中的一个遮挡半透的效果动图:
还有一个效果,暂且叫其遮挡溶解吧。这个效果我是在《神界3:原罪》中看到的,说实话,第一次看到这个效果的时候,着实被惊艳到了。
翻箱倒柜找出来这几个游戏,截了一发图,顺道怀念一下。哎呀,一不小心就给自己挖了个超级大的坑。四个效果,下面开始慢慢填坑吧。
X光效果
![](/assets/blank.gif)
//X光效果
//by:puppet_master
//2017.6.20Shader "ApcShader/XRayEffect"
{Properties{_MainTex("Base 2D", 2D) = "white"{}}SubShader{Tags{ "Queue" = "Geometry" "RenderType" = "Opaque" }//渲染X光效果的PassPass{Blend SrcAlpha OneZWrite OffZTest GreaterCGPROGRAM#include "Lighting.cginc"struct v2f{float4 pos : SV_POSITION;};v2f vert (appdata_base v){v2f o;o.pos = mul(UNITY_MATRIX_MVP, v.vertex);return o;}fixed4 frag(v2f i) : SV_Target{return fixed4(1,1,1,0.5);}#pragma vertex vert#pragma fragment fragENDCG}//正常渲染的PassPass{ZWrite OnCGPROGRAM#include "Lighting.cginc"sampler2D _MainTex;float4 _MainTex_ST;struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD1;};v2f vert(appdata_base v){v2f o;o.pos = mul(UNITY_MATRIX_MVP, v.vertex);o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target{return tex2D(_MainTex, i.uv);}#pragma vertex vert#pragma fragment frag ENDCG}}FallBack "Diffuse"
}
效果如下:
上图基本实现了X光的效果,不过效果不是很美观,我们将X光的Pass更换一下,使用半透+边缘光的效果进行渲染,代码如下:
//X光效果
//by:puppet_master
//2017.6.20Shader "ApcShader/XRayEffect"
{Properties{_MainTex("Base 2D", 2D) = "white"{}_XRayColor("XRay Color", Color) = (1,1,1,1)}SubShader{Tags{ "Queue" = "Geometry+100" "RenderType" = "Opaque" }//渲染X光效果的PassPass{Blend SrcAlpha OneZWrite OffZTest GreaterCGPROGRAM#include "Lighting.cginc"fixed4 _XRayColor;struct v2f{float4 pos : SV_POSITION;float3 normal : normal;float3 viewDir : TEXCOORD0;};v2f vert (appdata_base v){v2f o;o.pos = mul(UNITY_MATRIX_MVP, v.vertex);o.viewDir = ObjSpaceViewDir(v.vertex);o.normal = v.normal;return o;}fixed4 frag(v2f i) : SV_Target{float3 normal = normalize(i.normal);float3 viewDir = normalize(i.viewDir);float rim = 1 - dot(normal, viewDir);return _XRayColor * rim;}#pragma vertex vert#pragma fragment fragENDCG}//正常渲染的PassPass{ZWrite OnCGPROGRAM#include "Lighting.cginc"sampler2D _MainTex;float4 _MainTex_ST;struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;};v2f vert(appdata_base v){v2f o;o.pos = mul(UNITY_MATRIX_MVP, v.vertex);o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target{return tex2D(_MainTex, i.uv);}#pragma vertex vert#pragma fragment frag ENDCG}}FallBack "Diffuse"
}
来一张动图看一下X光的效果:
遮挡描边效果
![](/assets/blank.gif)
/********************************************************************FileName: OccOutLineEffect.csDescription: 遮挡描边后处理效果Created: 2017/07/20by puppet_master
*********************************************************************/
using UnityEngine;
using System.Collections;
using UnityEngine.Rendering;public class OccOutLineEffect : PostEffectBase
{private RenderTexture renderTexture = null;private CommandBuffer commandBuffer = null;//采样率public float samplerScale = 1;//降采样public int downSample = 1;//迭代次数public int iteration = 2;//描边强度[Range(0.0f, 10.0f)]public float outLineStrength = 3.0f;//目标对象public GameObject targetObject = null;void OnEnable(){Renderer[] renderers = targetObject.GetComponentsInChildren<Renderer>();//RT可以设置AA等级,降低锯齿效果if (renderTexture == null)renderTexture = RenderTexture.GetTemporary(Screen.width >> downSample, Screen.height >> downSample);//创建描边prepass的command buffercommandBuffer = new CommandBuffer();commandBuffer.SetRenderTarget(renderTexture);commandBuffer.ClearRenderTarget(true, true, Color.black);foreach (Renderer r in renderers)commandBuffer.DrawRenderer(r, r.sharedMaterial);}void OnDisable(){if (renderTexture){RenderTexture.ReleaseTemporary(renderTexture);renderTexture = null;}if (commandBuffer != null){commandBuffer.Release();commandBuffer = null;}}void OnRenderImage(RenderTexture source, RenderTexture destination){if (_Material && renderTexture && commandBuffer != null){//直接通过Graphic执行Command BufferGraphics.ExecuteCommandBuffer(commandBuffer);//对RT进行Blur处理RenderTexture temp1 = RenderTexture.GetTemporary(source.width >> downSample, source.height >> downSample, 0);RenderTexture temp2 = RenderTexture.GetTemporary(source.width >> downSample, source.height >> downSample, 0);//高斯模糊,两次模糊,横向纵向,使用pass0进行高斯模糊_Material.SetVector("_offsets", new Vector4(0, samplerScale, 0, 0));Graphics.Blit(renderTexture, temp1, _Material, 0);_Material.SetVector("_offsets", new Vector4(samplerScale, 0, 0, 0));Graphics.Blit(temp1, temp2, _Material, 0);//如果有叠加再进行迭代模糊处理for (int i = 0; i < iteration; i++){_Material.SetVector("_offsets", new Vector4(0, samplerScale, 0, 0));Graphics.Blit(temp2, temp1, _Material, 0);_Material.SetVector("_offsets", new Vector4(samplerScale, 0, 0, 0));Graphics.Blit(temp1, temp2, _Material, 0);}//叠加效果_Material.SetTexture("_BlurTex", temp1);_Material.SetFloat("_OutlineStrength", outLineStrength);Graphics.Blit(source, destination, _Material, 2);RenderTexture.ReleaseTemporary(temp1);RenderTexture.ReleaseTemporary(temp2);}else{Graphics.Blit(source, destination);}}
}
shader代码如下,其实就是之前的描边shader,偷了个懒,继续用一下(Pass1没有用):
//后处理描边Shader
//by:puppet_master
//2017.1.12Shader "Custom/OutLinePostEffect" {Properties{_MainTex("Base (RGB)", 2D) = "white" {}_BlurTex("Blur", 2D) = "white"{}}CGINCLUDE#include "UnityCG.cginc"//用于剔除中心留下轮廓struct v2f_cull{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;};//用于模糊struct v2f_blur{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;float4 uv01 : TEXCOORD1;float4 uv23 : TEXCOORD2;float4 uv45 : TEXCOORD3;};//用于最后叠加struct v2f_add{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;float2 uv1 : TEXCOORD1;};sampler2D _MainTex;float4 _MainTex_TexelSize;sampler2D _BlurTex;float4 _BlurTex_TexelSize;float4 _offsets;float _OutlineStrength;//Blur图和原图进行相减获得轮廓v2f_cull vert_cull(appdata_img v){v2f_cull o;o.pos = mul(UNITY_MATRIX_MVP, v.vertex);o.uv = v.texcoord.xy;//dx中纹理从左上角为初始坐标,需要反向
#if UNITY_UV_STARTS_AT_TOPif (_MainTex_TexelSize.y < 0)o.uv.y = 1 - o.uv.y;
#endif return o;}fixed4 frag_cull(v2f_cull i) : SV_Target{fixed4 colorMain = tex2D(_MainTex, i.uv);fixed4 colorBlur = tex2D(_BlurTex, i.uv);//最后的颜色是_BlurTex - _MainTex,周围0-0=0,黑色;边框部分为描边颜色-0=描边颜色;中间部分为描边颜色-描边颜色=0。最终输出只有边框//return fixed4((colorBlur - colorMain).rgb, 1);return colorBlur - colorMain;}//高斯模糊 vert shader(之前的文章有详细注释,此处也可以用BoxBlur,更省一点)v2f_blur vert_blur(appdata_img v){v2f_blur o;_offsets *= _MainTex_TexelSize.xyxy;o.pos = mul(UNITY_MATRIX_MVP, v.vertex);o.uv = v.texcoord.xy;o.uv01 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1);o.uv23 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 2.0;o.uv45 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 3.0;return o;}//高斯模糊 pixel shaderfixed4 frag_blur(v2f_blur i) : SV_Target{fixed4 color = fixed4(0,0,0,0);color += 0.40 * tex2D(_MainTex, i.uv);color += 0.15 * tex2D(_MainTex, i.uv01.xy);color += 0.15 * tex2D(_MainTex, i.uv01.zw);color += 0.10 * tex2D(_MainTex, i.uv23.xy);color += 0.10 * tex2D(_MainTex, i.uv23.zw);color += 0.05 * tex2D(_MainTex, i.uv45.xy);color += 0.05 * tex2D(_MainTex, i.uv45.zw);return color;}//最终叠加 vertex shaderv2f_add vert_add(appdata_img v){v2f_add o;//mvp矩阵变换o.pos = mul(UNITY_MATRIX_MVP, v.vertex);//uv坐标传递o.uv.xy = v.texcoord.xy;o.uv1.xy = o.uv.xy;
#if UNITY_UV_STARTS_AT_TOPif (_MainTex_TexelSize.y < 0)o.uv.y = 1 - o.uv.y;
#endif return o;}fixed4 frag_add(v2f_add i) : SV_Target{//取原始场景图片进行采样fixed4 ori = tex2D(_MainTex, i.uv1);//取得到的轮廓图片进行采样fixed4 blur = tex2D(_BlurTex, i.uv);//return blur;fixed4 final = ori + blur * _OutlineStrength;return final;}ENDCGSubShader{//pass 0: 高斯模糊Pass{ZTest OffCull OffZWrite OffFog{ Mode Off }CGPROGRAM#pragma vertex vert_blur#pragma fragment frag_blurENDCG}//pass 1: 剔除中心部分 Pass{ZTest OffCull OffZWrite OffFog{ Mode Off }CGPROGRAM#pragma vertex vert_cull#pragma fragment frag_cullENDCG}//pass 2: 最终叠加Pass{ZTest OffCull OffZWrite OffFog{ Mode Off }CGPROGRAM#pragma vertex vert_add#pragma fragment frag_addENDCG}}
}
然后人物本身的shader我们换成边缘光的shader(可以参考之前的文章),效果如下:
![](/assets/blank.gif)
遮挡半透
遮挡半透是游戏中非常常用的处理遮挡的手段。开头提到过的《黑魂2》,《Ori》都是用了这个效果,这俩游戏也都让我差点摔手柄.....半年之前开始玩的《Ori》,后来直接弃坑了,前一阵子才刚刚通关《Ori》,死了1700多次,不过游戏是真的好,虽然这辈子也不想再玩第二次了。额,一不小心又扯远了,而且不小心透露了最近断更的主要原因。下面继续看遮挡半透。
/********************************************************************FileName: TransparentControl.csDescription: 遮挡半透查询控制器,挂于摄像机Created: 2017/07/23history: 23:7:2017 12:45 by puppet_master
*********************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;public class TransparentControl : MonoBehaviour {public class TransparentParam{public Material[] materials = null;public Material[] sharedMats = null;public float currentFadeTime = 0;public bool isTransparent = true;}public Transform targetObject = null; //目标对象public float height = 3.0f; //目标对象Y方向偏移public float destTransparent = 0.2f; //遮挡半透的最终半透强度,public float fadeInTime = 1.0f; //开始遮挡半透时渐变时间private int transparentLayer; //需要遮挡半透的层级private Dictionary<Renderer, TransparentParam> transparentDic = new Dictionary<Renderer, TransparentParam>();private List<Renderer> clearList = new List<Renderer>();void Start (){transparentLayer = 1 << LayerMask.NameToLayer("OcclusionTran");}void Update (){if (targetObject == null)return;UpdateTransparentObject();UpdateRayCastHit();RemoveUnuseTransparent();}public void UpdateTransparentObject(){var var = transparentDic.GetEnumerator();while (var.MoveNext()){TransparentParam param = var.Current.Value;param.isTransparent = false;foreach (var mat in param.materials){Color col = mat.GetColor("_Color");param.currentFadeTime += Time.deltaTime;float t = param.currentFadeTime / fadeInTime;col.a = Mathf.Lerp(1, destTransparent, t);mat.SetColor("_Color", col);}}}public void UpdateRayCastHit(){RaycastHit[] rayHits = null;//视线方向为从自身(相机)指向目标位置Vector3 targetPos = targetObject.position + new Vector3(0, height, 0);Vector3 viewDir = (targetPos - transform.position).normalized;Vector3 oriPos = transform.position;float distance = Vector3.Distance(oriPos, targetPos);Ray ray = new Ray(oriPos, viewDir);rayHits = Physics.RaycastAll(ray, distance, transparentLayer);//直接在Scene画一条线,方便观察射线Debug.DrawLine(oriPos, targetPos, Color.red);foreach (var hit in rayHits){Renderer[] renderers = hit.collider.GetComponentsInChildren<Renderer>();foreach (Renderer r in renderers){AddTransparent(r);}}}public void RemoveUnuseTransparent(){clearList.Clear();var var = transparentDic.GetEnumerator();while(var.MoveNext()){if (var.Current.Value.isTransparent == false){//用完后材质实例不会销毁,可以被unloadunuseasset销毁或切场景销毁。var.Current.Key.materials = var.Current.Value.sharedMats;clearList.Add(var.Current.Key);}}foreach(var v in clearList)transparentDic.Remove(v);}void AddTransparent(Renderer renderer){TransparentParam param = null;transparentDic.TryGetValue(renderer, out param);if (param == null){param = new TransparentParam();transparentDic.Add(renderer, param);//此处顺序不能反,调用material会产生材质实例。param.sharedMats = renderer.sharedMaterials;param.materials = renderer.materials;foreach(var v in param.materials){v.shader = Shader.Find("ApcShader/OcclusionTransparent");}}param.isTransparent = true;}
}
我们先用Unity自带的半透的shader试一下,这次我们直接用一个surface shader:
Shader "ApcShader/OcclusionTransparent" {Properties {_Color ("Main Color", Color) = (1,1,1,1)_MainTex ("Base (RGB)", 2D) = "white" {}}SubShader {Tags { "RenderType"="Transparent" "Queue" = "Transparent"}ZWrite OffCGPROGRAM#pragma surface surf Lambert alphasampler2D _MainTex;fixed4 _Color;struct Input {float2 uv_MainTex;};void surf (Input IN, inout SurfaceOutput o) {fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;o.Albedo = c.rgb;o.Alpha = c.a;}ENDCG} FallBack "VertexLit"
}
然后,我们在场景中尝试一下这个效果。首先,需要把需要遮挡半透的物体添加碰撞体组件,这里我们直接用BoxCollider,毕竟碰撞体越简单,性能越好。然后在相机上挂上TransparentController脚本,当相机和人物中间出现了遮挡物,遮挡物就会自动替换成半透shader。效果如下:
![](/assets/blank.gif)
//增加prepass的半透渲染
//by:puppet_master
//2017.7.23
Shader "ApcShader/OcclusionTransparent" {Properties {_Color ("Main Color", Color) = (1,1,1,1)_MainTex ("Base (RGB)", 2D) = "white" {}}SubShader {Tags { "RenderType"="Transparent" "Queue" = "Transparent-1"}Pass{ZWrite On //开启深度写入ColorMask 0 //不写颜色} ZWrite OffCGPROGRAM#pragma surface surf Lambert alphasampler2D _MainTex;fixed4 _Color;struct Input {float2 uv_MainTex;};void surf (Input IN, inout SurfaceOutput o) {fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;o.Albedo = c.rgb;o.Alpha = c.a;}ENDCG} FallBack "VertexLit"
}
下面来一张图来对比一下使用了 Prepass和正常渲染的半透效果:
![](/assets/blank.gif)
![](/assets/blank.gif)
遮挡溶解
//vertex阶段o.pos = mul(UNITY_MATRIX_MVP, v.vertex);o.screenPos = ComputeGrabScreenPos(o.pos);//fragment阶段float screenSpacePos = i.screenPos.y / i.screenPos.w;return fixed4(screenSpacePos, screenSpacePos, screenSpacePos, 1);
比如我们把屏幕空间Y方向的位置输出出来,效果如下:
![](/assets/blank.gif)
float2 screenPos = i.screenPos.xy / i.screenPos.w;float2 dir = float2(0.5, 0.5) - screenPos;float distance = sqrt(dir.x * dir.x + dir.y * dir.y);return fixed4(distance, distance, distance, 1);
//遮挡溶解效果
//by:puppet_master
//2017.7.23Shader "ApcShader/OcclusionDissolve"
{Properties{_Diffuse("Diffuse", Color) = (1,1,1,1)_DissolveColorA("Dissolve Color A", Color) = (0,0,0,0)_DissolveColorB("Dissolve Color B", Color) = (1,1,1,1)_MainTex("Base 2D", 2D) = "white"{}_DissolveMap("DissolveMap", 2D) = "white"{}_DissolveThreshold("DissolveThreshold", Range(0,2)) = 0_ColorFactorA("ColorFactorA", Range(0,1)) = 0.7_ColorFactorB("ColorFactorB", Range(0,1)) = 0.8}CGINCLUDE#include "Lighting.cginc"uniform fixed4 _Diffuse;uniform fixed4 _DissolveColorA;uniform fixed4 _DissolveColorB;uniform sampler2D _MainTex;uniform float4 _MainTex_ST;uniform sampler2D _DissolveMap;uniform float _DissolveThreshold;uniform float _ColorFactorA;uniform float _ColorFactorB;struct v2f{float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float2 uv : TEXCOORD1;float4 screenPos : 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.pos = mul(UNITY_MATRIX_MVP, v.vertex);//计算屏幕坐标o.screenPos = ComputeGrabScreenPos(o.pos);return o;}fixed4 frag(v2f i) : SV_Target{float2 screenPos = i.screenPos.xy / i.screenPos.w;//计算距离中心点距离float2 dir = float2(0.5, 0.5) - screenPos;float distance = sqrt(dir.x * dir.x + dir.y * dir.y);//距离中心点近的才进行溶解处理float disolveFactor = (0.5 - distance) * _DissolveThreshold;//采样Dissolve Mapfixed4 dissolveValue = tex2D(_DissolveMap, i.uv);//小于阈值的部分直接discardif (dissolveValue.r < disolveFactor){discard;}//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;//这里为了比较方便,直接用color和最终的边缘lerp了float lerpValue = disolveFactor / dissolveValue.r;if (lerpValue > _ColorFactorA){if (lerpValue > _ColorFactorB)return _DissolveColorB;return _DissolveColorA;}return fixed4(color, 1);}ENDCGSubShader{Tags{ "RenderType" = "Opaque" }Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag ENDCG}}FallBack "Diffuse"
}
给一张噪声图,调整一个比较酷炫的颜色,然后我们就可以看到之前普通的溶解效果会随着我们视线的位置溶解掉我们看到的东西:
![](/assets/blank.gif)
哇塞,有一种鼬神天照的赶脚,看哪哪溶解。要是什么时候Get到这样的神技就好了。不过现在这种效果确实有些太猛了点,视线之内寸草不生,貌似不是我们想要的效果,毕竟我们想解决的只是挡住主角的那一部分。所以,我们需要给一个距离限制,当这个像素点距离相机在这个距离限制之内,就进行溶解,否则不进行溶解。其实这个距离刚好就可以给成我们相机到主角之间的距离,这样,只要人物经过哪里,人物和相机之间的内容就会进行溶解。我们给上面的shader加工一下,增加一个距离的限制:
//遮挡溶解效果
//by:puppet_master
//2017.7.26Shader "ApcShader/OcclusionDissolve"
{Properties{_Diffuse("Diffuse", Color) = (1,1,1,1)_DissolveColorA("Dissolve Color A", Color) = (0,1,1,0)_DissolveColorB("Dissolve Color B", Color) = (0.3,0.3,0.3,1)_MainTex("Base 2D", 2D) = "white"{}_DissolveMap("DissolveMap", 2D) = "white"{}_DissolveThreshold("DissolveThreshold", Range(0,2)) = 2_ColorFactorA("ColorFactorA", Range(0,1)) = 0.7_ColorFactorB("ColorFactorB", Range(0,1)) = 0.8_DissolveDistance("DissolveDistance", Range(0, 20)) = 14_DissolveDistanceFactor("DissolveDistanceFactor", Range(0,3)) = 3}CGINCLUDE#include "Lighting.cginc"uniform fixed4 _Diffuse;uniform fixed4 _DissolveColorA;uniform fixed4 _DissolveColorB;uniform sampler2D _MainTex;uniform float4 _MainTex_ST;uniform sampler2D _DissolveMap;uniform float _DissolveThreshold;uniform float _ColorFactorA;uniform float _ColorFactorB;uniform float _DissolveDistance;uniform float _DissolveDistanceFactor;struct v2f{float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float2 uv : TEXCOORD1;float4 screenPos : TEXCOORD2;float3 viewDir : TEXCOORD3;};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.pos = mul(UNITY_MATRIX_MVP, v.vertex);o.viewDir = ObjSpaceViewDir(v.vertex);//计算屏幕坐标o.screenPos = ComputeGrabScreenPos(o.pos);return o;}fixed4 frag(v2f i) : SV_Target{float2 screenPos = i.screenPos.xy / i.screenPos.w;//计算距离中心点距离作为一个控制系数float2 dir = float2(0.5, 0.5) - screenPos;float screenSpaceDistance = 0.5 - sqrt(dir.x * dir.x + dir.y * dir.y);//计算一下像素点到相机距离作为另一个控制系数float viewDistance = max(0,(_DissolveDistance - length(i.viewDir)) / _DissolveDistance) * _DissolveDistanceFactor;//用两个控制系数作为最终溶解的系数float disolveFactor = viewDistance * screenSpaceDistance * _DissolveThreshold;//采样Dissolve Mapfixed4 dissolveValue = tex2D(_DissolveMap, i.uv);//小于阈值的部分直接discardif (dissolveValue.r < disolveFactor){discard;}//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;//这里为了比较方便,直接用color和最终的边缘lerp了float lerpValue = disolveFactor / dissolveValue.r;if (lerpValue > _ColorFactorA){if (lerpValue > _ColorFactorB)return _DissolveColorB;return _DissolveColorA;}return fixed4(color, 1);}ENDCGSubShader{Tags{ "RenderType" = "Opaque" }Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag ENDCG}}FallBack "Diffuse"
}
效果如下面动态图所示,只有在距离相机距离比较近的内容才会被溶解掉:
![](/assets/blank.gif)
最终效果如下图所示,人物走过的部分可能会遮挡人物的物体就都被溶解掉了:
![](/assets/blank.gif)
Unity Shader-遮挡处理(X-Ray,遮挡描边,遮挡半透,遮挡溶解)相关推荐
- Unity Shader 卡通渲染 实时模型动画描边的研究
前言 卡通渲染也叫非真实感渲染(英文简写:NPR),"描边"在图形学和数字图像里都叫边缘检测.因此你可以在很多文献网站上面找到很多这类文献,但最后我发现基于图形学使用的方式基本都是 ...
- Unity Shader 基于 RGB 插值的 Wireframe 描边着色器
先睹为快 引入 这周学习了一种对模型的 Wireframe 做描边的着色器,其核心思想是:在几何着色器中,先将三维空间中每个顶点的坐标转换为屏幕坐标系下的平面坐标,然后求这个平面三角形的面积,继而求出 ...
- Unity shader学习-漫反射-兰伯特光照模型和半兰伯特光照模型
兰伯特漫反射公式:Diffuse = 直射光颜色 *物体颜色* max(0,cos夹角(光和法线的夹角) ) 下面给出顶点漫反射代码: Shader "Unlit/005" { ...
- Unity Shader 卡通渲染 模型描边之退化四边形
目录 前言 一.基于空间的边缘检测算法 二.退化四边形 三.Unity中的CommandBuffer和ComputeBuffer 四.构成描边的简单实例 五.模型描边的实现 前言 之前写了一篇< ...
- 【unity shader】unity游戏特效-遮挡显示效果 (含边缘光、描边效果版)
不知道你们有没有在玩Black Squad这个游戏啊 在被对手干掉时会有敌人高亮显示效果 (未被做掉时) (被做掉后高亮显示敌人位置) 明明敌人被不透明物体挡住却仍然可以被渲染出来 这效果要是能扔进自 ...
- Unity Shader - Occlusion Map 遮挡贴图
目录:Unity Shader - 知识点目录(先占位,后续持续更新) 原文:Occlusion Map 版本:2019.1 Occlusion Map 遮挡贴图用于提升模型间接光影效果.间接光源可能 ...
- Unity Shader 学习笔记(27)渲染轮廓线(描边)方法、卡通风格渲染、素描风格渲染
Unity Shader 学习笔记(27)渲染轮廓线(描边)方法.卡通风格渲染.素描风格渲染 参考书籍:<Unity Shader 入门精要> 渲染轮廓线(描边) 五种方法: 基于观察角度 ...
- Unity Shader - 描边效果
原文链接:http://blog.csdn.net/puppet_master/article/details/54000951 简介 描边效果是游戏里面非常常用的一种效果,一般是为了凸显游戏中的某个 ...
- Unity VR开发中UI始终优先渲染不被物体遮挡
Unity VR开发中UI始终优先渲染不被物体遮挡 在用Vive开发VR的时候,3DUI很容易被场景中的物体遮挡,解决办法是使用一个Shader:Overlay.shader,这个shader很好找, ...
最新文章
- iOS 实现不定参数方法
- 一个菜鸟程序员的游戏开发心得
- mac 如何配置mysql_MAC下安装与配置MySQL
- 人脸检测三个算法比较
- 一天一种设计模式之三-----单例模式
- ccs 移植创建新工程_CCS-6-新建TMS320F28335工程(可移植)).pdf
- 版本向量 使用css时正确区分IE版本[转]
- logistic regression及其Python实现
- Swift3数组编辑
- 2018为什么你一定要学Python
- 偏向锁、轻量级锁和重量级锁
- html实现多选框传值,解决Django中checkbox复选框的传值问题
- FPGA纯逻辑资源解码CameraLink视频,附带工程源码并详解
- Simulink 环境基础知识(二十一)--优化、估计和扫描模块参数值
- 《开源安全运维平台--OSSIM最佳实践》节日期间当当自营店 五折 优惠活动开始啦!...
- 我想加入阿里,我该怎么做
- 华为电脑怎么把虚拟化打开_【解决方案】华为虚拟化解决方案
- vue控件a-date-picker设置默认值的方式以及注意事项
- 从备份升级到容灾,利用华为云就可以做到的灾备方案
- 浮云绘图编辑器之文字、图片基础图元操作及源码,用于文本描述及拓扑图、平面布局图开发
热门文章
- delphi Android 创建缩略图,用Delphi实现缩略图查看
- Switch语句流程图
- 脉脉林凡:职场社交突破点在于行业间的融合
- 使用ijkplayer进行视频播放
- [xia谈]做一个爱家的程序员
- 他选歌太纠结了,有时临比赛开始的前一天才能
- 1548_AURIX_TC275_锁步比较逻辑LCL
- 面试官:你了解大厂的接口设计原则么?就会curd的我当场自闭
- android 映客弹幕实现方法,映客如何关闭弹幕 关闭弹幕方法
- java歌词解析器_Java swing实现音乐播放器桌面歌词字体变色效果|chu