半透明物体与非透明物体接缝柔和处理
前言
最近项目中遇到特效和地面的接触的时候,他们的接触面间有着一条很明显的接缝。当镜头拉进的时候这个情况尤为明显。幸好同事找到了一篇文章来解决这个问题。文章中说的是在Cocos
Creator中的实现,这里我就按照文章的思路来做出Unity
Shader的形式。且为了我自己方便查找,我自然是要写一篇文章来记录一下啦。
核心思想
我们先获取到深度图中在视图空间下的深度,和自身在视图空间下的深度。然后我们通过一定的公式来修改其颜色或是其透明通道。
具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| Shader "Test/DepthTransit" { Properties { _Color ("Color", Color) = (1,1,1,1) _Near ("Near",float) = 0.02 _Far ("Far",float) = 10 } SubShader { Tags { "RenderType"="Transparent" "Queue"="Transparent" } Blend SrcAlpha OneMinusSrcAlpha Cull off ZWrite Off ZTest LEqual
Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
struct appdata { float4 vertex : POSITION; };
struct v2f { float4 screenPos : TEXCOORD0; float4 vertex : SV_POSITION; };
CBUFFER_START(UnityPerMaterial) half4 _Color; float _Near; float _Far; CBUFFER_END
v2f vert (appdata v) { v2f o; o.vertex = TransformObjectToHClip(v.vertex.xyz); o.screenPos = ComputeScreenPos(o.vertex); //o.screenPos = o.screenPos / o.screenPos.w; // 获取视图空间下的深度值 o.screenPos.z = -TransformWorldToView(TransformObjectToWorld(v.vertex)).z; return o; }
half4 frag (v2f i) : SV_Target { // sample the texture half4 col = _Color; // 将深度图中的信息转为视图空间下的深度值 float depth = LinearEyeDepth(SampleSceneDepth(i.screenPos.xy / i.screenPos.w),_ZBufferParams); col *= clamp((depth - i.screenPos.z - _Near) * _Far, 0,1); return col; } ENDHLSL } } }
|
文章中的公式其实是:
1 2
| float diff = clamp((depth - screenUV.z - 0.02) / 0.1, 0.0, 1.0); o.rgb *= diff;
|
我这里是直接改为了:
1
| col *= clamp((depth - i.screenPos.z - _Near) * _Far, 0,1);
|
这样大家可以自行调整参数来看变化。至于为什么是_Near
和_Far
,这是因为这样的操作很像Unity中软粒子的实现。下面是Unity中软粒子的其中一个实现方法:
1 2 3 4 5 6 7 8 9 10 11 12
| float SoftParticles(float near, float far, ParticleParams params) { float fade = 1; if (near > 0.0 || far > 0.0) { float rawDepth = SampleSceneDepth(params.projectedPosition.xy / params.projectedPosition.w); float sceneZ = (unity_OrthoParams.w == 0) ? LinearEyeDepth(rawDepth, _ZBufferParams) : LinearDepthToEyeDepth(rawDepth); float thisZ = LinearEyeDepth(params.positionWS.xyz, GetWorldToViewMatrix()); fade = saturate(far * ((sceneZ - near) - thisZ)); } return fade; }
|
至于大家要直接乘以最终的颜色值还是只能修改最终颜色的透明度,那就要看各位的需求了。
参考文章
- Cocos
Shader入门基础七:一文彻底弄懂深度图:https://zhuanlan.zhihu.com/p/479574432