氵一篇最近群友聊到的新边缘光,使用深度计算,相对于传统边缘光可能更适合硬表面。
https://github.com/Jason-Ma-233/JasonMaToonRenderPipelinegithub.com
效果/关闭/强度提高/原始法线与平滑法线切换 预览图:
传统的N/H dot V的边缘光:

(FOV比较大,看着有点怪,正交和小FOV则会导致一些相关效果缩放出问题,之后会修)
代码相对简单:
float2 L_View = normalize(mul((float3x3)UNITY_MATRIX_V, context.L).xy);float2 N_View = normalize(mul((float3x3)UNITY_MATRIX_V, lerp(context.N, context.SN, _RimLightSNBlend)).xy);float lDotN = saturate(dot(N_View, L_View) + _RimLightLength * 0.1);float2 ssUV = posInput.positionSS + N_View * lDotN * _RimLightWidth * input.color.b * 40 * GetSSRimScale(posInput.linearDepth);float depthTex = LoadCameraDepth(clamp(ssUV, 0, _ScreenParams.xy - 1));float depthScene = LinearEyeDepth(depthTex, _ZBufferParams);float depthDiff = depthScene - posInput.linearDepth;float intensity = smoothstep(0.24 * _RimLightFeather * posInput.linearDepth, 0.25 * posInput.linearDepth, depthDiff);intensity *= lerp(1, _RimLightIntInShadow, context.shadowStep) * _RimLightIntensity * mask; float3 ssColor = intensity * lerp(1, context.brightBaseColor, _RimLightBlend)* lerp(_RimLightColor.rgb, context.pointLightColor, luminance * _RimLightBlendPoint); c = max(c, ssColor);原理更简单:取当前像素屏幕UV,向某一方向偏移后采样深度,与当前像素深度进行比较,深度差大于某一阈值则为边缘。
传统边缘光由于主要使用法线计算,在硬表面上可能是这样:

屏幕空间方法则可以取得较好的效果:

是不是有点像描边?
虽然原理很简单,但是可以做的事情其实还有很多。上面那一小段代码中额外加入了几个特性:
在一般法线和平滑法线间lerp
宽度根据距离改变,1到0变宽,1到无限远变窄,为此学了下用matlab拟合曲线
深度差阈值与距离成正比,越近边缘光细节越多
宽度根据L dot N进行衰减,当然也可以不衰减得到等宽的边缘光
对深度差使用smoothstep进行羽化
当然还有更多的可能性,稍微改一改可能变成完全不同的效果。比如再对Gbuffer法线进行采样(说来惭愧还没实质性写过延迟相关),进行某些操作。或者是某些内描边,甚至是做发影(在欧根试过效果不好,看来还得多问问colin大佬)
往期精选
Unity3D游戏开发中100+效果的实现和源码大全 - 收藏起来肯定用得着
Shader学习应该如何切入?
喵的Unity游戏开发之路 - 从入门到精通的学习线路和全教程
声明:发布此文是出于传递更多知识以供交流学习之目的。若有来源标注错误或侵犯了您的合法权益,请作者持权属证明与我们联系,我们将及时更正、删除,谢谢。
作者:喵刀Hime
原文:https://zhuanlan.zhihu.com/p/139290492
More:【微信公众号】 u3dnotes
