在unity后效中可以创建自定义效果,但是后效中获取不到stencil,这里采用render feature draw mesh的方法,在相机前方绘制一个quad片和场景直接进行blend,这样这个方片是能够拿到stencil的,后续可能会有其他问题。这里作为尝试进行实现。
C#部分
public class ScreenOutlineFeature : ScriptableRendererFeature
{
private Material _material;
private const string screenOutline = "path/ScreenOutline";
private ScreenOutlinePass screenOutlinePass;
...
public override void AddRenderPasses(ScriptableRenderer renderer)
{
if (screenOutlinePass == null)
screenOutlinePass = new ScreenOutlinePass(Event);
if (!GetMaterial()) return;
screenOutlinePass.Setup(_material);
renderer.EnqueuePass(screenOutlinePass);
}
Boolean GetMaterial()
{
if (_material == null)
{
var outLineShader = Shader.Find(screenOutline);
if (outLineShader == null)
{
Debug.Log($"Check for missing shader {screenOutline}");
return false;
}
_material = CoreUtils.CreateEngineMaterial(Shader.Find(screenOutline));
}
if (_material == null)
{
Debug.LogWarning("Missing screenOutline material. effect will not Active. Check for missing shader");
return false;
}
if (!_material.shader.isSupported) return false;
return true;
}
}
public class ScreenOutlinePass : ScriptableRenderPass
{
const string m_ProfilerTag = "Screen Outline";
private ProfilingSampler m_ProfilingSampler = new ProfilingSampler(m_ProfilerTag);
public Material _material;
public ScreenOutlinePass(RenderPassEvent evnt)
{
this.renderPassEvent = evnt;
}
public void Setup(Material material)
{
this._material = material;
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
if (_material == null) return;
var camera = renderingData.cameraData.camera;
CommandBuffer cmd = CommandBufferPool.Get(m_ProfilerTag);
using (new ProfilingScope(cmd, m_ProfilingSampler))
{
cmd.Clear();
context.ExecuteCommandBuffer(cmd);
cmd.SetViewProjectionMatrices(Matrix4x4.identity, Matrix4x4.identity);
cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, _material);
cmd.SetViewProjectionMatrices(camera.worldToCameraMatrix, camera.projectionMatrix);
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
}
Shader部分
Stencil {
Ref [_Stencil]
Comp Equal
Pass keep
}
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
ZWrite Off
ZTest Always
float3 ReconstructWorldPos(half2 screenPos, float depth)
{
#if defined(SHADER_API_GLCORE) || defined (SHADER_API_GLES) || defined (SHADER_API_GLES3)
depth = depth * 2 - 1;
#endif
#if UNITY_UV_STARTS_AT_TOP
screenPos.y = 1 - screenPos.y;
#endif
float4 raw = mul(UNITY_MATRIX_I_VP, float4(screenPos * 2 - 1, depth, 1));
float3 worldPos = raw.rgb / raw.a;
return worldPos;
}
float3 SampleSceneNormals(float2 uv)
{
return UnpackNormalOctRectEncode(SAMPLE_TEXTURE2D_X(_CameraNormalsTexture, sampler_ScreenTextures_linear_clamp, UnityStereoTransformScreenSpaceTex(uv)).xy) * float3(1.0, 1.0, -1.0);
}
float SampleScreenDepths(float2 uv)
{
return LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_ScreenTextures_linear_clamp,UnityStereoTransformScreenSpaceTex(uv).xy), _ZBufferParams);
}
half4 color = half4(lerp(fogColor.rgb, _OutlineColor.rgb, _OutlineColor.a), saturate(edge));
float GetEdge_D(float edgeSample[5])
{
float depth_0 = abs(edgeSample[1] - edgeSample[0]);
depth_0 += abs(edgeSample[2] - edgeSample[0]);
depth_0 += abs(edgeSample[3] - edgeSample[0]);
depth_0 += abs(edgeSample[4] - edgeSample[0]);
float edgeDepth = depth_0 * _OutlineDepthMultiplier;
return edgeDepth;
}
float GetEdge_N(float3 edgeSample[5])
{
float normal_0 = 1 - dot(edgeSample[1], edgeSample[0]);
normal_0 += 1 - dot(edgeSample[2], edgeSample[0]);
normal_0 += 1 - dot(edgeSample[3], edgeSample[0]);
normal_0 += 1 - dot(edgeSample[4], edgeSample[0]);
float edgeNormal = normal_0 * _OutlineNormalMultiplier;
return edgeNormal;
}
void Outline_float(float2 UV, float depth, float3 viewDir, out float edge)
{
float dis = LinearEyeDepth(depth, _ZBufferParams);
float depthDis = saturate((_OutlineEnd - dis) / (_OutlineEnd - _OutlineStart));
depthDis = lerp(_OutlineCurveMax, _OutlineCurveMin, depthDis);
float3 offset = float3((1 / _ScreenParams.x), (1 / _ScreenParams.y), 0) * depthDis;
float2 uvSamples[5];
float depthSamples[5];
float3 normalSamples[5];
uvSamples[0] = UV;
uvSamples[1] = UV - offset.xz;
uvSamples[2] = UV + offset.xz;
uvSamples[3] = UV + offset.zy;
uvSamples[4] = UV - offset.zy;
for (int i = 0; i < 5; i++)
{
depthSamples[i] = SampleScreenDepths(uvSamples[i]);
normalSamples[i] = SampleSceneNormals(uvSamples[i]);
}
float edgeDepth = GetEdge_D(depthSamples);
float edgeNormal = GetEdge_N(normalSamples);
float NdotV = 1 - dot(normalSamples[0], -viewDir);
float normalThreshold01 = saturate((NdotV - 0.1) / (1 - 0.1));
float normalThreshold = normalThreshold01 * _OutlineDepthBevelDamp + 1;
float depthThreshold = _OutlineDepthDisDamp * depthSamples[0] * normalThreshold;
edgeDepth = edgeDepth > depthThreshold ? 1 : 0;
edge = max(edgeDepth, edgeNormal);
float minDepth = _OutlineNear;
float depthSpan = _OutlineFar;
float alpha = lerp(1.0, 0.0, (clamp(dis, minDepth, minDepth + depthSpan) - minDepth) / depthSpan);
edge = edge * alpha;
}
|