您的位置:首页 > 移动开发 > Unity3D

【Unity ImageEffect】一个用于角色遮挡透视轮廓的效果

2016-01-12 10:15 936 查看

前言

有些游戏项目需要在角色被遮挡后仍然显示出来,让玩家不会迷失自我的感觉,但一般不会用原颜色渲染出来,一般会用另外的颜色表示。本文介绍一种当角色被遮挡后,用另外一种颜色并有轮廓渐变效果的渲染方法。

方案

Shader方式:在角色Shader加一道Pass,在深度检测时判断被遮挡后渲染角色轮廓。
ImageEffect方式:利用Camera的深度信息,判断角色被遮挡后渲染角色轮廓。

实现

Shader方式:


Shader "Yogi/Occlusion"
{
Properties
{
_BumpMap("Normalmap", 2D) = "bump" {}
_OccColor("Occlusion Color", Color) = (1, 1, 1, 1)
_OccPower("Occlusion Power", Range(0.0, 2.0)) = 0.5
_Alpha("Alpha", Range(0, 1)) = 1
}

SubShader
{
Tags
{
"Queue" = "Transparent+1"
"RenderType" = "Opaque"
"IgnoreProjector" = "True"
}

Pass
{
Name "BASE"

Blend SrcAlpha OneMinusSrcAlpha
Fog { Mode Off }
Lighting Off
ZWrite Off
ZTest Greater

CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest

sampler2D _BumpMap;
fixed4 _OccColor;
fixed _OccPower;

struct a2v
{
fixed4 vertex : POSITION;
fixed3 normal : NORMAL;
fixed4 tangent : TANGENT;
fixed4 texcoord : TEXCOORD0;
};

struct v2f
{
fixed4 pos : SV_POSITION;
fixed2 uv : TEXCOORD0;
fixed3 viewDir : TEXCOORD1;
};

v2f vert(a2v v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xy;

TANGENT_SPACE_ROTATION;
o.viewDir = normalize(mul(rotation, ObjSpaceViewDir(v.vertex)));

return o;
}

fixed4 frag(v2f i) : SV_Target
{
fixed3 n = UnpackNormal(tex2D(_BumpMap, i.uv));
fixed o = 1 - saturate(dot(n, i.viewDir));
fixed4 c = _OccColor * pow(o, _OccPower);

return c;
}

ENDCG
}
}

FallBack "Mobile/Diffuse"
}
在角色的Shader上加上一个 UsePass "Yogi/Occlusion/BASE" 就可以看到效果了

ImageEffect方式:


Shader "Yogi/ImageEffect/Occlusion"
{
Properties
{
_MainTex("Base (RGB)", 2D) = "white" {}
_DepthMap("DepthMap (RGB)", 2D) = "white" {}
_OcclusionMap("OcclusionMap (RGB)", 2D) = "white" {}
_Intensity("Intensity", Float) = 0.0
_Tiling("Tiling", Vector) = (1.0, 1.0, 0.0, 0.0)
}

SubShader
{
Pass
{
ZTest Always
ZWrite Off
Cull Off
Fog{ Mode Off }

CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest

sampler2D _MainTex;
sampler2D _DepthMap;
sampler2D _OcclusionMap;
sampler2D _CameraDepthNormalsTexture;
fixed4 _MainTex_TexelSize;
fixed _Intensity;
fixed _Power;
fixed4 _Tiling;

struct a2v
{
fixed4 vertex : POSITION;
fixed2 texcoord : TEXCOORD0;
};

struct v2f
{
fixed4 vertex : SV_POSITION;
fixed2 uv : TEXCOORD0;
};

v2f vert(a2v v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xy;

return o;
}

fixed4 frag(v2f i) : SV_Target
{
fixed4 c = tex2D(_MainTex, i.uv);

fixed4 depthMap = tex2D(_DepthMap, i.uv);
fixed depth = DecodeFloatRG(depthMap.zw);
fixed3 normal = DecodeViewNormalStereo(depthMap);

fixed4 cameraDepthMap = tex2D(_CameraDepthNormalsTexture, i.uv);
fixed cameraDepth = DecodeFloatRG(cameraDepthMap.zw);

fixed4 o = c;
if (depth > 0
&& cameraDepth < depth)
{
fixed2 uv = i.uv * _Tiling.xy + _Tiling.zw;
fixed3 color = tex2D(_OcclusionMap, uv);
fixed nf = saturate(dot(normal, fixed3(0, 0, 1)));
nf = pow(nf, _Intensity);
o.rgb = lerp(color, c.rgb, nf);
}

return o;
}

ENDCG
}
}

Fallback off
}


using UnityEngine;

[RequireComponent(typeof(Camera))]
[ExecuteInEditMode]
public class OcclusionEffect : MonoBehaviour
{
private const string NODE = "Occlusion Camera";

[Range(0.0f, 10.0f)]
public float intensity = 1.0f;
public Vector4 tiling = new Vector4(1, 1, 0, 0);
public Texture2D occlusionMap;
public LayerMask cullingMask;

private Camera occlusionCamera
{
get
{
if (null == m_OcclusionCamera)
{
Transform node = transform.FindChild(NODE);
if (null == node)
{
node = new GameObject(NODE).transform;
node.parent = transform;
node.localPosition = Vector3.zero;
node.localRotation = Quaternion.identity;
node.localScale = Vector3.one;
}

m_OcclusionCamera = node.GetComponent<Camera>();
if (null == m_OcclusionCamera)
{
m_OcclusionCamera = node.gameObject.AddComponent<Camera>();
}

m_OcclusionCamera.enabled = false;
m_OcclusionCamera.clearFlags = CameraClearFlags.SolidColor;
m_OcclusionCamera.backgroundColor = new Color(0, 0, 0, 0);
m_OcclusionCamera.renderingPath = RenderingPath.VertexLit;
m_OcclusionCamera.hdr = false;
m_OcclusionCamera.useOcclusionCulling = false;
m_OcclusionCamera.gameObject.hideFlags = HideFlags.HideAndDontSave;
}

return m_OcclusionCamera;
}
}
private Camera m_OcclusionCamera;

private Shader depthShader
{
get
{
if (m_DepthShader == null)
{
m_DepthShader = Shader.Find("Hidden/Camera-DepthNormalTexture");
}

return m_DepthShader;
}
}
private Shader m_DepthShader = null;

private Material occlusionMaterial
{
get
{
if (m_OcclusionMaterial == null)
{
m_OcclusionMaterial = new Material(Shader.Find("Yogi/ImageEffect/Occlusion"));
m_OcclusionMaterial.hideFlags = HideFlags.HideAndDontSave;
}

return m_OcclusionMaterial;
}
}
private Material m_OcclusionMaterial = null;

private RenderTexture depthMap;

private void OnPreRender()
{
depthMap = RenderTexture.GetTemporary(Screen.width, Screen.height, 16);

camera.depthTextureMode = DepthTextureMode.DepthNormals;

occlusionCamera.fieldOfView = camera.fieldOfView;
occlusionCamera.isOrthoGraphic = camera.isOrthoGraphic;
occlusionCamera.nearClipPlane = camera.nearClipPlane;
occlusionCamera.farClipPlane = camera.farClipPlane;
occlusionCamera.cullingMask = cullingMask;
occlusionCamera.targetTexture = depthMap;
occlusionCamera.RenderWithShader(depthShader, string.Empty);
}

private void OnPostRender()
{
RenderTexture.ReleaseTemporary(depthMap);
}

private void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)
{
occlusionMaterial.SetTexture("_DepthMap", depthMap);
occlusionMaterial.SetTexture("_OcclusionMap", occlusionMap);
occlusionMaterial.SetFloat("_Intensity", intensity);
occlusionMaterial.SetVector("_Tiling", tiling);
Graphics.Blit(sourceTexture, destTexture, occlusionMaterial);
}

private void OnDisable()
{
OnDestroy();
}

private void OnDestroy()
{
if (null != m_OcclusionCamera)
{
if (Application.isPlaying)
{
Destroy(m_OcclusionCamera.gameObject);
}
else
{
DestroyImmediate(m_OcclusionCamera.gameObject);
}
}
}
}


最后

两种方式实现出来的效果是不一样的,Shader方式能表现法线纹理细节,ImageEffect方式只能表现模型法线,因为是从摄像机深度法线贴图而来的。但是Shader方式需要更高的渲染序列,并且无时无刻占用多一个Pass也因此多了一个DC,而ImageEffect方式比Shader拥有更高的控制自由度,可以单独设置给一个玩家或者某几个玩家渲染。Shader的透视效果需要关闭ZWrite,因此会有叠面的效果,而ImageEffect则没有。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: