本篇总结卡通渲染的各项特性。
不含主要代码,只写制作理念,强调效果、理由(实用向)、利弊。

参考资料:
PBR_Guid_Vol1_中文版.pdf
【Unite Shanghai 2019】闪耀暖暖技术分享:2D到3D的进化与创造之路
【Unite Tokyo 2019】〈七つの大罪〉をゲームで!高品質グラフィックを具現化するための技法と開発最適化のご紹介
霜狼:2019美术知识学习总结—卡渲部分
Unity插件:Toony Colors Pro 2

漫反射

漫反射能表现出材质的物理特性,比如黑色的物体吸收了所有可见光,于是表现出黑色。
漫反射贴图,也就是基本色、BaseMap、MainColor,表示材质的性质。
在卡通渲染中,漫反射贴图常呈现色块状,缺少褶皱、脏迹(和PBR的重要区别)。

漫反射贴图占了最终效果的绝大部分,已经可以起到预览最终模型品质的功能。
相比漫反射,重要性稍微次一级的效果是阴影和高光,分别用于区别表面在暗处和高亮处。

烘焙与展示

如果将阴影和高光烘焙进漫反射贴图,那么我们就得到了一个可以用于Web展示的贴图。
我们通常在Sketchfab、Niconico工坊等3D模型展示网站看到的模型基本就是这个样子,阴影和高光被固定了。至于更复杂的效果,纯靠网页渲染引擎的支持。
我们可以得出一个普遍的规律,贴图如表现,漫反射贴图基本可以表示物体在被强度为1的平行光垂直照射时的表现。也就是说,实际效果根据光照衰减,可能变暗一点。

光照模型

有很多的光照模型,比如兰伯特:
diffuse = atten * light * max(0, NdotL); //漫反射成分根据光照角度变化
再比如半兰伯特:
diffuse = atten * light * (0.5 * NdotL + 0.5); //明亮变化相比兰伯特变缓了
又比如PBR公式:
color = UniversalFragmentPBR(...); //输入大量表面参数返回光照结果
小结:半兰伯特光照只有光的反方向的深处才是全黑的,比兰伯特光照的一半区域全黑要正常一点;PBR公式则综合了一整组参数用于计算效果,实现了圆滑的明暗渐变,还模拟出了物体表面的金属、反光特征。

但是卡通效果中,不需要有明显的明暗渐变,甚至阴影的形状都不一定是科学的。
比如,我们希望鼻子的左侧或者右侧总是有一点阴影(线状),表示这里有个鼻子;
比如,我们希望脖子下面有小圈阴影,但是这个圈的大小与头的大小没有关系。
卡通效果的依据源自于设计师在审美方面的任性,感性上觉得加点什么效果更好,那么可以。

常见的光照变量

L:光源方向:迎光面和背光面
N:表面法线方向:表面的朝向
V:观察方向:迎视角面和侧面
H:高光方向 H=normalize(L+V)
我们可以有意识的将效果向某个参照方向移动。

金属与反光的特征

用户判断一个物体是否为金属,从视觉先入为主,再结合触觉、听觉因素;
金属应该很硬,颜色上类似于铁、银、金等,如果表面平滑的话还可能反光。

在实时渲染中,光看外表很难断定物品是金属的(没有各向异性),也许更像塑料。
人物跑动的时候,鞋子和地板之间产生清脆的响声,我们会觉得地板很硬。
如果怪物跑动的时候,发出金属撞击声+火花,我们会觉得怪物的脚也很硬。
以及我们和怪物过招的过程中,怪物的动作(机械化)、格挡叮当响、死亡爆炸。
通过系统的GamePlay设计,来完成一个金属的概念。
反光在卡通中被普遍滥用,颜色丰富,与自发光(Bloom)一起作为光污染来源。
不仅地板、金属可以反光,丝袜、皮肤、头发都能反光。

漫反射衰减

卡通可能有衰减,也可能没有(简化明暗变化),衰减量不会很多。
最终的漫反射率,比如定义最多只能衰减20%,那么结果稳定在80%-100%之间。
颜色块内的渐变需要平滑,那么使用smoothstep方法:
shift = smoothstep(0.7, 1.0, NdotL); //光照角度的影响有下限
diffuse = lerp(0.8 * light, light, shift); //漫反射率的衰减有下限
为了表现漫反射的衰减,这里需要暴露光照角度阀值和衰减下限,2个参数。

色阶:图像亮度强弱的指数标准

赛璐璐(漫画/动画):明、暗
UTS中的四个区域:高亮区、通常区、一影(Halftone)、二影(Core of Shadow);
素描中的明暗五调:亮面、灰面、明暗交界、暗面、反光、投影;
在游戏中,使场景和角色有相近的色阶数量可减少异样感;
用户能从色阶中捕获深度变化,使角色更加立体、具有真实感。

使用Ramp贴图丰富色阶

可选项一:diffuse ramp,重映射NdotL;
可选项二:2D diffuse ramp,重映射NdotL,充分利用2D空间,纵轴用于调整材质;
可选项三:拟次表面双ramp贴图,偏离观察方向和光源方向加速阴影衰减。
可选项四:拟次表面ramp贴图,定义V+/L+、V-/L-、V+/L-、V-/L+值
注:要采样厚度,才能获得真正的次表面通透感,如西瓜、手掌心。

基于视角的图像修正

思路一:人物离相机距离极近时,增强漫反射衰减,使表面更有层次感;
思路二:为了保持人物的脸型,相机侧对脸部时进行骨骼修正,保持人物特征;

PBR

由于卡通在明暗变化、金属性、光滑度等方面更追求平面艺术,一般用不上PBR;
但是在特殊情况下,我们需要丰富的材质类型,来体现道具的材料差异;
总体来看,PBR和手绘风,应用的游戏范围不一样,对材料的强调程度不同。

NPR/PBR遮罩
一旦确定NPR无法满足项目目标,那么尝试PBR就非常的有必要。根据PBR Guid,我们可以知道要准备固有色、金属镀、AO、粗糙度等数据;魔改PBR公式,通过减少色阶增加卡通感。
配合高光遮罩、NPR/PBR遮罩、透明等,做出色彩丰富的卡通效果。

阴影

阴影区域和漫反射一样,也呈现色块状,或者说,就是阴影版的漫反射区域。
那么需要判定阴影区域+决定阴影色。

阴影的硬与软

现实中的光源有实际的形状,比如电灯泡、电杆;
这些形状使阴影区域分成三块:全阴影、无阴影、半影;
从半影区域的角度观察,只能看到光源的部分形状;
体积光和面积光在半影区有更细节的表现。

半透明阴影:

拆解成2个部分:受影物体(不透明)和投影物体(透明)
1.绘制shadowmap时写入投影物体;
2.正常绘制受影物体(采样shadowmap);
3.仅在颜色RT的A通道写入投影物体的透明度;
4.绘制受影物体(不采样shadowmap),根据透明度进行混合;
5.绘制投影物体
注:混合操作要求队列值在透明队列。

阴影区域

如果物体是球形,在平行光下,阴影划分呈现出基本的对半分(NdotL=0)。
脸部的阴影更接近于手绘,在特定角度下精确控制阴影形状。
如果单纯使用NdotL和遮挡关系直接作为阴影的判断依据,阴影边界会相当斑驳。
比如额头、脸颊、嘴唇,会成为阴影分界线主要持有者。
NdotL是判断阴影边界的重要线索,用于作为一个“阴影查找表”的输入参数。
阴影查找表类似于颜色映射,表示逐像素阴影阀值(低于NdotL上限时表现为阴影)。
这样一来我们获得了一个新的阴影模式,用于表现需要精确控制阴影形状的表面。

阴影边界的划分
方案一:固定阀值
比如NdotL在0.8-1.0之间,漫反射缓慢的衰减,在0.5-0.8之间保持漫反射色的下限。
这样做是为了让阴影和漫反射区域之间有清晰的分界线,过渡区域近乎于没有。
当NdotL在0.0-0.5之间,根据固定阀值判断当前像素是否处在阴影区域。
方案二:精确阀值
完全通过阴影查找表来判断阴影区域,表面逐像素定义阴影阀值对应的光照角度;
整个阴影查找表相当于等高线地图,同高度的区域有同步的阴影变化。

比如,鼻侧有一小块区域需要固定阴影,那么这个区域要求的NdotL的值是1;
对阴影查找表的精度要求变得严格,如果分辨率不足可能导致阴影模糊。
没有特殊细节要求时,可使用固定NdotL作为阀值,节约人力。

阴影色

阴影色和漫反射色有直接关联,相当于作用了一个亮度和温度的衰减。
方案一:统一衰减,使用颜色映射,统一衰减亮度和温度;
方案二:阴影衰减查找表,单通道,方案一的逐像素版本;
方案三:阴影贴图,RGB通道,使用指定的阴影色。

用户眼睛会被亮处吸引过去,暗部主要是需要有清晰的阴影轮廓即可;
卡通渲染并不考究阴影真实程度,统一衰减可以说是节约人力+效果还行。

Cookie

Cookie对光源进行一次过滤操作,使接下来传播的过程中不再保持统一强度。
比如:光从窗户里照进房间,留下了窗户栏杆的阴影;
比如:光照到头发,在肩上留下了蓝色头发的淡蓝色阴影(类似于次表面)。
Cookie使卡通物体的阴影色可以受到半透明遮挡物的影响。

自影和地面影

自影(Self Shadow):在MMD中(Toon纹理),关闭自影之后头发不会对脸部造成阴影。
地面影(Drop Shadow):仅绘制到Shadowmap,可以对其他物体造成阴影。
在倾向于真实度的渲染中,我们肯定会同时开启自影和地面影。
但是在卡通方案中,有一部分阴影我们自定义过了,比如脸部的特殊阴影;
这部分阴影可能在设计之初就考虑到了头发的影响,画在了漫反射贴图里。

关闭自影+接受投影:依然采样Shadowmap,写入Shadowmap的时机延后(不采样自己);
对于身体,如果需要接受自影,应该与头部使用不同mesh,并在头部之后绘制。

卡通硬阴影

因为卡通阴影对轮廓很重视,屏幕空间的Shadowmap的清晰度要精确到像素级别;
而传统的cascade Shadowmap(光源空间的深度图)能提供的精度实在有限。
方案一:独特的角色阴影,为离相机较近的角色单独定制Shadowmap。

方案二:RaytracedHardShadow,使用光线追踪绘制像素级别精度的Shadowmap。
使用二进制遮罩(比如00001011)来记录屏幕空间多个光源的逐像素阴影覆盖情况。

高光

高光和阴影类似,对范围控制有要求,最终能高光的区域只占很小的一部分。
一个区域能处于连片高光状态,表示这里很光滑、柔顺的(可爱)印象,比如头发。
那么,有没有反面教材,表示头发很乱呢?

在动漫人物设计里面是很难找到相应素材,美少女们的头发总是很亮。
比如命运石0的比屋定真帆、丧女的黑木智子,人物设定虽然糟蹋,但高光没什么不同。
通过黑眼圈,来表示人物的作息不规律;乱丢盒子,表示人物不讲卫生。
但是原则上,我们一直在强调卡通角色的可爱之处,所以滥用高光没有问题。
既然是滥用的,我们可以考虑多种方案决定高光范围;
至于高光颜色,一般都是白色,或者相比漫反射色要增加一点点亮度。

PBR向-双频高光

使用NdotH,计算出一个范围较大的高亮区域,作为低频高光,与周围区域融合。
使用遮罩,制作较小范围的高亮区域,作为高频高光;合并后增加高光区域细节。

Overlay式高光


这个效果在UTS中称作AngelRing(天使之环)、MatCap(材质捕获);
在MMD中,称作Sphere(球形高光),有加算/乘算,等模式;
两者的作用机制类似,在观察空间,使用一个xy值,采样2D的圆形范围。

AngelRing:xy值主要来自于顶点的y分量和表面法线的x分量,加算至漫反射色。
顶点的y分量可使视线上下移动时高光位置不随角度变化,需要烘焙;
法线的x分量和保证Overlay效果的水平相对位置。
可选项一:Overlay效果额外受到光源影响。
可选项二:Overlay效果与漫反射色进行透明度混合。
可选项三:在顶点的y分量和法线的y分量之间插值,可略微偏移Overlay位置。


Sphere:加算模式,和AngelRing类似;乘算模式用的少;需要烘焙xy值。
乘算,意味着颜色变暗,根据透明度混合后实现变亮(淡淡高光)和变暗(淡淡阴影);
加算和乘算关系到贴图和模型的制作:
加算:贴图中2D采样圆形范围内,只有高光和边缘角度才会很亮,剩余黑色。
乘算:alpha * self.rgb + (1 - alpha) * other.rgb
两者相比,乘算容易制作淡淡的浮影,加算可以准确控制高光强度的渐变。
在MMD中,设置扩散色和环境色为黑色,便于观察spa贴图的加量。

加算Sphere在MMD中广泛应用于不同的材质类型
头发:水平中位线上移,微微加白:多段水平高光区域;

金属:圆边、竖向多段加白,边界分明:模拟各向异性;
皮肤/玉/衣服:中间全黑,圆边微微加白:提供不同宽度/亮度的边缘光;
漆面:中间附近大团模糊加白:迎视角方向大范围低强度高光。

MatCap:和Sphere类似,使用观察空间的表面法线的xy分量。
在PBR流行之前,用于模拟金属表面对环境的反射(淡淡的浮影),廉价高效。
将物体看做近似球体,根据曲率采样2D圆形贴图;对小曲率面的效果很糟糕。
可选项一:判断相机Z轴旋转角度、使相机可以用Z轴旋转xy值。
可选项二:表面使用法线贴图,增加表面细节,解决表面曲率不足的问题。
可选项三:表面法线的xy值在|0-1|内重新分布,靠近中心或者分散。
可选项四:表面法线的xy值围绕(0, 0)缩放:(xy - scale)/(1- 2*scale)
可选项五:使用mipmap产生模糊效果:tex2Dlod(matcap, uv, 0.0, miplevel)

MatCap用于表现异常状态效果:
比如人物被冰冻了、中毒了、石化了,将效果拆解,更换人物(皮肤)的MatCap贴图。

MatCap用于模拟人物专用点光源:
在比较昏暗的环境,人物受到近处一个点光源照射的话,根据法线方向受光;
将受光的法线方向范围,转移到MatCap贴图中,提供区域性的亮度增量。

镜中判定:如果当前计算发生在平面反射相机,Overlay(观察空间)效果应该左右反转:
float3 crossZ = cross(UNITY_MATRIX_V[0], UNITY_MATRIX_V[1]);
float isMirror = dot(crossZ, UNITY_MATRIX_V[2]) < 0 ? 1 : -1;

修复观察空间中的表面法线:重构V矩阵使相机朝向像素,修复圆形边缘xy值。

遮罩式高光

遮罩式高光类似于阴影查找表,使用高光强度作为依据,查表判断是否达到点亮要求。
Phong高光强度:specular = pow(RdotV, 平滑度); //光源的反射方向R
Blinn高光强度:specular = pow(max(0, NdotH), 平滑度);
球面上会有一个高光最强的点,围绕这个点逐渐衰减;
通过自定义调整高光强度公式,可以使强度聚拢或分散,实现强度再分布。

限制在水平方向分布:
卡通追求连片的高光,需要垂直同步高光强度,可以修改高光强度线索NdotH:
在XZ平面计算NdotH:NdotH = dot(normalize(N.xz), normalize(H.xz));


使用高光遮罩限制可高光区域:只让有限的区域在高光强度高于阀值时处于高光状态。

各向异性高光

渲染中的各向异性通常体现在真实的金属和头发表面上,这些表面虽然整体光滑,在细微观察下却极为不平整,光线反射出来可能要弹射好几次;人的头发在灯光照射下表现出斑驳的高光;壶、刀等金属制品则有拉丝效果。
各项异性高光的好处是,从不同角度看有丰富的变化;需要处理好连片高光的问题。
参考资料:Kajiya各项异性头发高光模型

计算过程:
沿着顶点的世界空间的法线方向,扰动世界空间的副法线;
使用偏移后的副法线B与高光方向H的sin值作为高光强度依据。

推导过程:
要求头发的发丝方向为UV的V轴方向,发根在上,向下延伸;
切线T方向顺着UV的U轴,横向,从左到右;
副切线B方向顺着UV的V轴,纵向,从上到下(正好是发丝的方向);
假定头部是个球,根据观察角度V和光源角度L的变化,H的活动范围非常大;
假定V水平正对着球形的中心,可以形成一个水平的横截面(我们总能找到这个截面);
L可能从上方或者下方某个角度射过来,但是H必定落在截面的同一边(根据纵向分量);
假定B暂未偏移过,那么B垂直于V,B与H的夹角大于90度(取决于H与水平的夹角);
specular = sinBH;
以此值为阀值,就可以得到一个高光区域,强度在sinBH到1之间,问题是1在哪。
L和B不动,我们观察中心点偏上面一丁点的位置,需要将V的角度稍微往上移一点;
截面也跟着朝下倾斜了一点,导致H与水平的夹角变小(直到为0后再变大);
如果我们提高阀值要求,可以缩小高光范围,得到一个圆形光环。

球面上,每个像素的观察角度都不同,观察角度的变化使specular有规律的变化;
所有specular为1的点,可以连成一个环,形成连片高光的轨迹;
扰动B的方向,相当于调整了纵向的视角,specular值在纵向再分布。


各项异性高光的难点在于副切线偏移贴图的绘制,以满足多样性的需求。
比如魔改AngelRing、连片的小团子、W型的波浪,在各种影视游戏作品中都有出现。
通过对公式的调整,改变轨迹的形状,实现纵向偏移和横向偏移。

描边

描边是卡通从传统手绘艺能中借鉴的操作,用于突出颜色块的轮廓,模拟笔迹;
单纯化明暗、强调线(line),以漫画/动画的风格进行实时渲染。

根据作品风格不同,描边的宽度差异很大:
粗描边:形象平面化,简洁、易识别;考验设计、审美能力。
细描边:形象立体化,增加人物细节,界定边界提高识别度。

描边色
描边通常没有固定宽度和颜色,在视觉感受上是和漫反射一体的,受艺术家支配;
所以,漫反射色的比例是一个参数,皮肤和头发的描边色的漫反射比例会有不同;

另外,特殊视角下,我们可能期望描边色淡化一些(参考动漫人物),比如下巴两侧。

思路一:顶点位置沿某个方向移动一定距离,Cull Front,Queue+1。
由于在vertex阶段(光栅化之前)修改顶点位置,相当于是对模型的变形;
移动方向:模型空间的法线方向、NDC空间的法线方向、顶点位置(用于硬表面);
移动距离:相机远于一定程度关闭效果;随着靠近有限放大。
问题一:描边宽度大于1像素时,需要调整法线方向(平滑法线)修复断点;
问题二:非边缘的折痕面,无法通过backface描画,需要检测法线方向。

思路二:图像后处理-使用算子进行边缘检测-判断不连续的数据(深度/法线)
问题:使用统一着色,不方便逐像素着色;
描边的物体必定是我们想突出显示的物体,所以不能滥用,需要遮罩。
举例:不连续的地图、陷阱,需要用描边突出边界,起到提示作用。

边缘光


PBR中,边缘亮度源于涅菲尔反射,当视线接近垂直表面时,反射越明显;
比如垂直看水面时,折射率高能看到水底,水平看水面时看不到水底;
边缘光效果与涅菲尔反射之间,只有关于NdotV的间接关系;
边缘光的亮度可以从侧面表现物体的光滑度,粗糙的表面使边缘光模糊;
边缘光与描边都能中和角色的高饱和度(加白/加灰),提高识别度。
依赖遮罩控制效果的范围和细节形状。
类似于漫反射衰减,边缘光宽度能辅助建立视觉深度,体现物体厚度。

边缘光的使用倾向

思路一:滥用边缘光,但又不破坏色彩平衡,实现极不真实的平面卡通效果;
非真实倾向下边缘光外紧跟着一层描边,应避免跟随太久显得累赘;
根据饱和度适当保留边缘光,比如仅上方、左/右(强调光源方向)。

思路二:昏暗环境下,朝光源方向有少量边缘光(Bloom),实现拟真的CG效果;
CG环境比游戏中光污染严重的多,很注重渲染气氛,月光、灯笼都能Bloom;
真实倾向下,用高模表现细节,减少对描边的依赖,Bloom后与背景融合。

角色专用边缘光探针

边缘光与高光的能量来源不同,昏暗角落的边缘不应该有边缘光,受实时AO牵制。
所以我们很需要一个或多个实时的探针,用于确保角色能采样到正确的边缘光。

法线


法线贴图
法线贴图可以增加表面细节,我们可以把多出的细节提供给任何光照部分;
但是细节增加并不意味着整体渲染质量的增加,法线的剧烈变化产生不自然的阴影;
突出来的细节对应UV贴图中的位置不会变,

法线调整

调整切线空间Z轴的倾斜比例

光源

GI

逐顶点光源

逐像素光源

体积光

固定灯光和动态激光

卡通专用材质

全材质自定义化

HSV空间转换


在HSV空间中,我们可以更方便的对颜色施加感官方面的影响:
Hue,色调,色环的弧度,绕360°旋转可得到原颜色,0-360°;
Saturation,饱和度,从中心往边缘走颜色变得鲜艳,0-1;
Value,值,亮度,从下往上变亮,0-1。

在shader中,进行HSV空间相关的计算(参考Color.hlsl):
half3 hsvColor = RgbToHsv(baseColor.rgb); //将颜色转到HSV空间
//进行自定义计算调整hsv的三个分量
half3 linearColor = HsvToRgb(hsvColor); //将颜色转到线性空间

镜面

皮肤

晶莹剔透与简单2个色阶之间的抉择;
脸部需要减少明暗过渡区范围。

眉毛

显示在前面的眉毛
Stencil测试
ZTest GEqual:美貌使用比头发和脸稍后的序列值,空间位置在皮肤和刘海中间。

头发

AlphaClipping

刺猬头:消除过于复杂的描边
微微增减Z值,使描边体积后退一点点,提高了被深度擦除的比率。

眼睛

眼睛折射:
假设晶状体有厚度,类似于玻璃珠的感觉;
观察方向V在半球的截面(UV面)留下偏移量,获得新的UV。
float3 offset = float3(dot(V, uWorld), dot(V, vWorld), dot(V, eyeForward));
在纹理U轴个V的偏移量(折射的角度) 折射的厚度
将offset转化为纹理上的二维向量,使用科学方式计算折射率。

Glitter

斑驳的高光材质,噪音分布,鳞片状反光点。

双面材质

外套

双面透明材质:

纱布手套 模糊+折射

基于物理的折射

光在跨越介质传播时产生折射现象,假设我们的眼睛正在观察水底;
观察点坐标:我们的眼睛位置,也就是相机坐标;
观察方向:从水面一点到眼睛的方向;
入射角:水面的法线与观察方向的夹角;
折射方向:从水底某处出发到水面一点的方向;
折射角:水面的法线与折射方向的夹角;
角度比率:入射角/折射角,值与参与折射的两种介质有关,如:空气和水;
角度比率参考值:真空中光速为1,光在空气中传播速度比水中快,角度比率小于1(1 / 1.33);
水底坐标A:水面的正下方的水底的点;
水底坐标B:如果沿着观察方向与水底相交的点;
水底坐标C:如果沿着折射方向与水底相交的点;
光线折射率:根据菲涅尔(Fresnel)现象,观察角度越靠近掠射角时,光线参与镜面反射的比率越大,金属的反射率随观察角度变化的很小;
不同波长的光的折射率:波长越小,参与折射的比例越高;如:红光的波长长,折射率小,在雾中能传播的更远;
偏移量:与水底的深度分布和折射角有关;当斜着看水面时,水底因为透视视角变得倾斜,加上水底表面可能凹凸不平,我们无法使用折射方向与水底模型进行碰撞测试,使评估水底坐标C变得非常有难度;
方案A:使用逐像素的物理碰撞测试,这样可以清晰的看到水中的石头和鱼,丰富细节;
方案B:假设水底是一个平面,求折射方向在水底平面的投影向量,根据水面的波浪扭曲折射;
方案C:根据法线贴图中法线的扰动方向和偏移量系数,手动控制偏移方向和偏移量;
颜色混合:镜面反射+漫反射+折射,光线使用不同分量参与光照;

折射-焦散

卡通专用后处理

全后处理自定义化

Bloom

1/4 1/8 1/16 1/32

遮罩
方案一:使用Stencil作遮罩
方案二:使用A通道定义Bloom强度
透明队列的物体使用双Pass完成混合操作 第二个Pass不混合A通道

残影(轨迹)

LensGlare

物理系统

布料

动画系统

GPU Skinning

对模型的顶点的法线和切线进行差值

Lookat System

表情系统

嘴型同步

Keijiro的音乐仓库 - 识别音乐

角色慢动作

根据timescale的实时改变雨滴速度/形状

天气系统

云-天空盒子
早中夕夜的时间变化 阴晴-雷-雨-雾 春夏秋冬

卡通场景

学校-教室-游乐园-魔幻元素
烘焙


关注成长,注重因果。