2022-12-233676次浏览
1评论
44收藏
10点赞
分享
本文介绍了日式卡通渲染的几种基础技术,让读者可以更容易的入门,并且在此基础上扩展得到自己想要的渲染效果。
卡通渲染是属于非真实感计算机图形学的范畴的,分为美式卡通和日式卡通两种。
-美式卡通:色彩连续、存在渐变色(代表作:《军团要塞2》);
-日式卡通:明显的明暗交界,大范围的纯色色块(代表作:《崩坏三》);
日式卡通渲染的着色,总而言之就是对Blinn-Phong模型的一个简化。这其中用到了一个关键的函数为smoothstep。
我们先从传统的漫反射模型说起:传统漫反射使用模型法线与光线方向的点乘值来获得光影的过渡,传统漫反射系数的过渡比较的柔和(见下图)。
常见的光照模型有Lambert模型和Half Lambert模型。
日式卡通渲染中的漫反射主要是对传统的漫反射系数做一个离散化的操作。
传统的漫反射是用dot(N,L)来模拟的,而卡通渲染的漫反射通过一个阈值将光照分为亮和暗两个部分
对于日式卡通渲染,我们主要进行以下步骤:
首先将传统漫反射进行离散化
再使用smoothstep对边缘柔和度进行控制
这其中用到的关键函数是smoothstep
,他是我们实现日式卡通渲染的重要函数。
smoothstep是一个平滑过渡函数,他的函数图像如下。
函数原型
smoothstep(e0,e1,t),其中e0.e1为常量,t为变量。关于这个函数的更具体的特性,可以通过这个链接来进行体验。
函数作用
他在我们的实现中可以对平滑过渡的漫反射系数进行离散化,同时也能有很好的反走样的作用。下面的图片中可以看到,通过使用smoothstep,边界的走样问题得到了很好的改善。
主要通过使用_RampThreshold和_RampSmooth来分别来控制边界的阈值和柔和度,他的光影变化如下图。
图中的i对应我们的_RampThreshold,而s对应我们的_RampSmooth。当s的值越大,则过渡的速度越慢,这样边缘则更加柔和;反之,当s的值越小,过渡的速度则越快,这样边缘则更锐利。
float
ndl = dot(worldNormal, lightDirection) ∗ 0.5 + 0.5;
ndl = smoothstep(_RampThreshold - 0.5 ∗ _RampSmooth, _RampThreshold + 0.5 ∗ _RampSmooth, ndl);
可以看到在不同的_RampSmooth值下的表现:
_RampSmooth值应该使用一个比较相对较小的值,否则画面风格会变成美式卡通。
除了以上的使用系数的实现方法,我们也可以使用RampTexture来达到这一效果。
实现原理
通过Half Lambert值对一个纹理贴图进行采样来获得梯度变化的光影,这种实现方式的好处在于可以更加精细的控制各个位置的光影变化。
实现代码如下:
fixed halfLambert = 0.5* dot(lightDir, worldNormal) + 0.5;
fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb;
实现效果
卡通渲染中的风格化高光是Blinn-Phong模型的简化,依然使用半程向量
和物体的表面法线
来进行计算。
另外,为了达到卡通渲染简化的目的,我们依然使用了smoothstep函数来对高光系数做数值上的离散化处理。
传统高光是使用半程向量和法线来进行模拟的。
我们依然使用smoothstep函数对高光系数做一个离散化的处理。
通过同样的方式我们获得了边界明显的风格化高光:
边缘光,顾名思义,指的是模型边缘内部的发光效果。
边缘光与物理中一种叫菲涅尔反射的现象相关。在现实中我们可以通过观察玻璃球来看到这种效果。
当我们观察玻璃球中心时,光反射的效果会比较弱
而当我们观察玻璃球边缘时则可以看到比较强烈的反射效果
菲涅尔现象,简而言之,就是视线垂直于表面时,反射较弱,而当视线并非垂直表面时,夹角越小,反射越明显。
边缘光的实现主要是基于视线与物体表面法线来进行的。
基础实现
我们通过视线与物体表面法线的点乘来获得边缘光的强度:
当视线与物体表面垂直时,边缘光强度为0
当视线与物体表面平行时,边缘光强度为1
具体代码如下:
float
rimVal = 1- saturate(dot(i.viewDir, i.worldNormal));
同时我们也需要定义一个边缘光的颜色RimColor
,然后在片元着色器中将rimVal
和RimColor
相乘后得到边缘光的颜色分量。
通过上述计算,可以得到以下效果:
风格化边缘光实现
上面的效果比较写实,不符合卡通渲染通过简化进行增强这一原则。因此我们需要再加工一下,也就是离散化。
在这里我们还是使用smoothstep
函数来对rimVal
做一个加工,来达到简化的目的。
最终可以得到一个这样的风格化边缘光的效果:
在光照射不到的地方,我们希望不显示边缘光,因此我们可以用NDL
来与rimVal
相乘,这样在阴影处就不会有边缘光。
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
float NDL = saturate(dot(i.worldNormal, lightDir));
NDL = NDL > 0 ? 1 : 0;
half4 lightIntensity = NDL * _LightColor0;
float rimVal = 1- saturate(dot(i.viewDir, i.worldNormal));
rimVal = smoothstep(_RimThreshold - _RimSmooth, _RimThreshold + _RimSmooth, rimVal);
half4 rimIntensity = rimVal * _RimColor * NDL;
将以上几种实现综合在一起可以得到以下的日式卡通效果:
↓↓↓推荐阅读↓↓↓
评论 (1)