【非真实渲染】【卡通渲染技术点介绍】

阅读指南

文本介绍卡通渲染的基本技术,实现会放在另外的文档

关键词

Cel Shading,ToonShading,色块、色调,各向异性,描边,高光

特征

看起来像手绘的图片

少渐变(指光影的变换),有明显的分界

颜色少

色调少

看起来粗略(凸出主要特征)

卡通渲染的技术是为了实现这些特征

光照模型

卡通渲染光照组成

一般是 = 环境光 + 漫反射 + 高光,根据需要也可以加入对环境的反射

特效

边缘线 + 边缘光

头发天使环

头发上的高光反射

技术点

色块化

把渐变分出好几个级别,渐变变成突变,在边界做一点过渡,一般是根据【法线和视线】or 【法线和光线】计算一个值,根据这个值进行分段,某个范围映射到一个固定的值。

如果要美术定制,可以使用光照查询纹理,是一个分块的颜色纹理,根据光照计算的结果(也就是上面的方法里计算出来的值的相关值),一般是一个[0,1]的值作为uv来查询映射纹理

称为RampMap

色调

使用冷色调,暖色调,根据需要使用的色调配置不同的查询纹理,比如冷白皮肤,热带环境,极地环境

漫反射

色块化方式:

if dot(normalWS, lightDirectionWS) > 0

明亮

else

使用smoothstep做明暗变化的过渡

float lightIntensity = smoothstep(0, 0.01, NdotL);

高光

现实世界效果

卡通渲染效果

真实世界光照表现的不同在于,卡通渲染的高光区不会有太多过渡,是大块大块的突变

float specularIntensity = pow(NdotH * lightIntensity, _Glossiness * _Glossiness);

float specularIntensitySmooth = smoothstep(0.005, 0.01, specularIntensity);

外轮廓线

根据法线和视线的夹角

dot(normal, viewDirectionWS)越小,夹角越接近90度,说明是模型的边,但是获得的轮廓不均匀

优化,将法线变换到投影空间要乘以transform的转置逆再乘projection矩阵,这样法线不会受到非等比缩放的影响,即使用NDC空间的距离

这里面的精髓:多乘了一个w,这样进行透视除法的时候不影响我们设置的数值

v2f o;

o.pos = UnityObjectToClipPos(v.vertex);

float3 norm = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);

float2 extendDir = normalize(TransformViewToProjection(norm.xy));

//o.pos.xy += extendDir * (_OutlineWidth * 0.1);//拉动镜头粗细会变化,下面是优化的

o.pos.xy += extendDir * (o.pos.w * _OutlineWidth * 0.1);

参考

【02】卡通渲染基本光照模型的实现 - 知乎

2个Pass,根据法线外扩模型,剔除正面

背面扩张法(Procedural Geometry Silhouetting),缺点是同一个顶点在不同面上有不同法线的地方会有缺口(模型交接的位置出现缺口)

缺点:背面可能和原来的模型发生深度冲突,导致遮挡模型

一种解决方法是给backfaces设置Z-offset,使轮廓线埋没到临近的面里。另一种解决方法是修改backfaces扩张的法线,使轮廓线扁平化

后处理,根据法线和深度变化程度

一般使用Sobel边缘检测算子

缺点:

一些z变化很小的轮廓,比如桌子上的纸张,就无法检测出来

不能区分Silhouette edge和Crease Edge

后处理,边缘检测算子进行检测

内轮廓线

如果直接在贴图上绘制线条,缩放会模糊

使用本村线,角色表面描边的方法,并不是外描边,它是将模型的 UV 打直,只绘制于垂直于 U 轴或者 V 轴的直线,避免斜线线条的采样问题

缺点:

制作周期会比较长

解释下UV打直

把模型上没有整齐排布的uv给排整齐

不直的UV

直的UV

边缘光

根据相机位置和法线,夹角越接近90度,边缘光越强

如果用视线方向,如果模型在右侧45度,并且正面朝着相机,边缘光会出问题,所以要用相机位置

viewDir = normalize(cameraPos - position)

float4 rimDot = 1 - dot(viewDir, normal);

使用smoothstep进行过渡

float rimIntensity = smoothstep(_RimAmount - 0.01, _RimAmount + 0.01, rimDot);

去掉暗处的边缘光

float rimIntensity = rimDot * NdotL;

rimIntensity = smoothstep(_RimAmount - 0.01, _RimAmount + 0.01, rimIntensity);

根据法线和光源夹角调整边缘光,夹角越大,边缘光越弱

float rimIntensity = rimDot * pow(NdotL, _RimThreshold);

rimIntensity = smoothstep(_RimAmount - 0.01, _RimAmount + 0.01, rimIntensity);

阴影

卡通渲染的阴影也是一块一块呈现,没有渐变,只在交界处有过渡

float shadow = SHADOW_ATTENUATION(i);

float lightIntensity = smoothstep(0, 0.01, NdotL * shadow);

阴影倾向

有些地方容易产生阴影,有些地方则很难产生阴影。用一张贴图或者顶点色控制阴影的倾向,对上面的映射函数进行偏移

PBR

在PBR光照模型的计算中加入卡通渲染,使用NDotL、NDotV的值进行操作

头发高光-天使环

一般的高光都是使用法线方向进行制作的,但是此处使用了副切线(T)。原因是我们模拟的对象(发丝)是一个圆柱形,它的形状导致了它的法线的不唯一性,所以法线不能用来计算我们的高光。法线不确定,导致我们的切线也不确定,但是他们的平面是固定的,所以我们可以使用副切线(同时垂直于由法线与切线的向量)

算法说明http://web.engr.oregonstate.edu/~mjb/cs519/Projects/Papers/HairRendering.pdf

经验

法线

卡通渲染的模型一般需要人工调整法线

ilm map

用于调整阴影和高光区域的形状

sss map

用于调整阴影的颜色,暗部颜色 = mainTex * sssMap

参考资料

unity官方卡通渲染方案

Unity Toon Shader (Unity-Chan Toon Shader 3) | Unity Toon Shader | 0.6.1-preview

基础技术

Unity Toon Shader Tutorial - Roystan

outline

GitHub - IronWarrior/UnityOutlineShader: Source code for Outline Shader tutorial for Unity. Detects edges in a scene using the depth and normals buffers.

综合

【02】从零开始的卡通渲染-着色篇1 - 知乎

天使环

多了噪声做扰动

【Cel-Shading】各向异性发丝高光 | Invictus maneo

头发各向异性渲染写的详细且深入

图形学基础|各项异性与头发渲染_桑来93的博客-CSDN博客_头发各向异性

各向异性(Anisotropic)指的是在不同方向上表现出的光照效果会产生差异

猜你喜欢

转载自blog.csdn.net/ak47007tiger/article/details/126321708
今日推荐