UE 卡通着色 卡通描边
前言
- 本篇基于Unreal Engine 4 Cel Shading Tutorial | Kodeco教程进行一个总结和实现
卡通着色
描绘色带
- 与写实风格不同,卡通渲染的色带(阴影,中间调,高光)呈现多段式分布
描绘色带的方法
-
最常用的方法是比较表面normal朝向和光照方向
-
根据点成结果即可实现多段色带,若大于-0.8取深色,否则取浅色
-
缺点
该方法有一定的局限性,因为它只是简单的考虑某光线和表面相交的情况,所以无法反映其他的光照影响以及无法接受其他物体的投影
后处理材质
-
定义
后处理材质提供一个方法——可以在渲染完成后继续修改场景的整体外观,包括景深、运动模糊和辉光等等
-
启用
把"Material Domain"改为"Post Process"
计算光照缓冲
-
思路
我们知道最终渲染生成的画面是通过\(DiffuseColor \times Light\)得到的,这一计算结果便是Post-process Input。现在已知Post-process Input 和 DiffuseColor,Light的计算也就不难了。剩下的事就是获得两个缓冲区的数据——Post-process Input缓冲区(无任何后处理的最终渲染画面)和DiffuseColor缓冲区
-
获取Post-process Input缓冲区
这里需要用到"SceneTexture"节点,并将"Scene Texture id"换位"PostProcessInput"
-
获取DiffuseColor缓冲区
-
效果
照亮的区域接近白色,没照亮的接近黑色
创建阈值
- 为了渲染色带,在这里我们设定当光照缓冲值>0.5使用正常的Diffuse Color,光照缓冲值<0.5使用\(\frac{1}{2}\)的Diffuse Color
应用后处理材质
- 欲将后处理材质应用到场景中需要创建"Post Process Volume"
- 步骤
- 拖取"Post Process Volume"
- 添加"Post Process Materials"
- 默认情况下,Post Process Volume只作用它范围内的对象,为了作用整个场景,需要勾选"Infinite Extent"
- 拖取"Post Process Volume"
- 效果
Tonemapping
-
目标
上图看起来有些不对劲,因为我们是在Tonemapping后应用的Cel shader,而Post-process Input缓冲区是最终的渲染图形,也就是说这是Tonemapping后的,所以我们需要将该数据转至Before Tonemapping
-
实现
在shader根节点处,将"Blendable Location"改为"Before Tonemapping"
-
效果
很不错符合预期
分离卡通渲染
-
目标
目前我们的后处理材质运用在整个场景,但我们大部分时候只想卡通渲染只作用于角色,而背景等不受影响
-
解决方法
解决方法便是自定义深度(Custom Depth)
-
什么是自定义深度
场景深度存储每个像素到相机平面的距离,而自定义深度更加特殊,它针对于指定的几何体。若场景深度小于自定义深度,保持不变;若大于,采用卡通渲染
-
启用自定义深度
点击指定几何体,启用"Render Custom Depth Pass"
-
-
实现
启用后进行深度比较即可
-
效果
丰富色带
-
目标
从上图可以看出,目前实现效果仅仅表现了明暗两部分,但我们想要更加丰富得色带,如增加色带数量、色带和色带间的过渡等
-
解决方法
我们将使用查找表(Lookup Table)来丰富色带
-
什么是查找表
和乘法表类似,通过查找乘数和被乘数来迅速定位结果。而对于卡通渲染来说,查找表是一张包含明暗梯度关系的纹理图,这就如同normal map一般它的效果更加好
-
如何使用查找表
目前,我们计算阴影是通过将DiffuseColor * 0.5,而现在我们将使用查找表的值,而不是0.5
-
-
实现
-
更改查找表设置
-
禁用sRGB
在UE中,会将使用sRGB的纹理转换为线性颜色(方便计算),而sRGB适用于描述物体外观的纹理,对于normal这类计算数学计算的纹理不应转换到线性颜色空间
-
禁用平铺(tiling)
平铺将在从边缘采样时引发问题,如从左边缘取样一个像素,它将尝试混合到右边缘
想禁用平铺,需要将 X 轴平铺方法更改为"Clamp",y轴同理
-
-
shader实现
-
-
效果
五种不同值的查找表的效果
卡通描边
- 卡通描边有两种方法可以实现,一种是二次渲染,一种是通过后处理边缘检测
二次渲染
-
思想
复制指定mesh并赋予它指定颜色(一般为黑色),再稍稍放大复制的mesh,最后用复制的mesh覆盖原mesh,多出的部分即为物体轮廓
-
存在的问题
-
只是这样简单的实现会发现复制对象的颜色会覆盖原本的mesh,如何解决呢?
可行的方案是对复制的mesh先翻转法线方向,再启用背面剔除(因为法线定义了mesh的正面和背面),这一方案可行的关键在于这样实施后我们会得到mesh的内部而剔除mesh的外部
-
-
优点
- 生成的轮廓总能呈现清晰干净的线条
- 能够通过简单地手动调整顶点来修改轮廓样式
- 边缘线会随着模型距离的变化而变化
-
缺点
- Mesh数量翻倍,性能不友好
- 不会过渡勾勒内部轮廓
- 轮廓有因模型遮挡而被裁剪的风险
- 能很好地处理凸表面,但在凹表面和硬表面边缘会产生离断空洞
-
实现
-
shader根节点的"Blend Mode"改为"Masked"
-
"Shading Model"改为"Unlit"
-
启用"Two Sided"
"Two Sided"会禁用背面剔除
-
翻转mesh
-
复制mesh
- 在"Content Drawer"中创建一个"BLue Printer class"组件,添加一个mesh组件(类型根据你的模型来定)
- 在"Mesh"中使用模型的原材质函数,在"Outline"中使用Outline material
- 在"Content Drawer"中创建一个"BLue Printer class"组件,添加一个mesh组件(类型根据你的模型来定)
-
效果
-
优化
有时候我们并不希望边缘线会随着模型距离的变化而变化,那又如何实现呢?
- 利用"ViewSize"求得当前分辨率大小,再使用相机到物体的距离除以"ViewSize",最后乘上控制物体轮廓大小的因子
- 利用"ViewSize"求得当前分辨率大小,再使用相机到物体的距离除以"ViewSize",最后乘上控制物体轮廓大小的因子
-
边缘检测
-
边缘检测是一种用于检测图像中不连续区域的技术,这种不连续性体现在法线、颜色、深度、亮度等
-
优点
- 快速应用到整个场景
- 节省开销
- 在不同的距离下,描边粗细能保持相同
- 后处理效果不会因模型的几何关系而被裁减
-
缺点
- 需要多种边缘检测算子来涵盖所有边缘情况
- 易产生噪点。因为边缘总生成在变化非常大的区域上
-
执行边缘检测的方法:拉普拉斯边缘检测算法(Laplacian edge detection)
-
实质:计算一定范围内的斜度变化量
-
计算过程
-
卷积核
-
卷积运算
这里计算的斜率为1,变化量很大,这意味着该区域可能是是一个边缘
-
整体运算
-
-
-
实现
-
避免纹理分辨率对纹理的采样影响
假设目前采样的目标纹理分辨率为100x100,在uv空间一个像素点大小就是0.01,如果要采样下一个像素点就需要+0.01,这正是问题所在。当纹理分辨率变为200x200,一个像素点大小为0.05,若依旧是+0.01,则会采样到第二个像素点 -
使用的卷积核
-
边缘检测
-
效果
-
优化
目前还有些问题:有些边缘只有细小的深度差异;背景也参加了边缘检测
- 深度变化值大于4才显示边缘
- 深度大于9000的像素不参与边缘检测
- 效果
- 最终实现图
- 深度变化值大于4才显示边缘
-
控制描边粗细
为了控制描边粗细需要更大的卷积核,但这意味着更消耗性能(采样点增多),我们需要一种方法既能增大卷积核,性能也不差,该方法就是扩张卷积(3D视觉开发者社区 (orbbec.com.cn)这篇讲的不错)-
为什么使用扩张卷积?
-
因为扩张卷积可以增大感受野,在检测中能对较大物体表现出较好的效果
-
既能不增加采样个数,也能扩大卷积核
扩张率定义卷积核因子的间距
-
-
实现
-
效果
扩张率为1、2、3对应的效果
-
将线框和背景lerp
-
-
reference
UE4卡通渲染基础教程 Part1:Cel Shading - 知乎 (zhihu.com)
虚幻引擎中的后期处理效果 | 虚幻引擎5.0文档 (unrealengine.com)
Unreal Engine 4 Cel Shading Tutorial | Kodeco
UE4卡通渲染基础教程 Part2:Toon Outline - 知乎 (zhihu.com)
Constant Material Expressions in Unreal Engine | Unreal Engine 5.1 Documentation