unity3d的Crease描边效果非常不错,所以分析了一下这个效果。
用到3个shader:
ConvertDepth //渲染depthmap
CreaseApply //描边
SeparableBlur//模糊
这个效果的重点是 1.怎么查找边缘 2.怎么模糊边缘
unity3d的方法是:
采用2张depthmap,一张清晰,一张模糊,模糊的depthmap上可以看到边缘都模糊了,两种图做差,就知道边缘在哪里了。
看代码:return color * (1.0-abs(hrDepth.a-lrDepth.a)*intensity);
假如abs(hrDepth.a-lrDepth.a)即深度差越大,值越大,不是边缘的部分模糊之后值是不会有变化的,原理很简单。而且模糊之后这个差也能有渐变的效果,所以最后的描边可以比较虚幻。但是消耗是很大的,会增加drawcall,overdraw比较高,模糊的shader用到了很多次的图片采样,而且是在frag shader里面采样的,手机上的显卡填充率本来就不高,根本就用不了。
下面是深度渲染的shader:
Shader "Hidden/ConvertDepth" {
Properties {
_MainTex ("Base (RGB)", 2D) = "" {}
}
CGINCLUDE
#include "UnityCG.cginc"
struct v2f {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
sampler2D _CameraDepthTexture;
v2f vert( appdata_img v )
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xy;
return o;
}
half4 frag(v2f i) : COLOR
{
float d = UNITY_SAMPLE_DEPTH( tex2D(_CameraDepthTexture, i.uv.xy) );
d = Linear01Depth(d);
if(d>0.99999)
return half4(1,1,1,1);
else
return half4(d,d,d,1);//灰度显示的深度图
//return EncodeFloatRGBA(d);
}
ENDCG
Subshader {
Pass {
ZTest Always Cull Off ZWrite Off
Fog { Mode off }
CGPROGRAM
#pragma fragmentoption ARB_precision_hint_fastest
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
Fallback off
}
其中的EncodeFloatRGBA函数是为了能够更精确的存储深度值,具体算法如下:
// Encoding/decoding [0..1) floats into 8 bit/channel RGBA. Note that 1.0 will not be encoded properly.
inline float4 EncodeFloatRGBA( float v )
{
float4 kEncodeMul = float4(1.0, 255.0, 65025.0, 160581375.0);
float kEncodeBit = 1.0/255.0;
float4 enc = kEncodeMul * v;
enc = frac (enc);
enc -= enc.yzww * kEncodeBit;
return enc;
}
inline float DecodeFloatRGBA( float4 enc )
{
float4 kDecodeDot = float4(1.0, 1/255.0, 1/65025.0, 1/160581375.0);
return dot( enc, kDecodeDot );
}