每天一点UWA:卡通渲染笔记

描边的方法

基于视角的勾边

  • PS:这个是最简单的绘制边界的做法。计算方式就是dot(viewDir, normal)。根据 三角函数的定义值越小说明视角与模型某个点的表面法线的夹角越大,夹角为90°时说明这个triangle是视角看不到的,也就是边界了。

基于几何生成方法的描边

shell method
  • 首先在绘制结束正常的模型后,将需要描边的物体改用正面剔除再绘制一遍,在VS中将顶点沿着法线方向膨胀一定距离,然后在FS中将模型用纯色输出。
z-bias
  • 也是绘制背面,但不膨胀,而是把背面顶点的Z值稍微向前偏移一点点,使得背面的些许部分显示出来形成描边效果。

基于图像处理的描边

什么是“边缘”呢?
  • 边缘就是在深度或者法线上不连续的位置。
做法:
  • 需要将深度信息和法线信息以贴图的形式传入,运用边缘检测算法去寻找这些像素。
  • 这类方法的优点是描边的线宽一致,缺点是需要额外的法线和深度信息,当然,由于近年来流行的延迟渲染框架,法线和深度本来就是G-Buffer的一部分,因此往往不需要额外绘制法线和深度的信息。

美式卡通中的做法

  • 美式卡通中往往倾向于使用基于图像处理的描边方法来生成均匀一致的描边效果。在《英雄联盟》中小兵和英雄的勾边效果就是用Sobel算子对深度信息进行边缘检测来获得的。
  • 当物体较多时在进行正常绘制的阶段用stencil buffer标记出需要描边的物体,然后用一个全屏的后处理,对stencil buffer标记的像素进行边缘检测,当然这样的话,就很难给每个物体单独指定描边颜色了。

日式卡通中的做法

  • 日式卡通中往往倾向于使用基于几何体生成的方法去描边,这类描边方法相较于另两类方法的好处在于线宽更容易为美术所控制。
  • 在《GUILTY GEAR Xrd》中,角色的描边就是通过几何体生成的方法,结合了shell method和z-bias method,并引入了逐物体的顶点色来控制描边细节,同时也是为了保证描边粗细不会随着摄像机视距发生变化,具体来说,顶点色存储的信息包括:
    • R通道:控制toon shading的阈值,和描边无关,和着色有关,这个我们后面描述
    • G通道:控制顶点根据视距膨胀的强度(这个部分具体操作我也没有完全弄清楚,希望了解的朋友来补充)
    • B通道:控制描边的z-bias,越大则描边越不可见
    • A通道:控制描边的粗细

着色

Cel Shading

  • 只考虑光线

image
image
image

  • 同时考虑光线和视角

image
image

image

Tone Based Shading

  • Tone Based Shading的风格化是基于美术指定的色调插值,并且插值得到的色阶是连续的。首先需要由美术指定冷色调和暖色调,而最终模型的着色将根据法线和光照方向的夹角,在这两个色调的基础上进行插值,具体算法如下:

image
image

  • 其中,Kd仍是模型自身色彩贴图,Kblue,Kyellow和alpha,beta则均是自定义的参数。

image

基于tone based shading绘制的球体

日式卡通中的着色

  • 以《GUILTY GEAR Xrd》为例,它也一定程度上包含了Cel Shading和Tone Based Shading的部分思想,将色阶离散成为“明暗”两个色调,并由美术指定冷暖色调的颜色:

image
image

上述公式表示了这个卡通渲染的漫反射部分

threshold表示明暗交界的阈值,在游戏中通过顶点色的R通道来实现逐顶点的控制。

Kcool和Kwarm由美术逐物体地指定,Ksss是对模型次表面散射效果的模拟,对皮肤而言一般呈粉红色,通过美术绘制的SSS贴图来实现逐像素控制,并且只有暗部的像素才会受SSS贴图的影响。Kd是模型自身的颜色贴图。darkness表示了某个像素的明暗程度,用于确定色调的冷暖。

美术绘制的AO贴图,来实现一些边角缝隙的暗部效果。

  • 高光的计算更简单一些:基本上与blinn-phong一样

image
image

美式卡通中的着色

  • Valve在其游戏《军团要塞2》将卡通渲染着色分为了view dependent term和view independent term。两部分的计算分别如下:

image

image

image

  • PS:从公式中可以看到不依赖view的版本是l也就是光照向量,而依赖view的中是r,也就是反射计算。

image

风格化高光和阴影

可变形状的高光

  • 从日式着色的高光公式看,改变形状的关键在于H向量。这个H就是半角向量,参考Blinn-Phong公式。具体做法有:

    • 平移,改变高光的位置: image

    • 有方向的缩放,沿着切线空间的某个轴缩放高光形状:image

    • 分割,将一块连续的高光切分成两块:image

    • 方块化,将趋于圆形的高光变成方形:

      image

      image

      image

      image

风格化阴影

  • 类似于风格化的高光,风格化的阴影也是在标准的阴影计算流程之后,定义了一系列针对标准阴影的操作,通过这些操作,配合用户自定义的参数,便可以达到风格化阴影的效果,总的来说,共有四类操作:
    • 膨胀/腐蚀(Inflation):扩大或者缩小阴影范围,用参数i来控制
    • 亮度(Brightness):阴影区域的亮度,可以用于模拟半影区的效果,用参数b控制
    • 柔度(Softness):阴影边界处的柔和程度,用参数s控制
    • 抽象度(Abstraction):阴影形状的抽象程度,用参数alpha控制

image

几种操作和相应的效果

  • 整个风格化阴影的生成是基于图像空间的,从一个已经生成的精确阴影图开始。可以分成五个阶段:

    • 精确阴影的生成,由于是基于图像空间的,因此对精确阴影图的生成方法没有特别要求,可以是shadow volume,shadow map,ray tracing或者其他阴影生成技术,但必须要注意的是这里的阴影值一定是二值化的。
    • 有向距离场的生成,基于图像空间的精确阴影,计算每个像素距离最近阴影边界的有向距离,这是文中算法的核心,也是后面风格化的基础,在文中给出了一种有向距离场的计算方法,当然也可以采用其他方案。
    • 有向距离场的高斯模糊,这一步是抽象阴影生成的关键。
    • 过滤,通过一个转移函数,将模糊后的有向距离场重新映射为阴影图。
    • 使用过滤后的阴影进行光照计算。

image

  • PS:如果是unity的话可能要做屏幕后处理,如果是延迟渲染的的话G-buffer不确定有没有阴影信息。而且移动平台也不推荐。

结束语

  • 除了好好学习还能有啥好说的呢╮(╯▽╰)╭
# UWA
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×