妄想的咸鱼吧 关注:151贴子:2,416
  • 10回复贴,共1

卡通渲染及其相关技术

只看楼主收藏回复

转自https://zhuanlan.zhihu.com/p/26409746


IP属地:安徽1楼2017-07-01 10:20回复
    前言
    卡通渲染是图形学中一个有趣的话题,属于非真实感计算机图形学(NPR)的范畴,在NPR领域中也最多地被应用到实际游戏中,近年来流行的《守望先锋》,《英雄联盟》,《DOTA2》,《崩坏3》等游戏中都或多或少地出现过卡通渲染的身影,恰好最近对这个领域的内容作了一些了解和探索,所以就对其中涉及的一些经典技术做一个概述。


    IP属地:安徽2楼2017-07-01 10:22
    回复
      卡通渲染的分类
      在具体讨论技术手段之前,先就卡通渲染做一个分类。卡通渲染最关键的特征包括不同于真实感渲染的艺术化光影效果和描边。以这两个关键的特征为卡通渲染分类的话,可以将近年来游戏中常用的卡通渲染分为美式卡通风格和日式卡通风格。美式卡通风格在色彩上比较连续,有渐变色,着色风格很大程度上依赖于艺术家定义的色调(tone),而在阴影和高光方面常常采取夸张和变形的做法,比较典型的是《军团要塞2》;日式卡通风格往往角色造型更写实,但在着色方面,则趋向于大片大片纯色色块,并有的明暗交界,例如《崩坏3》。虽然这样的分类并没有清晰界限,但易于描述,接下来我们就按照美式卡通和日式卡通的分类,从光影和描边两个维度上分别列举各类技术实现。

      军团要塞2的卡通渲染,人物造型夸张,但着色连续,接近真实感光照

      崩坏3游戏截图,着色以单色色块为主,有明显的明暗交界


      IP属地:安徽5楼2017-07-01 10:24
      回复
        描边
        描边是一个比较常用的技术,在《Real Time Rendering》中有相当篇幅的综述,大致来说包含了三类:
        (1)基于视角的勾边,这部分的计算依赖于我们的一个直觉观察:当我们的视线和某个表面相切时,这个表面上的像素点往往就是模型的边缘,基于这个观察,我们可以用 来估计一个像素的“边缘程度”,当然,这个值也可以用来作为纹理坐标去采样一张预定义的“轮廓纹理”

        基于视角的描边,最大的缺点是线宽粗细差别较大,不易控制
        (2)基于几何生成方法的描边,这类方法的特点是描边本身是一个单独的几何体,通过特殊的方法绘制出来,比较常见的做法是shell method,原理和实现都比较简单:首先在绘制结束正常的模型后,将需要描边的物体改用正面剔除再绘制一遍,在VS中将顶点沿着法线方向膨胀一定距离,然后在FS中将模型用纯色输出。另外一种叫做z-bias的方法,也是绘制背面,但不膨胀,而是把背面顶点的Z值稍微向前偏移一点点,使得背面的些许部分显示出来形成描边效果。

        基于shell method的绘制方法,实现简单,线宽较为均匀
        (3)基于图像处理的描边,这类方法的实现可以说更接近于“边缘”这一概念的本质定义,什么是“边缘”呢?边缘就是在深度或者法线上不连续的位置。因此为了获取边缘,我们只需要在图片上找到深度或者法线不连续的位置即可,因此,我们需要将深度信息和法线信息以贴图的形式传入,运用边缘检测算法去寻找这些像素。这类方法的优点是描边的线宽一致,缺点是需要额外的法线和深度信息,当然,由于近年来流行的延迟渲染框架,法线和深度本来就是G-Buffer的一部分,因此往往不需要额外绘制法线和深度的信息。

        基于边缘检测的描边方法,分别用深度信息和法线信息进行单独的边缘检测,而后合并起来成为最终的描边


        IP属地:安徽7楼2017-07-01 10:27
        回复
          美式卡通中往往倾向于使用基于图像处理的描边方法来生成均匀一致的描边效果。在《英雄联盟》[1]中小兵和英雄的勾边效果就是用Sobel算子对深度信息进行边缘检测来获得的。由于游戏中只需要针对小兵和英雄勾边而不需要对场景地图进行勾边,因此在LOL中,勾边的计算并非全屏后处理,而是逐物体进行的,这样的好处是可以随意控制哪些物体描边,每个物体可以单独指定描边颜色,缺点是当物体较多时(尤其是skinned mesh较多时)计算量会增大。一个折衷的方案是,在进行正常绘制的阶段用stencil buffer标记出需要描边的物体,然后用一个全屏的后处理,对stencil buffer标记的像素进行边缘检测,当然这样的话,就很难给每个物体单独指定描边颜色了。
          实际上,在LOL中有两种类型的描边,一种是小兵和英雄的固定描边,另一种是防御塔发出攻击警报或者某个单位被点选时才产生的红色描边,这两种描边在处理上略有差别,前者直接使用边缘检测的结果作为最终描边,而后者则是对边缘检测结果再进行一次模糊,借此来扩大和柔化描边效果。


          LOL中两种类型的描边,可以看出第二类描边的线宽更宽,并且有明显的过度效果


          IP属地:安徽8楼2017-07-01 10:28
          回复
            日式卡通的做法
            日式卡通中往往倾向于使用基于几何体生成的方法去描边,这类描边方法相较于另两类方法的好处在于线宽更容易为美术所控制,而在日式卡通中,往往需要粗细有变化的描边去体现角色不同部位的特征,例如在《GUILTY GEAR Xrd》[2][3]中,角色的描边就是通过几何体生成的方法,结合了shell method和z-bias method,并引入了逐物体的顶点色来控制描边细节,同时也是为了保证描边粗细不会随着摄像机视距发生变化,具体来说,顶点色存储的信息包括:
            R通道:控制toon shading的阈值,和描边无关,和着色有关,这个我们后面描述
            G通道:控制顶点根据视距膨胀的强度(这个部分具体操作我也没有完全弄清楚,希望了解的朋友来补充)
            B通道:控制描边的z-bias,越大则描边越不可见
            A通道:控制描边的粗细
            上述做法中比较直观的理解是,通过引入逐顶点的线宽系数,使得整个描边的细节更易为美术控制,但是从我的理解来看,线宽控制只需要一个值即可,视距无关的粗细可以通过给偏移值offset.xy乘以当前顶点的z值来实现,似乎并不需要三个值来控制。

            没有vertex color,轮廓线宽没有粗细变化

            有vertex color, 轮廓线可以按照美术的需要去设定逐顶点粗细变化


            IP属地:安徽9楼2017-07-01 10:29
            回复
              着色
              Cel Shading和Tone Based Shading
              先来描述两种经典的NPR着色方法,分别是Cel Shading[4]和Tone Based Shading[5]。
              Cel Shading的基本思想是把色彩从多色阶降到低色阶,减少色阶的丰富程度,从而实现类似手工着色的效果,具体来说,可以用如下计算方法:


              其中,Kd表示模型自身的贴图颜色,celCoord表示法线和光照方向的点积,用作一维色彩表的查找坐标,而paletteTex则是由美术绘制的一维色阶表,一般来说是由几个纯色色块组成的,如下图:

              上述做法可以用于模拟卡通渲染的漫反射分量,却并没有考虑到视角相关的光照分量的模拟,因此很难实现类似菲涅尔效果的卡通渲染。实际上,也可以用类似的查找表的思路来视角相关光照分量的色阶离散化[6],只需要将一维查找表扩展到二维即可:

              相应地,查找坐标也扩展到了二维。

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

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

              基于tone based shading绘制的球体


              IP属地:安徽10楼2017-07-01 10:31
              回复
                日式卡通的着色
                前面已经描述过,日式卡通在着色方面比较典型的特点是以大量纯色为主,进一步说,往往只有“明暗”或者“冷暖”两个色阶,因此光照计算往往最后也要映射到离散的色彩表上。 仍然以《GUILTY GEAR Xrd》为例,它也一定程度上包含了Cel Shading和Tone Based Shading的部分思想,将色阶离散成为“明暗”两个色调,并由美术指定冷暖色调的颜色:

                上述公式表示了这个卡通渲染的漫反射部分,其中threshold表示明暗交界的阈值,在游戏中通过顶点色的R通道来实现逐顶点的控制。Kcool和Kwarm由美术逐物体地指定,Ksss是对模型次表面散射效果的模拟,对皮肤而言一般呈粉红色,通过美术绘制的SSS贴图来实现逐像素控制,并且只有暗部的像素才会受SSS贴图的影响。Kd是模型自身的颜色贴图。darkness表示了某个像素的明暗程度,用于确定色调的冷暖。除了正常的dot(normal, lightDir)项,游戏中还加入了由美术绘制的AO贴图,来实现一些边角缝隙的暗部效果。我在实现时又引入了动态的阴影部分,最终darkness的计算公式为:

                其中shadow是由shadowMap的算法计算得来的。
                高光的计算更简单一些:

                其中,spec表示高光的强度,threshold可以由美术逐物体或逐顶点指定,specMask和specPower由美术绘制的贴图来逐像素控制,类似于phong着色中的specular和glossiness的作用。specColor可以由美术逐物体地指定,也可以把AO,shadow和明暗色调作为影响因素添加进去。最终的着色结果将漫反射和高光叠加即可。
                在实际游戏中使用时,上述方法往往还需要配合美术针对具体模型进行法线修正。 根据模型顶点位置和拓扑关系计算出的法线往往细节过度,表现在上述卡通渲染的结果上就是往往会出现许多不需要的暗部细节,修正的方法是使用模型法线转印,给精细的模型一个近似的低精度proxy(比如用一个球形代表模型的头部,用一个圆柱形代表模型的胳膊或者腿),然后用proxy上附近顶点的法线作为模型的法线来使用。此外,还需要考虑到明暗交界处反走样的问题,这里不做展开。
                根据我的观察和研究,《崩坏3》应该是沿用了《GUILTY GEAR Xrd》中的卡通着色方法和美术工艺,因此在效果上和后者非常相似。

                基于不同的冷暖色调设定值得到的卡通渲染结果


                IP属地:安徽11楼2017-07-01 10:32
                回复
                  美式卡通的着色
                  Valve在其游戏《军团要塞2》[7]中描述了他们的卡通渲染方案,这个卡通渲染算法也在后来影响了《DOTA2》的卡通渲染的实现。他们将卡通渲染着色分为了view dependent term和view independent term。两部分的计算分别如下:



                  这部分的实现在其他知乎专栏文章[8]中有详细的描述和实现,这里不再做详细的解释。直观地来说,在视角无关的照明部分,《军团要塞2》中除了考虑了一般的漫反射部分外,还加入了基于模型法线方向的环境光分量,此外,通常的漫反射分量改为了wrapped diffuse;而在视角相关的照明部分,《军团要塞2》除了考虑了一般的镜面反射外,还基于菲涅尔现象实现了类似边缘光的效果。实际上,类似ambient cube和warpped diffuse的做法也被Valve应用在《Half Life》等其他游戏中[9],在早期3D游戏中用以模拟全局照明。虽然这些方法都是一些纯粹的trick,但是能够以很小的开销实现不错的效果。

                  《军团要塞2》的最终着色结果,可以看出明暗交界处有明显的泛红(warpped diffuse的效果), 模型边缘可以看到边缘光

                  经典的half-lambert方法也算是warpped diffuse的一种变体


                  IP属地:安徽12楼2017-07-01 10:33
                  回复
                    风格化高光和阴影
                    在[7]的Future Work里,还提到了可变形状的高光[10]和风格化阴影[11],这两个风格化渲染算法的思路都比较有趣,这里简单就其实现原理进行一个概述。
                    可变形状的高光
                    我们在日式卡通渲染的着色部分描述了一个相对较为简单的高光计算方式,从计算方法可以看出,该方法和经典的Blin-Phong模型有很多相似之处,尤其是对高光强度的计算上,都采用了这个计算项:

                    这个halfVec也就是我们常说的半角向量,计算方法是:

                    其中,L和V分别是光源方向和视线方向。
                    从我们上面描述的卡通渲染高光算法可以看出来,改变卡通渲染高光形状的关键就在于改变这个半角向量。因此文章中就针对半角向量定义了一系列的修改操作,这些修改操作可以叠加使用,也可以单独使用,每个操作对高光形状的影响均不同,具体有以下几个操作:
                    (1)平移,改变高光的位置:

                    这里,du和dv表示的是切线空间中的x轴和y轴,也就是切线和副法线,alpha和beta是自定义平移参数,最终偏移后的向量需要进行归一化处理。
                    (2)有方向的缩放,沿着切线空间的某个轴缩放高光形状:

                    sigma是自定义参数,范围是(0, 1],上式将使高光沿着切线空间的X轴缩放。
                    (3)分割,将一块连续的高光切分成两块:

                    其中,sgn是符号函数,负数返回-1,否则返回1,gamma1和gamma2分别是自定义参数,若其中一个为0,则只沿着另一个方向将高光切为两部分,若两个参数均不为0,则高光被切成四块。
                    (4)方块化,将趋于圆形的高光变成方形:



                    其中,n是自定义整数,n越大高光形状越方,sigma则定义了方形高光区域的大小,范围是[0, 1]。
                    上述四个操作的具体实现可参见这篇文章[12]。

                    四个基本的操作符


                    IP属地:安徽13楼2017-07-01 10:37
                    回复
                      这个图转着好难受。。。。。。。。。。想看的直接去原帖吧


                      IP属地:安徽14楼2017-07-01 10:39
                      回复