零. 前言
轮廓描边是卡通渲染中常用的技术,适当的描边会给人一种卡通漫画的感觉.
轮廓描边的方法有很多种,运用模板缓冲可以绘制模型轮廓描边效果,不过它能产生不同于其他描边方法的效果噢.
上篇文章我们了解了关于模板缓冲和模板测试基础理论概念,本章我们就进行理论的实践,带领大家了解如何用模板缓冲绘制模型轮廓描边效果.
还没看过上篇文章的同学,赶快花个5分钟康康吧→https://indienova.com/u/1149119967/blogread/25692
一. 轮廓描边的思路
(1)手绘练习!
首先大家大家发散想一想,假设我们在纸上有一个白色的圆形噢↓我们能怎么给这个圆形来描边咧?
想好再看哦
嘿嘿
肯定有个别手比较抖的同学,因为手太抖了,根本不能很好的描到圆的边界,横七竖八的很难看啊.就像下面那张图一样↓ 额..
好吧,那我们照顾一下手抖的同学吧,我们再想想另外一种办法吧..
欸有了!!! 我们拿个大点的圆形尺子,在原来的基础上拿铅笔轻轻地再画一个更大黑色的圆,并填充它,像↓这样
然后,我们小心地拿橡皮擦轻轻地擦掉中间的黑色铅笔痕迹
铛铛铛!!我们就得到了有黑色描边轮廓的圆辣!!!
好的,我们就结束咯,这就是我们的描边思路.
肯定有同学就会说: 不对啊,你这是手绘啊,游戏里的3d模型不能用啊,总不能把手伸进去屏幕吧???
别着急嘛,下面我们再想想办法~
(2) 三维联想!
我们发散一下噢~,从二维平面空间拓展到三维立体空间里,
我们首先在Unity建一个3D白色圆球模型 ,就像下面这样↓
然后,我们再建一个比较大黑色的圆球模型覆盖原来白球,就像这样↓
最后,也就是重点,在我们视角方向看去,挖掉黑色中间表面的面片,露出里面的白球
铛铛铛,出现了!!!轮廓描边效果!!
有同学又说了:"啊!!!我懂了...可是跟模板测试有什么关系的?"
别急别急,来来来,我这就说给你听~
(3)运用模板测试!
我们在上一章说过咯,模板测试可以再图形渲染出来颜色后,根据模板缓冲的值进行比对,比对不通过就丢去此像素颜色~
在上图中假设我们先建一个白色圆球模型,假设它的参考值是0, 我们再建一个黑色圆球模型,假设它的参考值是1,
然后我们把黑色圆球模型覆盖掉白色模型.
我们现在来分析分析,在没有重合的外围处,肯定是黑色的模型,没错ok吧,简单~
然而里面重头戏来了,在他们重合的地方运用模板测试进行比对,1等不等于0?不等于0, 好,我们就把黑色丢掉,只露出里面白色.这样就达到了黑色外轮廓,白色内容的模型了,即轮廓描边!
二. 程序实现
1. 首先创建Unlit Shader和材质
2. 程序思路:
代码上有两个关键点:
在SubShader下设置Stencil 和 两个Pass分别用来渲染原本模型和偏大用于描边效果黑色模型.
Stencil{ Ref [_RefValue] Comp Equal Pass IncrSat }
_RefValue就是我们设置的参考值,默认需要写为0,因为默认的Stencil Buffer中的Stencil值是为0,一开始需要和0比较是否相等。
Equal表示当参考值与缓冲值相等时才算通过.
而IncrSat是重点!!! IncrSat 意思时当一个Pass渲染完成并且通过了模板测试后,对参考值+1;
第一个Pass除了就是正常渲染的模型,还往Stencil Buffer里写入了参考值假设为0吧.
然后IncrSat效果对参考值进行了+1操作,这时第二个Pass渲染的时候参考值已经是1了,在模型重合的地方会和上一个Pass写入模板缓冲中的0进行比对,1和0显然是不一样的,就抛弃此(黑)颜色,从而保持原有的白色.
不过还有个小问题: 那我们怎么得到黑色偏大的模型呢?
在第二个Pass里黑色模型我们可以在原有模型数据上,对其各个顶点沿法向扩张一点点达到膨胀效果,这样我们就获得比原有还要大一点的模型辣~
3. 具体代码如下
Shader "Unlit/StencilBufferOutline" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color("Color",Color) = (1,1,1,1) _RefValue("Stencil RefValue",Int) = 0 _Outline("Outline Width",Range(0,0.1)) = 0.05 _OutlineColor("Outline Color",Color) = (0,0,0,1) } SubShader { Tags { "RenderType"="Opaque" } Stencil{ Ref [_RefValue] Comp Equal Pass IncrSat } Pass { //..正常渲染原来模型,我们这里只渲染白色 } //渲染偏大用于描边效果黑色模型 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; }; fixed _Outline; fixed4 _OutlineColor; v2f vert(a2v v) { v2f o; //对其各个顶点沿法向扩张一点点达到膨胀效果 v.vertex = v.vertex + float4(normalize(v.normal) *_Outline,1); o.pos = UnityObjectToClipPos(v.vertex); return o; } //这里只返回黑色颜色噢 fixed4 frag (v2f i) : SV_Target { return _OutlineColor; } ENDCG } } FallBack "Diffuse" }
三.效果展现
下图就是最终效果啦:
使用模板描边和其他描边方法有一个很大的区别就是,在使用同一模板参考值的物体交界处会发现边界融合在一起了.
就如右边胶囊体和圆球的交界处. 大家自己可以思考一下为什么噢~(答案下一章揭晓哈哈哈哈哈哈哈哈
参考资料
模板测试:
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/02%20Stencil%20testing/
外描边实际上还有个更简单的办法是直接把黑球的法线反转一下……
@芒苔:嗯嗯是哒,这也是一种很聪明的方法..法线反转后正面变背面,再剔除掉背面(也就是原来的正面).然后就是外描边效果了. 之前有听说《大神》那游戏也是这样的描边技术,不知道是不是真的...
最近由 阿创 修改于:2020-04-24 21:59:52