程序干货:日式卡通渲染基础技术(下)

Z

2022-12-213419次浏览

1评论

28收藏

7点赞

分享

文章分为上下篇,为了更好的阅读感受,请按顺序阅读。上篇链接:日式卡通渲染基础技术(上)


三、描边

描边在日式卡通渲染中主要是为了将物体的轮廓显示出来,这样可以将物体与背景隔离,形成强烈的对比。

描边按实现方式可以分为以下三种:

  • 基于视点的描边

  • 基于过程几何方法的描边

  • 基于边缘检测的描边

1.基于视点方向的描边

使用视点方向(view point) 和 表面法线(surface normal) 之间的点乘结果得到轮廓线信息。

点乘结果越接近于0,说明这个表面更大可能是在侧向的视角方向,则我们可以将其当作轮廓边缘进行描边。

(1)实现

(2)特点

  • 开销小,只需要一个Pass

  • 效果差(如下图)

2.基于过程几何方法的描边

基于过程几何方法的描边的基本步骤有以下两步:

①渲染正向表面(剔除背面)

②肉渲染背向表面(剔除正面)

他使用双Pass来进行描边,分为Z-Bias和BackFacing两种。

(1)Z-Bias

这个方法是通过在观察空间,将模型沿z轴移动然后绘制描边层来进行实现

实现

使用两个Pass,一个Pass绘制主体,一个Pass绘制描边。

特点

-双Pass实现

-实现简单

-在某些视角下,存在瑕疵

(2)Back Facing

Back Facing是使用两个Pass实现的,其中一个Pass绘制本体,另一个Pass绘制沿法线向外扩张的描边。

特点:

  • 双Pass实现

  • 解决了Z-Bias在某些视角下描边位置偏移的问题

实现

v.vertex.xyz = v.vertex.xyz + v.normal * _OutlineWidth;

基本思想就是将顶点沿法线方向扩张,然后在其前方绘制物体本体。

注意点

绘制描边时,需要将正面剔除,否则会产生对本体的遮挡。

存在的问题

描边的宽度会随着物体离相机的远近而变化,描边的宽度现在是相对世界空间不变的,这相机拉近后,描边就会变粗。

解决方案:转到NDC空间进行扩张

主要实现方式是将法线转到ndc空间,然后需要注意获得屏幕宽高比,否则描边边缘会出现不均匀的情况。

float4 nearUpperRight = mul(unity_CameraInvProjection, float4(1, 1, UNITY_NEAR_CLIP_VALUE, _ProjectionParams.y));

float aspect = abs(nearUpperRight.y / nearUpperRight.x);

另外还做了一个优化:添加一张描边贴图用于控制描边宽度大小。

具体实现代码如下图:

效果如下图:

3.使用边缘检测实现描边(基于图像检测)

主要思想:通过使用边缘检查算子对图像数据进行卷积操作来找出轮廓

(1)卷积

(2)常见的卷积算子

-Roberts算子对边缘比较敏感,适合具有陡峭边缘且含噪声少的图像

-Sobel算子对噪声较多的图像处理效果更好

(3)实现细节

代码中使用的算子是Sobel算子。

①求出近似梯度值

②根据Edge显示边缘

(4)实现效果

(5)思考与改进

-使用该种方式得到的结果会存在一些不合适的边缘,主要是因为Sobel算子对边缘的不敏感导致,可以考虑换成别的算子。

-另外使用此种方式也存在一些局限性,例如无法通过参数来微调局部的边缘宽度等。

-可以考虑使用基于深度和法线的边缘检测方法来获得更好的效果。

四、头发渲染

对于头发的渲染,这里使用的是各向异性的头发渲染着色模型Kajiya-Kay模型。通过使用合适的法线偏移贴图,我们可以达到日式卡通中W型头发高光的效果。

1.Kajiya-Kay模型

Kajiya-Kay模型是一个非常经典的头发渲染模型,《神秘海域》中头发的实现也是在这一渲染模型的基础上实现的。

2.Kajiya-Kay的实现

实现这个渲染模型有一个基础的前提,就是头发的面的切线方向需要从发根指向发尾。有了上面这个基础,我们可以通过使用偏移贴图对切线进行偏移以获得抖动的高光。

(1)Kajiya-Kay中的高光

Kajiya-Kay中高光的实现有别于Blinn-Phong中使用半程向量(H)和法线方向(N)的点积(HdotN)这种计算方式,而使用了半程向量(H)和发尾至发根方向(T)的夹角正弦值(sin(T,H))来进行高光的计算。根据三角函数的公式可以知道,sin(T,H)=dot(H,N)。

具体推导过程如下(参照下方向量图进行推导):

(2)双高光项

在Kajiya-Kay模型中,我们使用双高光项进行叠加渲染,这是基于日常对于头发的一个观察:

Kajiya-Kay中高光项的计算主要有以下步骤:

  • 1.将高光在发根->发尾方向上进行随机偏移

  • 2.重复偏移高光的方法获得两个高光项

  • 3.对两个高光项进行叠加

(3)对高光进行偏移

这里会使用一张偏移贴图来获得不同位置的高光偏移量,通过高光偏移量与法线相乘后并与切线求和,我们就可以将高光进行偏移。

具体代码如下:

float3 ShiftTangent(float3 T, float3 N, float shift)

{

      return normalize(T + N * shift);

}

这是我们使用的偏移贴图:

(4)高光项的计算

高光项的计算和Blinn-Phong类似,也是进行一个Pow操作:

float StrandSpecular(float3 T, float3 V, float3 L, float exponent)

{

      float3 H = normalize(L + V);

      float dotTH = dot(T, H);

      float sinTH = sqrt(1 - dotTH * dotTH);

      float dirAtten = smoothstep(-1.0, 0.0, dotTH);

      return dirAtten * pow(sinTH, exponent);

}

其中需要注意的是dirAtten这一项,他的作用是当T和H的夹角变化时,控制高光的衰减。他遵循以下规则:

  • T,H夹角为钝角时,进行衰减

  • T,H夹角为锐角时,不衰减

接下来是头发光照的函数,需要注意的是,其实公司中的T,传入的是副法线。

fixed4 HairLighting(float3 tangent, float3 normal, float3 lightDir, float3 viewDir, float2 uv)

{

   fixed3 shiftColor = tex2D(_HairTex, uv);

   float shiftTex = shiftColor.g;

   float noise = shiftColor.b;

   float3 t1 = ShiftTangent(tangent, normal, _PrimaryShift + shiftTex);

   float3 t2 = ShiftTangent(tangent, normal, _SecondaryShift + shiftTex);

   float3 specular = _SpecularColor1 * StrandSpecular(t1, viewDir, lightDir, _Glossiness1);

   specular += _SpecularColor2 * StrandSpecular(t2, viewDir, lightDir, _Glossiness2) * noise;

  fixed4 o;

   o.rgb = specular;

   return o;

}

通过以上的计算,可以获得下面的效果:

五、眼睛

当眼睛被头发遮挡时,在日式卡通渲染中做法是将被头发遮挡的眼睛显示出来,这里的实现原理比较简单,主要使用了模板测试来进行。

(1)实现

在进行眼睛渲染的Pass时,将模板值设置为某一固定值(代码如下):

Stencil {

        Ref 1

   Comp Always

   Pass Replace

   Fail Replace

}

如以上的代码,我们在模板测试成功/失败后都会将模板值改为1(当然你也可以指定其它值)。接着在对头发进行渲染的Pass内,对模板值进行比较,让模板值不等于1的片元通过,通过这种方式则可以渲染出头发遮挡下的眼睛。头发的Shader如下:

Stencil {

         Ref 1

   Comp NotEqual

   Pass Keep

   Fail Keep

}

如以上的代码,我们在模板测试成功/失败后都会将模板值改为1(当然你也可以指定其它值)。接着在对头发进行渲染的Pass内,对模板值进行比较,让模板值不等于1的片元通过,通过这种方式则可以渲染出头发遮挡下的眼睛。头发的Shader如下:

Stencil {

     Ref 1

     Comp NotEqual

     Pass Keep

     Fail Keep

}

具体效果可见UTS2中的人物表现:

(2)本村线

本村线是由《罪恶装备》的TA提出来的内轮廓线方案。普通的手绘内轮廓线方案,在进行放大时会出现锯齿:

通过使用本村线的方案,在模型放大后也不会出现走样的问题:

本村线的原则

-内轮廓线与u轴v轴平行

-没有手绘的轮廓线

-需要美术对UV进行拆分、排列


参考:

https://www.bilibili.com/read/cv3347514

https://zhuanlan.zhihu.com/c_1215952152252121088

https://blog.csdn.net/puppet_master/article/details/83759180

https://blog.csdn.net/poem_of_sunshine/article/details/79853066

http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/Scheuermann_HairRendering.pdf

https://learnopengl-cn.readthedocs.io/zh/latest/02%20Lighting/02%20Basic%20Lighting/

评论 (1)

0/1000
网易游学APP
为热爱赋能
扫描二维码下载APP