临时标题 -- 卡通渲染?
关于卡通渲染,在很久我曾经写过一篇文章,记录了我第一次修改ue4引擎然后仿了个O的效果,时至今日,已然工作两年的我,对二次元渲染也有了一些新的理解与认识,因此在这里重新写一篇关于我对卡渲的理解,这一次我并不想将重心放在具体实现的代码上,而是'为了这个效果'我做了'什么什么',以及对"光"的理解。
现在我认为卡渲最重要的就是把光效调好,摄影圈有句话叫 “摄影是用光的艺术” 我觉得卡通渲染也是如此,仅仅把卡通材质写好并不算难,也没多少内容,主要还是与光的交互,最重要的能提升质感的就是光照效果。
做完写完之后总感觉还是缺了点什么,更加细节的补充也只能之后再慢慢来了-.-
一张简单的最终效果(等尾图做完美了再替换过来当首图吧)
少前2的模型真好看啊,因为某游模型目前不能用,这里暂时全用少前的来吧
一 基础效果
关于二次元,我最初认为赛璐璐才是正统,现在感觉细节更丰富的基于物理的卡渲更加好看,所以,基础效果这里就是对粗糙度金属度的调整。
这里我直接放在了材质连连看里,直接调整BaseColor的结果,这样只有主光源可以对粗糙度金属度造成影响,点光源不参与。并且对于这一部分我并没有做的太复杂,只是进行一些NoL,NoV的计算,连金属的MatCap都没有加,这样的效果我觉得是足够了的(也许以后还会进行调整?)。
二 阴影
卡渲的灵魂,二值化的阴影(如下光学核心的阴影图例)
在UE中,要修改阴影就要在DeferredLight阶段计算光照的时候把想要的结果算好,这里我是计算NoL然后与SurfaceShadow进行混合,再将结果对Ramp图进行采样。
if (GBuffer.ShadingModelID == SHADINGMODELID_TOONSKIN)
{
float NPRShadow = dot(N, L) * 0.5f + 0.5f;
//NPRShadow = lerp(NPRShadow * SurfaceShadow, SurfaceShadow, SurfaceShadow);
NPRShadow *= SurfaceShadow * TrickHairShadow;
float3 RampColor = Texture2DSampleLevel(View.ToonRampTexture, View.ToonRampSampler, float2(NPRShadow, RampIndex), 0);
ToonDiffuseColor = RampColor;
}
LightAccumulator_AddSplit(LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, ToonDiffuseColor * MaskedLightColor, bNeedsSeparateSubsurfaceLightAccumulation);
- 关于自阴影
可以在计算surfaceShadow的时候将自阴影关掉,以获得更干净的阴影表现
if (GBuffer.ShadingModelID == SHADINGMODELID_TOONSKIN || GBuffer.ShadingModelID == SHADINGMODELID_TOONFACE )
{
LightAttenuation.z = lerp(LightAttenuation.z, 1.0f, GBuffer.CustomData.b);
}
- 阴影中的死黑区域
在ue中如果一个阴影区域的值小于等于0,那这一部分是不会进行光照计算的,所以采样ramp的时候会发现有一块区域比ramp的暗部还要黑,这个时候就需要在surfaceShadow的计算中把最小值设置为0.001
if (ShadingModelID == SHADINGMODELID_TOONSKIN || ShadingModelID == SHADINGMODELID_TOONFACE)
{
LightAttenuation = max(LightAttenuation, 0.001f);
}
三 脸部与头发
- 关于脸部阴影
目前,我还是用的SDF来计算脸部阴影,本来想用SDF烘焙一张法线图来做脸部阴影的(前段时间在知乎发现的新方法),但是发现问题很多,传统SDF左右各采样一次的问题依然存在,法线图也没法仅仅传入一张就完美解决,也还是需要对脸部做单独的阴影计算。
在ue中,要计算SDF的脸部阴影,同样需要在DeferredLight中计算,而为了在外部选择不同的面部SDF图,就需要先在材质中采样,然后传入GBuffer中。
- 头发对脸/皮肤的投影
关于头发的投影,通过屏幕空间头发的剪影进行偏移,偏移后的区域当做阴影来计算就好了。所以只需要在GBuffer中将头发区分出来就可以了。
- 眼睛
关于眼睛,少前的贴图画的很好,这里直接把两张贴图放上去效果就已经很好了。眼球能有凹凸的感觉我认为就可以了。
- 眉毛
二次元的眉毛是可以穿透头发的,待制作,不过这一点我觉得可有可无,之后看情况补一下吧。
-.-
- 头发
头发我没有用各向异性高光,直接叠加一个画的高光上去(为什么poposha这里没有,因为她没高光mask,我也懒狗了不想再专门做一个各向异性上去了)。等以后解禁了再补一下别的头发高光图。
四 描边
描边我认为最重要的是把区域划分好,至于描边的算法,知乎上各式各样的都有,我这里只做了最简单的处理,计算周围八个像素的区域,然后平均一下进行计算,我想要的是一种“圆润”的效果,并且这样平均计算之后,发尖的部分也是可以得到很好的效果的。
目前的描边我是放在了后处理中,本想也放在DeferredLight阶段,这样灯光的颜色也可以影响到描边的颜色,但是加了描边之后发现描边不需要太明显,淡淡的存在就可以提升二次元的感觉,修改颜色也很难注意到,并且边缘光也可以将描边晕染,因此描边染不染色就有点无足轻重了。
五 边缘光
对于边缘光,我是直接添加在了灯光上,根据当前视角与灯光方向添加边缘光,仅在侧面和背面会产生边缘光,正面打的光不会存在。
tips:点光源的方向问题,LightData中存的点光源方向是点光源的旋转方向,如果要计算发射状的点光源方向则需要用光源位置-PixelPos重新计算一下光照方向
float4 RimLight(FDeferredLightData LightData, FGBufferData GBuffer, float3 CameraVector, float SurfaceShadow, float3 WorldPos)
{
CameraVector *= -1.0f;
float3 LightDir = LightData.Direction;
///点光源方向
if (LightData.bRadialLight)
{
LightDir = normalize(LightData.TranslatedWorldPosition - WorldPos);
}
float4 RimColor = float4(0.0f, 0.0f, 0.0f, 1.0f);
if (GBuffer.ShadingModelID == SHADINGMODELID_TOONSKIN || GBuffer.ShadingModelID == SHADINGMODELID_TOONFACE)
{
float NoV = 1.0f - abs(dot(GBuffer.WorldNormal, CameraVector));
float RimLightMask = smoothstep(LightData.RimLightWidth, 1.0f, NoV);
float CameraDirValue =saturate(0.75f - Pow2(dot(LightDir, CameraVector) * 0.5f + 0.5f));
float LightDirValue = saturate(1.0f - (dot(LightDir, GBuffer.WorldNormal) * 0.5f + 0.5f) - LightData.RimLightOffset);
RimLightMask *= LightDirValue * LightData.RimLightIntensity * CameraDirValue * lerp(0.5f, 1.0f, SurfaceShadow);
RimColor = float4(LightData.RimLightColor * RimLightMask, 1.0f);
}
return RimColor;
}
- 补充: 屏幕空间的边缘光
(菲涅尔的边缘光局限性还是太大了,稍远一些就会变得要么看不到要么效果不好,增加一个基于深度的等宽边缘光
--图例之后再补
六 光效
Bloom与LightShaft
在不开体积雾的情况下,似乎只有面片的效果更好了
待续,找到其他方法了再补吧()
应用
为什么写了这篇文章,主要还是想做个视频,复刻一下GBC的live 。因此,第一步就是先把卡渲的效果做好,翻看了一下之前的东西,当时的做法很不成熟,效果也实在不是很理想,所以正好在UE5上重做一下,而关于光影的内容也是我之前不曾做过的,也正是这次的重点。
整场live复刻起来工作量实在有些难度,先从一帧开始吧。
(这个灯阵好麻烦啊,正式做的时候再细调吧,面片的LightShaft的质感还是不够,要做成上图这样还需要晕染一下)
最后
对于卡通渲染的内容应该已经结束,“应用”环节也只是新内容的起始,之后实际制作中应该会有很多琐碎的小内容修改,不过不会影响到目前的整体效果,现在的效果就是我个人比较喜欢的二次元渲染效果了,干净通透的赛璐璐加一些pbr的光影细节。
放一下修改后的引擎链接吧,不过现在并不完善,等完全做完再解除私有吧(而且github的这个fork好像也不能直接公开,要改也有点麻烦)
https://github.com/BlowHail/UnrealEngine--Hail