Init
This commit is contained in:
110
03-UnrealEngine/卡通渲染相关资料/Toon 眼睛反射与视差效果.md
Normal file
110
03-UnrealEngine/卡通渲染相关资料/Toon 眼睛反射与视差效果.md
Normal file
@@ -0,0 +1,110 @@
|
||||
## PBR Refrection(有问题)
|
||||
```
|
||||
//input anteriorChamberDepth 贴图 使用HeightMap
|
||||
//input radius
|
||||
//input mask 贴图
|
||||
//input texcoord
|
||||
//input n 折射率比值
|
||||
//input frontNormalW 贴图float3(0,0,1) Local=>World
|
||||
|
||||
// 角膜区域突起的模型 height = max(-positionL.z – eyeIrisDepth, 0.0);
|
||||
// 球形模型 Plot[Max[1.0 - 18.4 * r * r, 0.0], {r, 0, 0.3}]
|
||||
float height = anteriorChamberDepth ;//* saturate( 1.0 - 18.4 * radius * radius );
|
||||
|
||||
float3 normalW=Parameters.WorldNormal;
|
||||
float3 viewW= mul(float3(0,0,1),(float3x3)View.ViewToTranslatedWorld);//CameraViewToTranslatedWorld
|
||||
|
||||
// refractedW
|
||||
float w = n * dot( normalW, viewW );
|
||||
float k = sqrt( 1.0 + ( w - n ) * ( w + n ) );
|
||||
float3 refractedW = ( w - k ) * normalW - n * viewW;
|
||||
refractedW=-normalize(refractedW);
|
||||
|
||||
//float3 frontNormalW=mul(float(0,0,1),float(3x3)GetPrimitiveData(Parameters.PrimitiveId).LocalToWorld) * -1;
|
||||
float cosAlpha = dot(frontNormalW, refractedW);
|
||||
float dist = height / cosAlpha;
|
||||
float3 offsetW = dist * refractedW;
|
||||
float2 offsetL = mul(offsetW, (float3x2)GetPrimitiveData(Parameters.PrimitiveId).WorldToLocal);
|
||||
texcoord += float2(mask, -mask) * offsetL;
|
||||
|
||||
return texcoord;
|
||||
```
|
||||
### 原始代码
|
||||
```
|
||||
// 角膜区域突起的模型
|
||||
// Alternatively, use a displacement map
|
||||
// height = max(-positionL.z – eyeIrisDepth, 0.0);
|
||||
|
||||
// 球形模型
|
||||
// Plot[Max[1.0 - 18.4 * r * r, 0.0], {r, 0, 0.3}]
|
||||
height = anteriorChamberDepth * saturate( 1.0 - 18.4 * radius * radius );
|
||||
|
||||
// refractedW
|
||||
float w = n * dot( normalW, viewW );
|
||||
float k = sqrt( 1.0 + ( w - n ) * ( w + n ) );
|
||||
float3 refractedW = ( w - k ) * normalW - n * viewW;
|
||||
|
||||
float cosAlpha = dot(frontNormalW, -refractedW);
|
||||
float dist = height / cosAlpha;
|
||||
float3 offsetW = dist * refractedW;
|
||||
float2 offsetL = mul(offsetW, (float3x2) worldInverse);
|
||||
texcoord += float2(mask, -mask) * offsetL;
|
||||
```
|
||||
## Matcap反射效果
|
||||
```
|
||||
float2 CalcMatcapUV(FMaterialPixelParameters Parameters,float3 normalTexture)
|
||||
{
|
||||
float3 ViewVector=Parameters.CameraVector * -1;
|
||||
float3 ViewSpaceRightVector=normalize(cross(ViewVector , mul( float3(0.0,1.0,0.0) , ResolvedView.ViewToTranslatedWorld )));
|
||||
float3 ViewSpaceUpVector=cross(ViewVector ,ViewSpaceRightVector);
|
||||
|
||||
float3x3 Matrix=float3x3(
|
||||
ViewSpaceRightVector.x,ViewSpaceUpVector.x,ViewVector.x,
|
||||
ViewSpaceRightVector.y,ViewSpaceUpVector.y,ViewVector.y,
|
||||
ViewSpaceRightVector.z,ViewSpaceUpVector.z,ViewVector.z
|
||||
);
|
||||
|
||||
float3 ZeroOneNormal=mul(normalize(Parameters.WorldNormal+normalTexture),Matrix)*0.5+0.5;
|
||||
return float2(ZeroOneNormal.x,ZeroOneNormal.y*-1);
|
||||
}
|
||||
```
|
||||
|
||||
### 这个不太行
|
||||
```
|
||||
//input normalTexture 法线贴图
|
||||
float3 TangentWS=Parameters.TangentToWorld[0];
|
||||
float3 NormalWS=Parameters.TangentToWorld[2];
|
||||
float3 BinormalWS=cross(NormalWS, TangentWS);
|
||||
|
||||
float3 worldNormal;
|
||||
worldNormal.x = dot(float3(TangentWS.x,BinormalWS.x,NormalWS.x), normalTexture);
|
||||
worldNormal.y = dot(float3(TangentWS.y,BinormalWS.y,NormalWS.y), normalTexture);
|
||||
worldNormal.z = dot(float3(TangentWS.z,BinormalWS.z,NormalWS.z), normalTexture);
|
||||
worldNormal = normalize(worldNormal);
|
||||
|
||||
float3 e = normalize(GetWorldPosition(Parameters) - ResolvedView.WorldCameraOrigin);
|
||||
float3 reflectVector = reflect(e, worldNormal);
|
||||
float3 reflectVectorVS = normalize(mul(reflectVector,ResolvedView.TranslatedWorldToView));
|
||||
float m = 2.82842712474619 * sqrt(reflectVectorVS.z + 1.0);
|
||||
float2 cap = reflectVectorVS.xy / m + 0.5;
|
||||
cap=cap*0.5 + 0.5;
|
||||
return cap;
|
||||
```
|
||||
|
||||
## 高光贴图位置调整
|
||||
```
|
||||
//input float2 LeftUp,LeftDown,RightUp,RightDown;
|
||||
|
||||
float3 viewW = mul(float3(0,0,1),(float3x3)View.ViewToTranslatedWorld);
|
||||
float3 viewL = mul(viewW, (float3x2)GetPrimitiveData(Parameters.PrimitiveId).WorldToLocal);
|
||||
float2 viewL2D = normalize(viewL.xy);
|
||||
|
||||
float2 Left=lerp(LeftUp,LeftDown,viewL2D.y);
|
||||
float2 Right=lerp(RightUp,RightDown,viewL2D.y);
|
||||
return lerp(Left,Right,viewL2D.x);
|
||||
```
|
||||
## 焦散
|
||||
- dot(Parameters.WorldNormal,View.DirectionalLightDirection);
|
||||
- Fresnel
|
||||
|
||||
乘以焦散Mask即可
|
98
03-UnrealEngine/卡通渲染相关资料/UEToonShading/EOE.md
Normal file
98
03-UnrealEngine/卡通渲染相关资料/UEToonShading/EOE.md
Normal file
@@ -0,0 +1,98 @@
|
||||
## EOE需求
|
||||
- 渲染功能
|
||||
- 描边功能改进
|
||||
- [x] ScreenPercentage 自适应(移植代码,1小时)
|
||||
- [ ] Fov 自适应 (1~2天)
|
||||
- [ ] 根据投射调整宽度 (1~2天)=> (使用李兄的方案,0天)
|
||||
- [ ] 支持PBR描边控制 (2~4天) => (使用李兄的方案,0天)
|
||||
- 厚涂风格卡通皮肤渲染
|
||||
- [ ] 移植厚涂引擎中的代码 (移植代码,2~5天)
|
||||
- 脸部阴影控制
|
||||
- [ ] 角色在向左/向右旋转头部时,面部不会产生阴影和一些残留的”脏”的阴影信息 (14天)
|
||||
- 眼睛
|
||||
- [ ] 眼白阴影 (5天)=>(使用李兄的方案进行进一步开发,1天)
|
||||
- 头发
|
||||
- [ ] 头发高光调整 (移植代码,1天)=>(使用李兄的方案,0天)
|
||||
- 卡通材质重制
|
||||
- ToonStandard (5~7天)
|
||||
- [ ] 制作母材质以及其他Toon材质的MaterialInstance
|
||||
- ToonSkin (1~2天)
|
||||
- ToonHair(1~2天)
|
||||
- ToonCloth
|
||||
- [ ] 制作母材质以及几套衣服的案例。(1~2天)
|
||||
- [ ] 大约40套衣服,每套衣服(上衣、裙子、鞋子、其他饰品)包含3~6个材质球 (时间不确定,而且体力活,可能是乐华那边做了)
|
||||
- 其他功能
|
||||
- 将UE4插件移植到UE5
|
||||
- [ ] ZeroAnim(大致的修改已经完成,等待测试。)
|
||||
- [ ] JsonAssistPlug(大致的修改已经完成,等待测试。)
|
||||
- 角色动画修型(可选)
|
||||
- [ ] 替换潘翔的动画蓝图,使用UE5小灰人自带的PoseDriver进行修型。(没具体研究过时间不确定,14~28天)=> (7~14天)
|
||||
- ShowCase关卡与文档制作
|
||||
- ShowCase关卡(可选)
|
||||
- [ ] ShowCase关卡
|
||||
- [ ] ContentExample关卡
|
||||
- [[#文档]]
|
||||
- [x] 搭建文档系统
|
||||
- [ ] 编写文档(14天左右)
|
||||
- 后续开发计划(看乐华感不感兴趣)
|
||||
- 表情系统
|
||||
- [ ] 开发接近Live2D与MMD的表情系统与相关制作规范 (乐华未提,但后续应该花时间去开发一下)
|
||||
- [ ] 参考 https://www.youtube.com/watch?v=YGZB8-2Kazk 尤其是0:28、2:32左右。 https://www.youtube.com/watch?v=eC_Om8idXTo
|
||||
- [ ] 程序化眼睛 (乐华未提,但后续应该花时间去开发一下)
|
||||
- 定制渲染功能开发
|
||||
- [ ] 乐华那边测试完之后还需要的渲染功能。
|
||||
- 嘴唇效果提升
|
||||

|
||||

|
||||

|
||||
|
||||
## 耗费工时
|
||||
- 楼嘉杰:35~60天 => 30天 主要负责开发功能、打包引擎、编写文档、规划与制作ShowCase ContentExample关卡。
|
||||
- 华湛飞:5天=> 3天左右(并不是连续的5天),负责协助`楼嘉杰` 制作配套资产与贴图。
|
||||
|
||||
## EOE需要制作的资产
|
||||
DAWA 杭州负责把几个ShaderModel的主材质以及材质实例规范做好。同时也会选择一份有代表性的角色资产,制作贴图与材质。
|
||||
|
||||
- 模型(非必要,看透明后处理描边效果再做决定)
|
||||
- 提取角色下颚与嘴唇处的模型,用于头部外扩描边。
|
||||
- 贴图
|
||||
- 不用重新绘制的贴图
|
||||
- BaseColor
|
||||
- Metallic
|
||||
- Roughness
|
||||
- Opacity
|
||||
- Normal
|
||||
- ShadowColor(暗部颜色指定)
|
||||
- ShadowFalloff(阴影过渡控制)
|
||||
- ToonDataTexture(每个贴图4个通道,根据需求绘制,主要适用于控制角色光影交接、轮廓颜色粗细控制、轮廓id)
|
||||
- ToonDataA
|
||||
- ToonDataB
|
||||
- ToonDataC
|
||||
- 厚涂皮肤相关
|
||||
- Trickness(厚涂皮肤材质专用,使用八猴子烘焙Trickness与Curvature混合,并调整后获得)
|
||||
- 次表面颜色贴图
|
||||
- ToonAssetID贴图
|
||||
- 其他
|
||||
- 轮廓FOV自适应曲线资产
|
||||
- 轮廓距离衰减曲线资产
|
||||
- ToonAsset材质集合(一个卡通渲染用材质集,根据需要添加)
|
||||
- 全局贴图(3张预积分贴图,按照需求进行更改)
|
||||
- 材质
|
||||
|
||||
## 项目要求
|
||||
- 渲染功能需求
|
||||
- 将卡通渲染引擎升级至UE5版本。
|
||||
- 描边功能改进。包括ScreenPercentage、Fov自适应;根据投射调整宽度 ;支持PBR描边控制。
|
||||
- 提升卡通皮肤渲染效果。
|
||||
- 实现面部阴影控制功能,使得角色在各个角度的面部阴影都可以控制。
|
||||
- 卡通材质增加眼白阴影效果。
|
||||
- 提升卡通渲染头发高光效果。
|
||||
- 卡通渲染主材质移植。
|
||||
- 其他需求
|
||||
- 将UE4插件ZeroAnim、JsonAssistPlug升级至UE5版本。
|
||||
- 角色动画蓝图移植。
|
||||
|
||||
|
||||
# 竞品参考
|
||||
- 四禧丸子
|
||||
https://www.bilibili.com/video/BV1uy4y1Q7BU/?spm_id_from=333.788.recommend_more_video.5&vd_source=d47c0bb42f9c72fd7d74562185cee290
|
82
03-UnrealEngine/卡通渲染相关资料/UEToonShading/EOE动画计划.md
Normal file
82
03-UnrealEngine/卡通渲染相关资料/UEToonShading/EOE动画计划.md
Normal file
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: EOE动画计划
|
||||
date: 2023-02-06 20:59:13
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
|
||||
# 修型
|
||||
1. UE5的骨骼。
|
||||
1. 主要的问题是投入/产出(效果)比。
|
||||
2. 是否于动捕软件匹配。
|
||||
3. 
|
||||

|
||||
|
||||
2. 提一嘴骨骼长度与中之人是否匹配。不太清楚 动捕的数据是否会在机器里先做一次重定向?本身UE4的重定向因为算法的局限性,对于身体比例不同的模型会有一些偏移。
|
||||
|
||||

|
||||
- 【初音未来】 「CH4NGE」 运动捕捉 【MMD】
|
||||
https://www.bilibili.com/video/BV1kL4y1A77G/?spm_id_from=333.788.video.desc.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
|
||||
|
||||
- 动捕设备外设
|
||||

|
||||
https://www.bilibili.com/video/BV1Y7411q7m3/?spm_id_from=333.337.search-card.all.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
|
||||
|
||||
# 修型
|
||||
1. 尝试使用曲线来修型。
|
||||
2. 使用PostProcess_AnimBP进行后处理修型。
|
||||
1. 将之前的逻辑移植到后处理动画蓝图中。
|
||||
|
||||
# 保持角色基础结构相同的原因
|
||||
1. 动画资产可以复用。
|
||||
2. 只有一个负责逻辑的主动画蓝图,方便管理。
|
||||
3. 可以使用主动画蓝图设计逻辑,子动画蓝图调节不同参数的功能。
|
||||
4. 对于后续其他衍生品生产会比较友好(手机AR=>Hololive app,ASoul VR演唱会,以及其他延伸游戏)
|
||||
|
||||
## 共享骨架
|
||||
骨架资产的一个重要特性是单个的骨架资产可以由多个骨骼网格体使用,只要其需要拥有相同的整体rig层级。这意味着骨骼命名和骨骼的层级排序必须一致,才能够正确地共享。
|
||||
举个例子,一个骨骼网格体中的一个肢拥有3块骨骼,分别命名为 **1**、**2** 和 **3**:
|
||||
|
||||

|
||||
如果有另一个需要使用相同骨架资源的骨架网格体,则需要保证这些骨骼的命名和排序相同。 然而第二个骨骼网格体可以添加额外或者层级外部的骨骼。如果接收到的动画数据是用于骨骼网格体之外的骨骼,那么该数据会被忽略。
|
||||
|
||||
在这种情况下,你的新层级应该如下所示。在这里,第二个骨骼网格体有着额外的骨骼,但是并没有改变第一个骨架的层级结构,也没有造成冲突。
|
||||
|
||||

|
||||
然而,为使两个骨架网格体使用相同的骨架资源,无法对层级进行重新排序,也无法重命名骨骼。如果第二个骨骼网格体要使用不同的骨骼层级和命名结构,那么需要重新创建一个新的骨骼资产。
|
||||
|
||||

|
||||
如果你在不改变顺序的情况下插入一个骨骼,那么能够正常共享。然而大部分情况下,额外的骨骼可能会导致骨架产生意料之外的变形偏移。我们建议尽量避免这样做。
|
||||

|
||||
结合这些共享规则,再虚幻引擎中有几种方式来在骨骼网格体之间共享骨架。以下是一些细节。
|
||||
|
||||
### 导入期间合并
|
||||
第一种共享骨架的方式是在FBX导入过程期间进行的。导入你的新骨骼网格体时,(包含额外的和外部的骨骼,遵循上述的共享规则),你可以从项目中已有的骨骼网格体中选择一个骨架。虚幻引擎将会将这些骨架合并,并且将全部新骨骼添加到层级中。除此以外,你的骨架的比例会由创建它的原始骨骼网格体来定义。
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
## IK Rig 与Fullbody IK
|
||||
[[实时重定向与骨骼动画共用相关#IK Rig In Animation Blueprint]]
|
||||
|
||||
- 解决高跟鞋与平底鞋转换的问题。(应该可以)
|
||||
- 可以解决动捕中一些穿帮问题。
|
||||

|
||||

|
||||
|
||||
嘉然爬椅子
|
||||
https://www.bilibili.com/video/av507701005/?vd_source=d47c0bb42f9c72fd7d74562185cee290
|
||||
|
||||
但更多的是一些用于在交互游戏中比较有用,比如让中之人去按按钮,使用这个技术可以保证伸手按的地方是对得上的。
|
||||
|
||||
## 基础结构相同
|
||||
Skeleton Compatibility,官方翻译为**可兼容的骨架**,不同骨骼之间共享动画的功能。
|
||||
https://docs.unrealengine.com/5.0/en-US/skeletons-in-unreal-engine/#compatibleskeletons
|
||||
|
||||
[[实时重定向与骨骼动画共用相关#UE5实时重定向]]
|
||||
|
||||
## 其他
|
||||
- Motion Wraping
|
||||
- 机器学习 实现肌肉效果
|
408
03-UnrealEngine/卡通渲染相关资料/VRM4U/VRM4U 功能分析与功能列表翻译.md
Normal file
408
03-UnrealEngine/卡通渲染相关资料/VRM4U/VRM4U 功能分析与功能列表翻译.md
Normal file
@@ -0,0 +1,408 @@
|
||||
# VRM4U角色
|
||||
https://hub.vroid.com/en/users/36144806
|
||||
|
||||
B站整理 https://www.bilibili.com/video/BV1gR4y1j7sK/?spm_id_from=333.788.recommend_more_video.2&vd_source=d47c0bb42f9c72fd7d74562185cee290
|
||||
|
||||
- Alicia Solid:https://hub.vroid.com/en/characters/515144657245174640/models/6438391937465666012
|
||||
- AvatarSample_F:https://hub.vroid.com/en/characters/6193066630030526355/models/537531113514541613
|
||||
|
||||
# VRM4U学习笔记
|
||||
VRM4U是一个不错的插件,在此记录一下功能与大致实现方式。
|
||||
快速入门文档:https://ruyo.github.io/VRM4U/01_quick-start/
|
||||
功能介绍视频:https://youtu.be/epcQ-uU6tfU
|
||||
|
||||
视频中的右上角的自定义编辑器可以通过搜索"WBP_"来找到对应的EditorUtilityWidget,之后在Asset上有右键——点击运行EditorUtilityWidget。
|
||||
|
||||
- VRm4U:定义了主要的VrmAssetListObject、VrmMetaObject、VrmLicenseObject、VrmRuntimeSettings以及若干组件与动画节点。
|
||||
- VRM4UImporter:主要的导入相关逻辑实现:导入流程、导入设定窗口、若干动画节点。
|
||||
- VRM4ULoader:导入数据转换以及生成逻辑:数据转换与生成Asset相关的类、UVrmLoaderComponent与若干组件实现。
|
||||
- VRM4UEditor:Sequence编辑器函数EvaluateCurvesFromSequence()实现。
|
||||
- VRM4UMisc:Log日志类型定义。
|
||||
|
||||
PS.
|
||||
- FVRM4UModule模块启动与结束时,会分别注册/卸载
|
||||
- FVRM4UImporterModule模块启动与结束时,会分别注册/卸载 UVrmAssetListObject、UVrmLicenseObject、UVrmMetaObject自定义Asset类型与FVRMRetargetSrcAnimSequenceCustomization自定义属性编辑器。
|
||||
- FVRM4ULoaderModule模块启动与结束时,加载/卸载assimp的动态链接库;如果勾选**Drop VRMFile Enable**,则会
|
||||
|
||||
## VRM4U的格式导入功能
|
||||
值得学习:
|
||||
- Runtime Import功能:在游戏打包后,依然可以通过拖拽文件到游戏窗口的方式来导入VRM文件。
|
||||
- 导入窗口实现:实现一个导入窗口(SVrmOptionWindow),这样Runtime与Editor导入都可以输入自定义的参数。
|
||||
|
||||
待改进:
|
||||
- 没有实现VMD的导入逻辑(虽然实现了BVH格式的导入逻辑)
|
||||
|
||||
### 导入功能
|
||||
VRM4U使用Assimp库来进行格式解析,并且通过修改GLTF来实现VRM格式导入(VRM是基于GLTF开发的)。插件作者也公开了魔改的Assimp代码,当然最好的方式就是下载编译好的库文件与h文件直接引用了。
|
||||
|
||||
得益于Assimp,所以也可以导入PMX格式,但需要勾选 Project Setting>Plugin>VRM4U>Allow All AassImp Format的选项,之后就可以导入PMX格式的模型。(开放格式:pmx、obj、fbx、dae、gltf)
|
||||
|
||||

|
||||
|
||||
同时可以通过下面的Ext List来添加其他格式的后缀名,来使用VRM4U导入。
|
||||
|
||||
主要逻辑位于VRM4UImporter模块的UVRM4UImporterFactory中。
|
||||
|
||||
#### FactoryCreateBinary
|
||||
UVrmImportUI用于存储用户设置的导入选项数据。ULoaderBPFunctionLibrary::GetVRMMeta()用来导入VRM的Meta数据。ULoaderBPFunctionLibrary::LoadVRMFileFromMemory()为核心导入逻辑。
|
||||
|
||||
导入流程为:
|
||||
1. 在初始化UVrmImportUI(导入选项对象)后,会调用ULoaderBPFunctionLibrary::GetVRMMeta(),通过Assimp读取VRM文件的Meta数据(缩略图与版权信息)来填充UVrmImportUI。对于PMX格式会有额外的设置。(模型缩放1=>0.1、不合并材质、不合并图元、强制材质双面显示)
|
||||
2. 取得父窗口,之后创建并添加自定义的SWindow2包裹SVrmOptionWindow来进行导入参数显示,会在用户选择好数据后,进行之后的步骤。
|
||||
3. 创建TAssetPtr<UVrmAssetListObject>、TArray< TAssetPtr<UObject> >、TAssetPtr<UClass>对象。
|
||||
4. 取得默认加载设置对象UVrmRuntimeSettings,之后会依次尝试载入VrmObjectListBP、VrmAssetListObjectBP、UVrmAssetListObject来初始化上一步说的TAssetPtr<UVrmAssetListObject>。
|
||||
5. 创建static VRMConverter::Options并使用之前的用户修改过的设置选项进行初始化。
|
||||
6. 使用上一步取得选项对象,并调用ULoaderBPFunctionLibrary::LoadVRMFileLocal()来导入文件。导入的存放在UVrmAssetListObject mret中。
|
||||
7. 返回mret->GetOuter();
|
||||
|
||||
#### LoadVRMFileFromMemory
|
||||
1. 根据后缀名设置对应参数
|
||||
2. 调用Assimp::Importer的ReadFileFromMemory()或者ReadFile()读取文件来获取场景节点aiScene。
|
||||
3. 创建UVrmAssetListObject* OutVrmAsset(复制InVrmAsset或NewObject)
|
||||
4. 使用VRMConverter对象,取出VRM的Json与aiScene数据来以此初始化。之后更新OutVrmAsset里的数据(调整材质的AlphaCutoff选项、替换骨骼名称、转换并且生成贴图与材质、转换VRM的Meta数据、转换并且生成模型数据、重命名Meta数据、设置骨骼模型的Rig Pose MorphTarget、转换重定向用的Humanoid、将之前所有数据保存成Asset)
|
||||
|
||||
#### 运行时导入
|
||||
主要的逻辑为调用LoadVRMFileAsync()加载文件并且初始化UVRMAssetList,之后再设置SkeletalMesh或者SpawnActor等一系列设置。
|
||||
|
||||
使用这个功能前首先得确定 Project Settings->Plugin->VRM4U->Drop VRMFile Enable处于勾选状态才能使用这个功能。
|
||||
功能演示可以查看参考插件Content的Maps/VRM4U_runtimeload,其功能位于DropActor。其挂载了UVrmDropFilesComponent组件,并在Beginplay绑定UVrmDropFilesComponent组件的OnDrogFile委托。其绑定的LoadVrmFronFilePath事件主要逻辑为:创建VRMAssetList对象,之后调用LoadVRMFileAsync()加载文件。之后给SpawnMToonActor,并给骨骼物体设置动画。(还有一些逻辑在关卡蓝图中)
|
||||
|
||||
UVrmDropFilesComponent的主要功能
|
||||
|
||||
它定义了2个委托,并会在组件注册/卸载时让StaticOnDropFilesDelegate绑定/解绑OnDropFilesDelegate_Handler()。OnDropFilesDelegate_Handler()会调用OnDropFiles.Broadcast(FileName);
|
||||
```c++
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FStaticOnDropFiles, FString);
|
||||
static FStaticOnDropFiles StaticOnDropFilesDelegate;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDropFiles, FString, FileName);
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnDropFiles OnDropFiles;
|
||||
```
|
||||
|
||||
UVrmDropFilesComponent::VRMGetOpenFileName()会在关卡蓝图中被调用,用于调用Winapi的GetOpenFileName()来显示一个打开文件用的窗口,在选取了文件之后会调用DropActor的LoadVrmFronFilePath()。
|
||||
|
||||
##### 运行时拖拽导入
|
||||
FDropMessageHandler的父类为IWindowsMessageHandler是一种用与处理Windows消息的一种接口类。FDropMessageHandler的主要逻辑为:
|
||||
1. 取得UVrmDropFilesComponent组件与World指针。
|
||||
2. 如果世界类型为Game与PIE则调用Winapi的DragAcceptFiles()
|
||||
3. 调用Winapi的DragQueryFile()查询拖拽入的文件,并将文件地址传**UVrmDropFilesComponent::StaticOnDropFilesDelegate.Broadcast(Filepath);**
|
||||
|
||||
在FVRM4ULoaderModule::StartupModule()中执行增加MessageHandler。
|
||||
```c++
|
||||
if (FSlateApplication::IsInitialized()) {
|
||||
static FDropMessageHandler DropMessageHandler;// = FDropMessageHandler();
|
||||
//FWindowsApplication* WindowsApplication = (FWindowsApplication*)GenericApplication.Get();
|
||||
TSharedPtr<GenericApplication> App = FSlateApplication::Get().GetPlatformApplication();
|
||||
|
||||
if (App.IsValid()) {
|
||||
auto WindowsApplication = (FWindowsApplication*)(App.Get());
|
||||
|
||||
//WindowsApplication->SetMessageHandler(DropMessageHandler);
|
||||
WindowsApplication->AddMessageHandler(DropMessageHandler);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 渲染与材质
|
||||
值得学习:
|
||||
- 卡通渲染材质(里面有一些faker技巧)
|
||||
- 拍摄模式
|
||||
- Over Parse,模型压平的二次元模式,视角矫正效果
|
||||
|
||||
VRM4U的MToon Lit、MToon Unlit、Subsurface、Subsurface Profile材质类型都是基于MaterialUtil\MToonUtil\M_VrmMToonBaseOpaque,它是作者实现的一个接近UniVRM Shader效果的材质。里面有一些Hack技巧值得学习。上述这几材质类型的区别在于材质实例的属性不同,以MToon Lit与MToon Unlit的区别为例:
|
||||
1. bUseLight是否勾选
|
||||
2. ShaderModel不同:Default Lit与Unlit
|
||||
|
||||
Unlit、PBR基于一个简单材质MaterialUtil\MToonUtil\M_VrmSimple。
|
||||
|
||||
### 轮廓线与投射阴影
|
||||
使用MToonAttachActor来应用轮廓线和自身阴影(Unlit ShaderModel不会接收其他物体的投影)。自己制作的VRM模型可能会有问题,导致Outline与阴影不能出现,建议使用Vroid官方模型进行测试。
|
||||
|
||||

|
||||
|
||||
- SceneCaptureComponent2D组件用于渲染Depth到RT上,以此来制作Shadow。勾选WBP_VRMMaterial->Model->MToonAttachActor->Advanced->Debug Shadow Cube后,会在角色正上方显示深度贴图Cube。
|
||||

|
||||
|
||||
在MF_VrmMToonBase中搜索bUseShadowMap就可以找到所引用的计算函数
|
||||

|
||||
里面逻辑就是通过Depth贴图计算ShadowMask
|
||||

|
||||
|
||||
- UVrmPoseableMeshComponent:用于实现Outline(AOShadow目前没有作用)。主要使用M_VrmNone,一个Masked Unlit材质,通过模型外扩实现Outline效果。
|
||||
|
||||
### 后处理效果
|
||||
MToonMaterialSystem
|
||||
|
||||

|
||||
|
||||
这些蓝图BP的大致逻辑为,挂载带有PostProcess空间组件的BP,之后往里面添加PostProcess材质。
|
||||
|
||||
Content\Util\Actor\Post\sub目录中有
|
||||
- M_PostBloom
|
||||
- M_ColorGradation
|
||||
- MI_ColorGradation
|
||||
- M_CenterBlur
|
||||
|
||||
### 光照工具与PostToon
|
||||
- VRM4U_CameraLight有演示CharacterLightRigActor,方便用户快速摆出主光、轮廓光,其中轮廓光会一直对着摄像机旋转。
|
||||
VRM4U有关的曝光的参数调节技巧:https://ruyo.github.io/VRM4U/02_envlight/
|
||||
|
||||
- PostToonSystem文档:https://ruyo.github.io/VRM4U/02_toon/
|
||||
|
||||
具体可以参考VRM4U_PostToon.umap,设置步骤如下:
|
||||
1. 在场景中放置一个SkeletalMeshActor。
|
||||
2. 在场景中放置一个BP_PoseCopyToon,并且设置Target Actor为上一步放置的SkeletalMeshActor,并调整参数。
|
||||
|
||||
#### PostToon原理
|
||||
首先BP_PoseCopyToon挂载了5个组件(隐藏了2个非重要组件),SkeletalMesh为主要渲染,VrmPoseableMesh用于接受光照。
|
||||
- SkeletalMesh
|
||||
- VrmPoseableMesh
|
||||
- VrmPoseableMesh_translucent
|
||||
|
||||
1. 设置TargetMeshForLight(SkeletalMesh或者VrmPoseableMesh)
|
||||
2. BP_PoseCopyToon会在一开始遍历TargetMeshForLight的Materials,之后根据材质数目给VrmPoseableMesh添加对应数目的MI_BaseLight材质。(M_BaseLight与MToon材质大致一样,都使用MF_VrmMToonBase)
|
||||
3. 给VrmPoseableMesh_translucent执行上一步类似操作,但添加的材质为MI_PostToon。
|
||||
4. 如果SkeletalMesh中有使用M_VrmNone材质,那VrmPoseableMesh与VrmPoseableMesh_translucent的对应部分也会被设置成M_VrmNone。
|
||||
5. 隐藏Debug用的VrmPoseableMesh_translucent。
|
||||
6. 调用VrmPoseableMesh的VRMSetLightingChannelPrim()。设置了图元的LightingChannel。
|
||||
7. 调用VrmPoseableMesh_translucent的VRMSetPerBoneMotionBlur()。设置SkinnedMesh->bPerBoneMotionBlur,默认是true。
|
||||
|
||||

|
||||
|
||||
##### M_VrmMToonBaseOpaque、M_BaseLight、M_PostToon比较
|
||||
- M_BaseLight:有基础灯光计算(NoL)。**实际使用的是MI_BaseLight**所以会有一些参数改变:比如他是一个PBR材质,有调整过BaseColor、Metalic、Roughness。
|
||||
- M_ToonPost
|
||||
- M_VrmMToonBaseOpaque:实现了TAA透明效果
|
||||
|
||||
M_VrmMToonBaseOpaque
|
||||

|
||||
|
||||
M_BaseLight:用于渲染光照效果
|
||||

|
||||
|
||||
M_ToonPost:用于取得光照效果并且合成在一起。使用SceneColor节点取得上一个Material渲染的结果作为灯光结果(因为是透明材质,所以是在后面渲染的)。
|
||||

|
||||
|
||||
#### 使用Custom Material
|
||||
VRM4U可以使用自定义的材质系统代替MToon材质。
|
||||
|
||||
1. 根据M_VrmSimple的格式,创建自己的CustomMaterial。
|
||||
2. 创建DS_VRMCustom DataAsset并且填入对应的Custom Material Instance(改变Shader Model与Blend Mode)。
|
||||
3. 在操作面板WBP_VRMMaterial的MaterialSettings上,将材质模型设置成Custom,并且填入上面创建的DS_VRMCustom DataAsset。
|
||||
|
||||

|
||||

|
||||
|
||||
### 其他Hack技巧
|
||||
- Anti-ToneMapping:使用UE自带的函数抵消ToneMapping效果。
|
||||
- Exposure:使用EyeAdaptation节点控制亮度。
|
||||
|
||||

|
||||
|
||||
### 拍摄模式
|
||||
1. 使用BP_VrmModelActor。
|
||||
2. 视线跟踪 /VRM4U/Util/Actor/Misc/LookAtPoint放置TargetActor并设置目标模型。
|
||||
3. VRM4U的角色相机使用Pawn类实现:提供若干快捷操作方便用户得到最佳的镜头。https://ruyo.github.io/VRM4U/02_shortcut2/
|
||||
4. 使用MToonMaterialSystem调整阴影效果。
|
||||
5. 使用MorphControl控制角色表情(目标设置为BP_VrmModelActor)。
|
||||
|
||||
### Over Parse
|
||||
文档:https://ruyo.github.io/VRM4U/02_pers/
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
Actor位于Util\Actor\:
|
||||
- FOVCharacter
|
||||
- FOVCustom
|
||||
|
||||
FOVCustom使用了VrmCameraCheck组件,并且会在蓝图的Construction Script中绑定SetFovDistance()至CameraMove委托。主要的逻辑位于SetFovDistance()中,主要逻辑Tick()也会使用。
|
||||
1. 将FOVCustom Attach 到 TargetActor上。
|
||||
2. 寻找场景中所有MToonMaterialSystem,并设置FovScaleBias、Fov_distance、Fov_scale参数并应用。
|
||||
|
||||
FOVCharacter逻辑相似,核心逻辑在CustomEvent_1中,他会绑定给VrmCameraCheck组件的CameraMove委托。主要设置了**材质集**的FOV2Begin、FOV2Pow、FOV2Scale、FOV2ScaleLimit、FOV2FarCenter、FOV2FarPoint、MainCameraPosition、MainCameraDirection。相关逻辑位于材质中。
|
||||
|
||||

|
||||
|
||||
## 动画
|
||||
值得学习:
|
||||
- Runtime Retarget功能
|
||||
- Limited Animation(抽帧动画)
|
||||
- Control Rig与Morph Target控制器
|
||||
|
||||
实现动画节点:
|
||||
- VRM4U
|
||||
- AnimNode_VrmCopyHandBone
|
||||
- AnimNode_VrmModifyBoneDynamic
|
||||
- AnimNode_VrmModifyBoneList
|
||||
- AnimNode_VrmModifyHumanoidBone
|
||||
- AnimNode_VrmQuestHandBone
|
||||
- AnimNode_VrmRetargetFromMannequin
|
||||
- AnimNode_VrmSpringBone
|
||||
- AnimNode_VrmVMC
|
||||
- VRM4UImporter
|
||||
- AnimGraphNode_VrmCopyHandBone
|
||||
- AnimGraphNode_VrmModifyBoneDynamic
|
||||
- AnimGraphNode_VrmModifyBoneList
|
||||
- AnimGraphNode_VrmModifyHumanoidBone
|
||||
- AnimGraphNode_VrmQuestHandBone
|
||||
- AnimGraphNode_VrmRetargetFromMannequin
|
||||
- AnimGraphNode_VrmSpringBone
|
||||
- AnimGraphNode_VrmVMC
|
||||
|
||||
### BP_VrmPoseCopy
|
||||
挂载了以下5个组件,有一个子类BP_VrmPoseCopy_PostShadow。
|
||||
- VrmPoseableMesh
|
||||
- BP_VrmAnimControlComponent
|
||||
- BP_VrmUtilComponent
|
||||
- SkeletalMesh
|
||||
|
||||
有关抽帧效果,其关键函数在于BP_VrmAnimControlComponent或者BP_VrmPoseCopy的SetFrameLimite(),函数是一个无线循环函数。
|
||||
1. 判断是否开启EnableFrameLimit,不开启就结束了
|
||||
2. SetFrameLimite()中判断当前播放帧数是否达到nextToPlay变量的值,如果没有则等待;有则执行VrmPoseableMesh的VRMCopyPoseAndMorphFromSkeletalComponent()
|
||||
3. 更新nextToPlay变量并触发OnFrameLimitUpdate委托。
|
||||
4. 在更新完模型的Transform后,跳回第2步。
|
||||
|
||||
**改进思路**:在蓝图中这样实现会有很大的性能问题,使用c++会更好。而且这个逻辑完全可以使用动画蓝图实现,使用PoseCache,之后根据条件,输出更新Pose或是PoseCache。
|
||||
|
||||
UVrmPoseableMeshComponent继承自UPoseableMeshComponent。
|
||||
```c++
|
||||
void UVrmPoseableMeshComponent::VRMCopyPoseAndMorphFromSkeletalComponent(USkeletalMeshComponent* InComponentToCopy) {
|
||||
if (InComponentToCopy) {
|
||||
Super::CopyPoseFromSkeletalComponent(InComponentToCopy);
|
||||
|
||||
MorphTargetWeights = InComponentToCopy->MorphTargetWeights;
|
||||
ActiveMorphTargets = InComponentToCopy->ActiveMorphTargets;
|
||||
}
|
||||
}
|
||||
```
|
||||
CopyPoseFromSkeletalComponent()的主要逻辑就是判断骨骼是否一一对应,如果对应则使用移动构造函数对Target与Source骨骼数据进行交换;不对应就遍历复制骨骼数据。
|
||||
|
||||
#### Limited Animation 抽帧效果
|
||||
文档地址:https://ruyo.github.io/VRM4U/05_limitedanim/
|
||||
|
||||
使用步骤
|
||||
1. 在关卡上放置一个执行通常动画的骨架模型Actor。设置为“始终勾选姿势并刷新骨骼”以将动画移动到屏幕之外。
|
||||
2. 在关卡中放置一个BP_VrmPoseCopy Actor,并TargetActor设置为上一步中的骨骼模型。打开LimitedAnim,输入你要播放的帧率(12、24等,每秒的帧数)。
|
||||
|
||||
#### 运行时重定向
|
||||
文档:https://ruyo.github.io/VRM4U/03_gray/
|
||||
视频(UE5的实时重定向方案):https://www.bilibili.com/video/BV1HZ4y117Yk?spm_id_from=333.999.0.0
|
||||
|
||||
视频中的步骤:
|
||||
1. 给新导入的VRM4U模型添加重定向骨骼链数据。
|
||||
2. 新建IK Rig Asset并选择IK_Mannequin。(假设使用UE5小蓝人来重定向)
|
||||
3. 新建IK Retarget Asset,设置Source为IK_Mannequin的Rig,Target为上一步新建的Rig。
|
||||
4. 修改TPose=>APose。
|
||||
|
||||
5. 在角色类中的Mesh下面新建一个SkeletalMesh组件,并且选择新导入的VRM4U模型。创建一个动画蓝图Asset并指定(我感觉并不需要挂载新创建的SkeletalMesh)。
|
||||
6. 动画蓝图中使用RetargetPoseFromMesh节点。
|
||||
7. 将角色类中Mesh的VisibilityBasedAnimTickOption属性改为AlwaysTickPose=>AlwaysTickPoseAndRefreshBones。
|
||||
8. 隐藏新建的SkeletalMesh组件。
|
||||
|
||||
VRM4U的实时重定向步骤:
|
||||
1. 将BP_VrmMannequinRetarget放入场景中,之后设置Targetmannequin。
|
||||
2. 设置VRM Asset List Override。
|
||||
3. 最后点击GenerateRegargetPoseCopy即可。
|
||||
|
||||

|
||||
|
||||
BP_VrmMannequinRetarget使用了动画蓝图ABP_VRoidSimpleMannequinRetarget,里面使用了VrmRetargetFromMannequin节点。大致逻辑就是遍历所有的骨骼(humanoid_bone)之后进行重定向。本质上与UE5的方法一样,但UE5使用最新的骨骼链算法,无论是便捷与效果都更胜一筹。
|
||||
|
||||
## 其他工具与实现
|
||||
- 面部捕捉
|
||||
- VirtualMotionCapture(VMC) 身体捕捉 https://ruyo.github.io/VRM4U/08_vmc/
|
||||
|
||||
VMC入门资料:
|
||||
1. UE VR Track介绍(视频中使用了SteamVR控制器手柄):https://www.bilibili.com/video/BV1kS4y167SK?spm_id_from=333.337.search-card.all.click
|
||||
2. UE VRN4U VMC功能讲解:https://www.bilibili.com/video/BV1ub4y1Y74K?spm_id_from=333.337.search-card.all.click
|
||||
3. 捕捉数据软件介绍(Up推荐VseeFace,免费、效果好):https://www.bilibili.com/video/BV1DK411u75k?spm_id_from=333.999.0.0
|
||||
4. Vseeface和OBS的设置:https://www.bilibili.com/video/BV16K4y1H7Cb?spm_id_from=333.999.0.0
|
||||
5. VSeeFace配合LeapMotion:https://www.bilibili.com/video/BV1KA41137os?spm_id_from=333.337.search-card.all.click
|
||||
6. 全身动作捕捉软件ThreeDPoseTracker:https://www.bilibili.com/video/BV1vy4y157An?spm_id_from=333.999.0.0
|
||||
|
||||
## 其他模块文件
|
||||
- VRM4U
|
||||
- VrmUtil:导入选项、GetXXX工具函数、骨骼转换映射表
|
||||
- VrmUtilImage:图片处理函数
|
||||
- VRM4UImporter
|
||||
- VRM4UDetailCustomize:针对FVRMRetargetSrcAnimSequence的自定义编辑器
|
||||
- VrmAssetListThumbnailRenderer:Asset缩略图渲染控制
|
||||
|
||||
Util中的工具
|
||||
Actor
|
||||
- latest:作者研发用的文件夹,里面的东西会引起打包出问题,建议在打包的时候删掉。
|
||||
|
||||
# VRM4U 功能列表翻译
|
||||
- Asset输出支持日语名称
|
||||
- 显示许可证与文件的Meta信息
|
||||
- bvh 支持
|
||||
|
||||
## 骨骼动画
|
||||
- MorphControlActor。支持VRoid模型的Morph Target,并且名称与原始数据相同
|
||||
- 视线跟随Actor
|
||||
- 增加动画节点:VRMSpringBone
|
||||
- 默认重定向是 A-pose。启用以更改为常规 T 姿势作为选项
|
||||
- 增加套用ALS功能(添加IKBone,复制VirtualBone,创建骨骼名称为UE4 Mannequin)
|
||||
- 可以从其他骨骼Asset上复制Socket
|
||||
- 实现了 WindActor
|
||||
- LiveLink 面部捕捉支持
|
||||
- 角色相机添加了呼吸选项、更改角色相机的焦点位置功能。
|
||||
- 对应ControlRig不能很好应用的模型(PMX中骨骼层次不同的模型,使用半标准骨骼的模型)
|
||||
- 添加了一个工具来控制Sequence中的面部动画
|
||||
|
||||
## MToon材质
|
||||
默认材质设置为 Unlit,除此之外还是先PBR与SSS模式。
|
||||
|
||||
- 实现 MPC 材质集来调整材质参数
|
||||
- 自己实现ShaderMap以实现Unlit材质模型的投射阴影;可以设置第二个阴影颜色;修复可能无法绘制阴影的问题(没有MaterialSystem可以正常绘制阴影)
|
||||
- EyeAdaptation (对轮廓线部分使用了特殊的眼部自适应调整)
|
||||
- 实现 MatCap 上反射阴影
|
||||
- RimLight
|
||||
- UV Scroll
|
||||
- 启用生成 AO 阴影模型
|
||||
- MToonMaterialSystem 添加了 SSGI 切换选项
|
||||
- 启用以引用 LightRig 中的 DirectionalLightComponent(支持 SunSkyActor)。通过定向光到 Light Rig 的俯仰角添加了亮度校正选项。
|
||||
- 添加了一个参数来更改 FilmicTonemapper 逆变换的强度。从 MToonMaterialSystem 更改。
|
||||
- PostShadow 新增卡通功能
|
||||
- 注)M MoonLit 会根据场景稍微变暗。我决定不参考 SkyLight 来减少负载。要回到过去,请从材质中打开 bUseSkyLightDirect。
|
||||
- Rim light 和 matcap 不再受正常校正的影响
|
||||
|
||||
## VR相关
|
||||
- 在AnimBP中增加MotionController 和 Leap Motion 跟踪
|
||||
|
||||
## 官方上的支持功能
|
||||
您可以导入 VRM 文件
|
||||
- 动画片
|
||||
- 您可以轻松地重新定位。生成 A-pose / T-pose 和 BoneMap。
|
||||
- 面部动画(Morphtarget / BlendShapeGroup)可用。
|
||||
- 您可以为摆动骨骼选择 VRMSpringBone 或 PhysicsAsset。
|
||||
- 有一个通用的控制装置和一个用于操作的 UMG。
|
||||
- 您可以从外部应用程序接收动作捕捉数据。它支持 VMC 协议。
|
||||
- 您可以在运行时从 UEMannequin 重定向到 VRM 模型。
|
||||
- UE5:可以自动生成 IKRetargeter 资产。您可以从 Epic Skeleton 简化重定向过程。
|
||||
- 材料
|
||||
- 再现 M Moon 的素材。阴影颜色规格、轮廓颜色/粗细调整、MatCap 等都适用。
|
||||
- 您可以根据 PBR 背景切换和调整绘图模式。
|
||||
- 现有的后置滤镜和光线追踪可以同时使用。
|
||||
- 移动的
|
||||
- 配合BoneMap缩减功能,不用担心骨骼数量即可显示。
|
||||
- 您可以切换绘图质量。它支持低规格。
|
||||
- 虚拟现实/增强现实
|
||||
- 绘图同时支持 Forward / Deferred。
|
||||
- 由于它是由简单的函数组成的,所以它不会崩溃。
|
||||
- 运行时负载
|
||||
- 任何用户的 VRM 文件都可以从打包的 EXE 文件中读取。
|
||||
- 动画可以在运行时重新定位。
|
||||
- UE兼容性好
|
||||
- 导入后,它将是一个标准的骨架网格体资源。
|
||||
- 对应的UE版本是4.20~4.27,5.0(截至2022/04)很容易支持最新版本。
|
||||
- 依次支持VRM1.0β
|
||||
- VRM1.0数据在一定程度上可以导入。
|
||||
- 您可以保留本地轴或在导入时选择它。
|
||||
- 对应M Moon的新功能。
|
||||
- 实验实现
|
235
03-UnrealEngine/卡通渲染相关资料/VRM4U/VRM4U与其他MMD插件笔记.md
Normal file
235
03-UnrealEngine/卡通渲染相关资料/VRM4U/VRM4U与其他MMD插件笔记.md
Normal file
@@ -0,0 +1,235 @@
|
||||
# Assimp入门
|
||||
https://assimp-docs.readthedocs.io/en/latest/about/introduction.html
|
||||
|
||||
# 其他卡通渲染推主
|
||||
https://twitter.com/rukikuri
|
||||
|
||||
# VRoid商城地址
|
||||
https://booth.pm/zh-cn
|
||||
|
||||
# VRM格式地址
|
||||
https://vrm.dev/
|
||||
## Unity3d导入插件
|
||||
导入https://github.com/vrm-c/UniVRM/releases
|
||||
导出Fbx插件:https://github.com/KellanHiggins/UnityFBXExporter
|
||||
|
||||
# VRM4U
|
||||
https://github.com/ruyo/UnrealEngine_VRM4UPlugin
|
||||
https://github.com/ruyo/VRM4U
|
||||
|
||||
## 渲染参考
|
||||
原文(上/下):
|
||||
https://qiita.com/ruyo/items/ec082d81dea3033e1500
|
||||
https://qiita.com/ruyo/items/71a3f2f694d2853b3f1e
|
||||
|
||||
光照:
|
||||
https://qiita.com/ruyo/items/28255f26725a6b6bd475
|
||||
|
||||
### 其他参考
|
||||
MToon:https://dwango.github.io/vrm/univrm/shaders/mtoon/
|
||||
Ue4中的卡通渲染:https://qiita.com/com04/items/a7895160df8d854fe924
|
||||
|
||||
## 曝光与ToneMapper问题解决
|
||||
ToneMapper与曝光会影响贴图的亮度,所以需要尝试干掉或者抵消掉他们的影响。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
手动去除了Gammer矫正以及ToneMapper取消,并且使用 人眼适应节点抵消曝光效果。
|
||||
|
||||
除此之外,还使用RayTracingQualitySwitchReplace节点与人眼适应节点做了Raytracing质量调整。(手机端为1)
|
||||
|
||||
## 阴影
|
||||

|
||||
使用了SceneCaptureComponent2D。
|
||||
在Orthographic(平行投影*)中,将CaptureSource设置为 "SceneDepth in R",TexrureTarget格式设置为 "RTF R32f"。
|
||||
这将导致深度在UnrealUnit中线性地写入缓冲区(默认为1=1cm)。 别紧张。
|
||||
|
||||

|
||||
要用Shadowmap投出一个阴影,你只需要知道从光线中看到的深度,以及投影矩阵的矩阵。
|
||||
我们会把这些东西找回来,传给材料。
|
||||
https://www.shibuya24.info/entry/shadowmap
|
||||
|
||||
官方的SceneCaptureComponent2D会做很多无用的渲染工作,推荐自己重新写一个,以减少不必要的消耗。
|
||||
|
||||
### 蓝图传递逆矩阵
|
||||
|
||||
在蓝图中国计算出SceneCaptureComponent2D的逆矩阵后,将其传入材质中。
|
||||

|
||||
|
||||
### 材质内
|
||||
在一个自定义节点中,将参数打包成float4x4,并进行计算。
|
||||
现在你可以从世界坐标中参考相应的影子图。
|
||||
在材料函数中,结果返回0-1。
|
||||

|
||||
|
||||
|
||||
## 描边
|
||||
在具体节点方面,
|
||||
投影结果 → TransformPosition节点转换为ViewSpace的转换结果
|
||||
Pixel Thickness → VectorLength的值。
|
||||
乘以倒数→部分除以VectorLength的值,再乘以反数→部分除以VectorLength的值。
|
||||
使用ScreenResolusion的原因是为了确保不影响窗口大小和宽高比。
|
||||
我不确定我是否准备好了......
|
||||
在MToon再现方面,我们需要能够选择 "恒定厚度模式 "和 "世界坐标参考模式"。
|
||||

|
||||
|
||||
http://historia.co.jp/archives/5587/
|
||||
|
||||
使用PoseableMeshComponent,与CopyPoseFromSkeletalComponent函数实现外描边。到时候比对一下这个方法与我的重定义图元方法的效率。
|
||||
|
||||
## 轮廓光
|
||||
使用MaterialCapture材质制作轮廓光。
|
||||
|
||||
## 光照模式
|
||||
默认的MToon为无光照模式(自定义光照效果)
|
||||
|
||||
其余的工作将通过增加材料参数来体现数值。 我就不说了,因为这样做是多余的。 (我没有评论了...)
|
||||
也可以应用ShadingShadingShift。
|
||||
|
||||
主源和GI可以通过在自定义节点中写出以下内容来获得。
|
||||
如果你用GetSkySHDiffuseSimple或GetSkySHDiffuse搜索usf文件,会有帮助。
|
||||
不要忘了在最终输出的SkyLight(ResolvedView.SkyLightColor.rgb)中乘以颜色。
|
||||
|
||||
```
|
||||
//主光源.usf
|
||||
return ResolvedView.DirectionalLightColor;
|
||||
```
|
||||
```
|
||||
//GIの影響.usf,然而这个是天光
|
||||
#if SIMPLE_FORWARD_SHADING
|
||||
float4 NormalVector = float4(Normal, 1);
|
||||
|
||||
float3 Intermediate0;
|
||||
Intermediate0.x = dot(View.SkyIrradianceEnvironmentMap[0], NormalVector);
|
||||
Intermediate0.y = dot(View.SkyIrradianceEnvironmentMap[1], NormalVector);
|
||||
Intermediate0.z = dot(View.SkyIrradianceEnvironmentMap[2], NormalVector);
|
||||
|
||||
// max to not get negative colors
|
||||
return max(0, Intermediate0) * ResolvedView.SkyLightColor.rgb;
|
||||
#else
|
||||
float4 NormalVector = float4(Normal, 1);
|
||||
|
||||
float3 Intermediate0, Intermediate1, Intermediate2;
|
||||
Intermediate0.x = dot(View.SkyIrradianceEnvironmentMap[0], NormalVector);
|
||||
Intermediate0.y = dot(View.SkyIrradianceEnvironmentMap[1], NormalVector);
|
||||
Intermediate0.z = dot(View.SkyIrradianceEnvironmentMap[2], NormalVector);
|
||||
|
||||
float4 vB = NormalVector.xyzz * NormalVector.yzzx;
|
||||
Intermediate1.x = dot(View.SkyIrradianceEnvironmentMap[3], vB);
|
||||
Intermediate1.y = dot(View.SkyIrradianceEnvironmentMap[4], vB);
|
||||
Intermediate1.z = dot(View.SkyIrradianceEnvironmentMap[5], vB);
|
||||
|
||||
float vC = NormalVector.x * NormalVector.x - NormalVector.y * NormalVector.y;
|
||||
Intermediate2 = View.SkyIrradianceEnvironmentMap[6].xyz * vC;
|
||||
|
||||
// max to not get negative colors
|
||||
return max(0, Intermediate0 + Intermediate1 + Intermediate2) * ResolvedView.SkyLightColor.rgb;
|
||||
#endif
|
||||
```
|
||||
## 其他光照模型
|
||||
|
||||
|
||||
## Runtime载入思路
|
||||
```
|
||||
NewObject<USkeletalMesh>()
|
||||
|
||||
SkeletalMesh->GetResourceForRendering()
|
||||
->LODRenderData[0]
|
||||
• StaticVertexBuffers
|
||||
顶点信息。 最起码,这将填补。
|
||||
• MultiSizeIndexContainer
|
||||
- 绘图时的顶点信息。 如果你要运行时加载的话,也要埋下这一点。
|
||||
- 从GameThread停止重写的成员从RenderThread重写。
|
||||
• SkeletalMesh->GetImportedModel()->LODModels[0].Sections;
|
||||
- 网格和材料信息。 我也要把这个埋了。
|
||||
• SkeletalMesh->RegisterMorphTarget()
|
||||
- 混合形状信息。 我也要把这个埋了。
|
||||
- UE4的顶点权重为uint8,所以有一个分数。 最后,正常化。
|
||||
```
|
||||
### VRM(glTF)转换时需要注意
|
||||
改变坐标系。
|
||||
- 转换为Z-up
|
||||
- 转换比例尺为虚幻单位(x100)
|
||||
- 支持多种根骨。
|
||||
- 去除不必要的骨头
|
||||
- 如果你不想移除的话,可以添加一个假根骨(root)。
|
||||
|
||||
字符与编码转换
|
||||
|
||||
### 物理资源导入
|
||||
```
|
||||
• bs = NewObject<USkeletalBodySetup>();
|
||||
• ct = NewObject<UPhysicsConstraintTemplate>();
|
||||
• physicsAsset->SkeletalBodySetups.Add(bs);
|
||||
• physicsAsset->ConstraintSetup.Add(ct);
|
||||
```
|
||||
我想让我的骨头不至于发狂。
|
||||
- 物理学变得很粗糙。 当碰撞被猛烈地掩埋时,就会引起注意。
|
||||
- 增加计算次数,会在一定程度上改善它。
|
||||
- 我想保持我想要的形状。
|
||||
- 我的头发因为地心引力而垂下来。
|
||||
- 可移动的范围可以指定,但移动不规范。 杂项:
|
||||
|
||||
VRMSpringBone是Unity的一个实现,目前已经发布了VRMSpringBone。
|
||||
- 理论上可以移植到UE4上。
|
||||
- 源码(VRMSpringBone.cs)只有329行。
|
||||
- 我不知道这样可以吗?
|
||||
|
||||
于是作者移植了VRMSpringBone。
|
||||
|
||||
我现在可以看VRM了。
|
||||
- 你现在可以导入/运行时加载
|
||||
- 我现在也能读出混合形状和碰撞了!
|
||||
- 现在,你可以重新创建VRMSpringBone
|
||||
- 你现在可以干扰UE4的碰撞了。
|
||||
|
||||
StaticMesh的运行时加载也是可能的。
|
||||
- 我可以创建一个自定义的摇摆骨质节点。
|
||||
- 对于那些对PhysicalAsset和AnimDynamics节点的行为不满意的人。
|
||||
- 你为什么不尝试着去做一个呢?
|
||||
|
||||
## 动画部分
|
||||
要想导入动画,可以使用
|
||||
|
||||
- (一) 骨架通用化
|
||||
- (二) 重新定位动画资产。
|
||||
|
||||
但两者都是编辑器功能,无法在runtime中使用。
|
||||
|
||||
### 使用AnimBP实现运行时重定向
|
||||

|
||||
将复制资产设置为AnimInstance,你就可以走了。
|
||||
|
||||
复制源头网状物。
|
||||
设置在编辑器中创建的AnimBP
|
||||
|
||||
要复制的网状物。
|
||||
设置VrmAnimInstanceCopy
|
||||
|
||||
# IM4U
|
||||
https://github.com/bm9/IM4U
|
||||
https://github.com/bm9/UnrealEngine_IM4UPlugin
|
||||
|
||||
# Blender和pmx2fbx
|
||||
|
||||
# Muro_CG
|
||||
使用Unity卡通着色的3D动画表达
|
||||
https://qiita.com/MuRo_CG/items/c417ef6d6cbeed3dd42b
|
||||
|
||||
Unity トゥーンシェーディングを使った3Dアニメ表現
|
||||
https://qiita.com/MuRo_CG/items/c417ef6d6cbeed3dd42b
|
||||
https://github.com/unity3d-jp/unitychan-crs
|
||||
|
||||
https://ja.whotwi.com/MuRo_CG/tweets/popular
|
||||
https://togetter.com/li/1438719
|
||||
|
||||
# Unity_Japan
|
||||
【Unite 2017 Tokyo】VR MAGIC! ~キャラクターに命を吹き込んだこの4年間の記録~
|
||||
https://www.slideshare.net/Unite2017Tokyo/unite-2017-tokyovr-magic4
|
||||
|
||||
https://www.youtube.com/watch?v=N4_x9w7wNNY&feature=youtu.be
|
||||
|
||||
# 米哈游
|
||||
http://www.uniteseoul.com/2018/download_files/T1_0503_2.pdf
|
83
03-UnrealEngine/卡通渲染相关资料/VRM4U/VRM4U中资源带入逻辑与Assimp的使用.md
Normal file
83
03-UnrealEngine/卡通渲染相关资料/VRM4U/VRM4U中资源带入逻辑与Assimp的使用.md
Normal file
@@ -0,0 +1,83 @@
|
||||
## 自定义Asset
|
||||
https://zhuanlan.zhihu.com/p/77812246
|
||||
|
||||
- FAssetTypeActions_Base:是对已经创建好的资源操作函数的封装。这个资源在内容浏览器中的颜色,类型名字,显示的缩略图。
|
||||
- UFactory:这个是创建自定义类型文件的入口。以及创建资源的二级菜单。
|
||||
- UBlueprint: 创建继承类,来实现一个新的视图编辑窗口
|
||||
- UXXXX : Public Object : 这里面存储我们要编辑的数据
|
||||
|
||||
VRM中对应类为:
|
||||
- FAssetTypeActions_VrmAssetList、UVrmAssetListThumbnailRenderer
|
||||
- UVRM4UImporterFactory
|
||||
- UVrmAssetListObject
|
||||
|
||||
## Assimp
|
||||
资源工厂类中:UVRM4UImporterFactory::FactoryCreateBinary
|
||||
```
|
||||
{
|
||||
const UVrmRuntimeSettings* Settings = GetDefault<UVrmRuntimeSettings>();
|
||||
|
||||
{
|
||||
FSoftObjectPath r = Settings->AssetListObject; //(TEXT("/VRM4U/VrmObjectListBP.VrmObjectListBP"));
|
||||
UObject *u = r.TryLoad();
|
||||
if (u) {
|
||||
if (Cast<UBlueprint>(u)) {
|
||||
c = (UClass*)(Cast<UBlueprint>(u)->GeneratedClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (c == nullptr) {
|
||||
FSoftObjectPath r(TEXT("/VRM4U/VrmAssetListObjectBP.VrmAssetListObjectBP"));
|
||||
UObject *u = r.TryLoad();
|
||||
if (u) {
|
||||
c = (UClass*)(Cast<UBlueprint>(u)->GeneratedClass);
|
||||
}
|
||||
}
|
||||
|
||||
if (c == nullptr) {
|
||||
c = UVrmAssetListObject::StaticClass();
|
||||
}
|
||||
|
||||
m = NewObject<UVrmAssetListObject>((UObject*)GetTransientPackage(), c.Get());
|
||||
}
|
||||
|
||||
UVrmAssetListObject* mret = nullptr;
|
||||
if (m) {
|
||||
//auto a = NewObject<UVrmAssetListObject>(MatClass.Object, NAME_None, RF_Transactional);
|
||||
//MatClass.Object;
|
||||
//ULoaderBPFunctionLibrary::LoadVRMFile(nullptr, fullFileName);
|
||||
|
||||
GWarn->BeginSlowTask( NSLOCTEXT("UnrealEd", "ImportVRM", "Importing VRM"), true );
|
||||
|
||||
int ret = true;
|
||||
auto &g = VRMConverter::Options::Get();
|
||||
g.SetVrmOption(ImportUI->GenerateOptionData());
|
||||
|
||||
ULoaderBPFunctionLibrary::SetImportMode(true, Cast<UPackage>(InParent));
|
||||
{
|
||||
ret = ULoaderBPFunctionLibrary::LoadVRMFileLocal(m.Get(), mret, fullFileName);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
蓝图函数库中:bool ULoaderBPFunctionLibrary::LoadVRMFileFromMemory()。
|
||||
```
|
||||
Assimp::Importer mImporter;
|
||||
const aiScene* mScenePtr = nullptr; // delete by Assimp::Importer::~Importer
|
||||
|
||||
mScenePtr = mImporter.ReadFileFromMemory(pFileDataData, dataSize,
|
||||
aiProcess_Triangulate | aiProcess_MakeLeftHanded | aiProcess_CalcTangentSpace | aiProcess_GenSmoothNormals | aiProcess_OptimizeMeshes,
|
||||
e.c_str());
|
||||
|
||||
if (mScenePtr == nullptr) {
|
||||
std::string file;
|
||||
file = utf_16_to_shift_jis(*filepath);
|
||||
|
||||
mScenePtr = mImporter.ReadFile(file, aiProcess_Triangulate | aiProcess_MakeLeftHanded | aiProcess_CalcTangentSpace | aiProcess_GenSmoothNormals | aiProcess_OptimizeMeshes);
|
||||
}
|
||||
```
|
271
03-UnrealEngine/卡通渲染相关资料/VRM4U/VRM4U卡通渲染实现.md
Normal file
271
03-UnrealEngine/卡通渲染相关资料/VRM4U/VRM4U卡通渲染实现.md
Normal file
@@ -0,0 +1,271 @@
|
||||
# Assimp入门
|
||||
https://assimp-docs.readthedocs.io/en/latest/about/introduction.html
|
||||
|
||||
# 其他卡通渲染推主
|
||||
https://twitter.com/rukikuri
|
||||
|
||||
# VRM格式地址
|
||||
https://vrm.dev/
|
||||
## Unity3d导入插件
|
||||
导入https://github.com/vrm-c/UniVRM/releases
|
||||
导出Fbx插件:https://github.com/KellanHiggins/UnityFBXExporter
|
||||
|
||||
# VRM4U
|
||||
https://github.com/ruyo/UnrealEngine_VRM4UPlugin
|
||||
https://github.com/ruyo/VRM4U
|
||||
|
||||
## 渲染原理介绍
|
||||
原文(上/下):
|
||||
https://qiita.com/ruyo/items/ec082d81dea3033e1500
|
||||
https://qiita.com/ruyo/items/71a3f2f694d2853b3f1e
|
||||
|
||||
MToon:https://dwango.github.io/vrm/univrm/shaders/mtoon/
|
||||
Ue4中的卡通渲染:https://qiita.com/com04/items/a7895160df8d854fe924
|
||||
|
||||
材质:
|
||||
https://qiita.com/ruyo/items/ddf727e9fa81a24070fb (细节都在这里)
|
||||
https://qiita.com/ruyo/items/28255f26725a6b6bd475
|
||||
|
||||
Unlit
|
||||

|
||||

|
||||
|
||||
PBR
|
||||

|
||||

|
||||
|
||||
SSS
|
||||

|
||||

|
||||
|
||||
## 灯光调整
|
||||

|
||||
|
||||
2种方法:
|
||||
- 开启天光的LowerHemisphereIsSolidColor,但这样会场景效果变得有些奇怪
|
||||
- 添加一个从下往上的方向光
|
||||
|
||||
使用这个方法 以及 PBR材质中的BaseColor与Emissive进行插值来接近HalfLambda的效果。
|
||||
|
||||
## 调整面部法线来柔化面部阴影
|
||||

|
||||

|
||||
|
||||
## 曝光与ToneMapper问题解决
|
||||
ToneMapper与曝光会影响贴图的亮度,所以需要尝试干掉或者抵消掉他们的影响。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
手动去除了Gammer矫正以及ToneMapper取消,并且使用 人眼适应节点抵消曝光效果。
|
||||
|
||||
除此之外,还使用RayTracingQualitySwitchReplace节点与人眼适应节点做了Raytracing质量调整。(手机端为1)
|
||||
|
||||
## 阴影
|
||||

|
||||
使用了SceneCaptureComponent2D。
|
||||
在Orthographic(平行投影*)中,将CaptureSource设置为 "SceneDepth in R",TexrureTarget格式设置为 "RTF R32f"。
|
||||
这将导致深度在UnrealUnit中线性地写入缓冲区(默认为1=1cm)。 别紧张。
|
||||
|
||||

|
||||
要用Shadowmap投出一个阴影,你只需要知道从光线中看到的深度,以及投影矩阵的矩阵。
|
||||
我们会把这些东西找回来,传给材料。
|
||||
https://www.shibuya24.info/entry/shadowmap
|
||||
|
||||
官方的SceneCaptureComponent2D会做很多无用的渲染工作,推荐自己重新写一个,以减少不必要的消耗。
|
||||
|
||||
### 蓝图传递逆矩阵
|
||||
|
||||
在蓝图中国计算出SceneCaptureComponent2D的逆矩阵后,将其传入材质中。
|
||||

|
||||
|
||||
### 材质内
|
||||
在一个自定义节点中,将参数打包成float4x4,并进行计算。
|
||||
现在你可以从世界坐标中参考相应的影子图。
|
||||
在材料函数中,结果返回0-1。
|
||||

|
||||
|
||||
|
||||
## 描边
|
||||
在具体节点方面,
|
||||
投影结果 → TransformPosition节点转换为ViewSpace的转换结果
|
||||
Pixel Thickness → VectorLength的值。
|
||||
乘以倒数→部分除以VectorLength的值,再乘以反数→部分除以VectorLength的值。
|
||||
使用ScreenResolusion的原因是为了确保不影响窗口大小和宽高比。
|
||||
我不确定我是否准备好了......
|
||||
在MToon再现方面,我们需要能够选择 "恒定厚度模式 "和 "世界坐标参考模式"。
|
||||

|
||||
|
||||
http://historia.co.jp/archives/5587/
|
||||
|
||||
使用PoseableMeshComponent,与CopyPoseFromSkeletalComponent函数实现外描边。到时候比对一下这个方法与我的重定义图元方法的效率。
|
||||
|
||||
## 轮廓光
|
||||
使用MaterialCapture材质制作轮廓光。
|
||||
|
||||
## 光照模式
|
||||
默认的MToon为无光照模式(自定义光照效果)
|
||||
|
||||
其余的工作将通过增加材料参数来体现数值。 我就不说了,因为这样做是多余的。 (我没有评论了...)
|
||||
也可以应用ShadingShadingShift。
|
||||
|
||||
主源和GI可以通过在自定义节点中写出以下内容来获得。
|
||||
如果你用GetSkySHDiffuseSimple或GetSkySHDiffuse搜索usf文件,会有帮助。
|
||||
不要忘了在最终输出的SkyLight(ResolvedView.SkyLightColor.rgb)中乘以颜色。
|
||||
|
||||
```
|
||||
//主光源.usf
|
||||
return ResolvedView.DirectionalLightColor;
|
||||
```
|
||||
```
|
||||
//GIの影響.usf,然而这个是天光
|
||||
#if SIMPLE_FORWARD_SHADING
|
||||
float4 NormalVector = float4(Normal, 1);
|
||||
|
||||
float3 Intermediate0;
|
||||
Intermediate0.x = dot(View.SkyIrradianceEnvironmentMap[0], NormalVector);
|
||||
Intermediate0.y = dot(View.SkyIrradianceEnvironmentMap[1], NormalVector);
|
||||
Intermediate0.z = dot(View.SkyIrradianceEnvironmentMap[2], NormalVector);
|
||||
|
||||
// max to not get negative colors
|
||||
return max(0, Intermediate0) * ResolvedView.SkyLightColor.rgb;
|
||||
#else
|
||||
float4 NormalVector = float4(Normal, 1);
|
||||
|
||||
float3 Intermediate0, Intermediate1, Intermediate2;
|
||||
Intermediate0.x = dot(View.SkyIrradianceEnvironmentMap[0], NormalVector);
|
||||
Intermediate0.y = dot(View.SkyIrradianceEnvironmentMap[1], NormalVector);
|
||||
Intermediate0.z = dot(View.SkyIrradianceEnvironmentMap[2], NormalVector);
|
||||
|
||||
float4 vB = NormalVector.xyzz * NormalVector.yzzx;
|
||||
Intermediate1.x = dot(View.SkyIrradianceEnvironmentMap[3], vB);
|
||||
Intermediate1.y = dot(View.SkyIrradianceEnvironmentMap[4], vB);
|
||||
Intermediate1.z = dot(View.SkyIrradianceEnvironmentMap[5], vB);
|
||||
|
||||
float vC = NormalVector.x * NormalVector.x - NormalVector.y * NormalVector.y;
|
||||
Intermediate2 = View.SkyIrradianceEnvironmentMap[6].xyz * vC;
|
||||
|
||||
// max to not get negative colors
|
||||
return max(0, Intermediate0 + Intermediate1 + Intermediate2) * ResolvedView.SkyLightColor.rgb;
|
||||
#endif
|
||||
```
|
||||
## 其他光照模型
|
||||
|
||||
|
||||
## Runtime载入思路
|
||||
```
|
||||
NewObject<USkeletalMesh>()
|
||||
|
||||
SkeletalMesh->GetResourceForRendering()
|
||||
->LODRenderData[0]
|
||||
• StaticVertexBuffers
|
||||
顶点信息。 最起码,这将填补。
|
||||
• MultiSizeIndexContainer
|
||||
- 绘图时的顶点信息。 如果你要运行时加载的话,也要埋下这一点。
|
||||
- 从GameThread停止重写的成员从RenderThread重写。
|
||||
• SkeletalMesh->GetImportedModel()->LODModels[0].Sections;
|
||||
- 网格和材料信息。 我也要把这个埋了。
|
||||
• SkeletalMesh->RegisterMorphTarget()
|
||||
- 混合形状信息。 我也要把这个埋了。
|
||||
- UE4的顶点权重为uint8,所以有一个分数。 最后,正常化。
|
||||
```
|
||||
### VRM(glTF)转换时需要注意
|
||||
改变坐标系。
|
||||
- 转换为Z-up
|
||||
- 转换比例尺为虚幻单位(x100)
|
||||
- 支持多种根骨。
|
||||
- 去除不必要的骨头
|
||||
- 如果你不想移除的话,可以添加一个假根骨(root)。
|
||||
|
||||
字符与编码转换
|
||||
|
||||
### 物理资源导入
|
||||
```
|
||||
• bs = NewObject<USkeletalBodySetup>();
|
||||
• ct = NewObject<UPhysicsConstraintTemplate>();
|
||||
• physicsAsset->SkeletalBodySetups.Add(bs);
|
||||
• physicsAsset->ConstraintSetup.Add(ct);
|
||||
```
|
||||
我想让我的骨头不至于发狂。
|
||||
- 物理学变得很粗糙。 当碰撞被猛烈地掩埋时,就会引起注意。
|
||||
- 增加计算次数,会在一定程度上改善它。
|
||||
- 我想保持我想要的形状。
|
||||
- 我的头发因为地心引力而垂下来。
|
||||
- 可移动的范围可以指定,但移动不规范。 杂项:
|
||||
|
||||
VRMSpringBone是Unity的一个实现,目前已经发布了VRMSpringBone。
|
||||
- 理论上可以移植到UE4上。
|
||||
- 源码(VRMSpringBone.cs)只有329行。
|
||||
- 我不知道这样可以吗?
|
||||
|
||||
于是作者移植了VRMSpringBone。
|
||||
|
||||
我现在可以看VRM了。
|
||||
- 你现在可以导入/运行时加载
|
||||
- 我现在也能读出混合形状和碰撞了!
|
||||
- 现在,你可以重新创建VRMSpringBone
|
||||
- 你现在可以干扰UE4的碰撞了。
|
||||
|
||||
StaticMesh的运行时加载也是可能的。
|
||||
- 我可以创建一个自定义的摇摆骨质节点。
|
||||
- 对于那些对PhysicalAsset和AnimDynamics节点的行为不满意的人。
|
||||
- 你为什么不尝试着去做一个呢?
|
||||
|
||||
## 动画部分
|
||||
要想导入动画,可以使用
|
||||
|
||||
- (一) 骨架通用化
|
||||
- (二) 重新定位动画资产。
|
||||
|
||||
但两者都是编辑器功能,无法在runtime中使用。
|
||||
|
||||
### 使用AnimBP实现运行时重定向
|
||||

|
||||
将复制资产设置为AnimInstance,你就可以走了。
|
||||
|
||||
复制源头网状物。
|
||||
设置在编辑器中创建的AnimBP
|
||||
|
||||
要复制的网状物。
|
||||
设置VrmAnimInstanceCopy
|
||||
|
||||
# IM4U
|
||||
https://github.com/bm9/IM4U
|
||||
https://github.com/bm9/UnrealEngine_IM4UPlugin
|
||||
|
||||
# Blender和pmx2fbx
|
||||
|
||||
## U3d日本分部分项
|
||||
|
||||
『崩壊3rd』開発者が語るアニメ風レンダリングの極意
|
||||
ユニティちゃんトゥーンシェーダー2.0使いこなしスペシャル ~こだわりの活用法を紹介します!~
|
||||
|
||||
〈七つの大罪〉をゲームで! 高品質グラフィックを具現化するための技法と開発最適化のご紹介 by Jaeseong Ryu 他
|
||||
|
||||
|
||||
# Muro_CG
|
||||
使用Unity卡通着色的3D动画表达
|
||||
https://qiita.com/MuRo_CG/items/c417ef6d6cbeed3dd42b
|
||||
|
||||
Unity トゥーンシェーディングを使った3Dアニメ表現
|
||||
https://qiita.com/MuRo_CG/items/c417ef6d6cbeed3dd42b
|
||||
https://github.com/unity3d-jp/unitychan-crs
|
||||
|
||||
https://ja.whotwi.com/MuRo_CG/tweets/popular
|
||||
https://togetter.com/li/1438719
|
||||
|
||||
# Unity_Japan
|
||||
【Unite 2017 Tokyo】VR MAGIC! ~キャラクターに命を吹き込んだこの4年間の記録~
|
||||
https://www.slideshare.net/Unite2017Tokyo/unite-2017-tokyovr-magic4
|
||||
|
||||
https://www.youtube.com/watch?v=N4_x9w7wNNY&feature=youtu.be
|
||||
|
||||
# 米哈游
|
||||
http://www.uniteseoul.com/2018/download_files/T1_0503_2.pdf
|
||||
|
||||
|
||||
米哈游面部阴影实现:
|
||||
Improved Alpha-Tested Magnification for Vector Textures and Special Effects
|
||||
|
||||
https://steamcdn-a.akamaihd.net/apps/valve/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf
|
BIN
03-UnrealEngine/卡通渲染相关资料/VRM4U/VRM4U技术讲解.pdf
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/VRM4U/VRM4U技术讲解.pdf
Normal file
Binary file not shown.
445
03-UnrealEngine/卡通渲染相关资料/卡通渲染实现笔记.md
Normal file
445
03-UnrealEngine/卡通渲染相关资料/卡通渲染实现笔记.md
Normal file
@@ -0,0 +1,445 @@
|
||||
# 实用库
|
||||
`#include "/Engine/Private/Random.ush"`
|
||||
`#include "/Engine/Private/SobolRandom.ush"`
|
||||
|
||||
# 眼睛
|
||||
眼睛焦散:
|
||||
|
||||
## 凹模型
|
||||
直接贴图即可。
|
||||
折射
|
||||
模型高光
|
||||
|
||||
## 凸模型
|
||||
1. 使用视差贴图来控制瞳孔效果 + 折射模拟
|
||||
```
|
||||
float2 viewL = mul(viewW, (float3x2) worldInverse);
|
||||
float2 offset = height * viewL;
|
||||
offset.y = -offset.y;
|
||||
texcoord -= parallaxScale * offset;
|
||||
```
|
||||

|
||||
|
||||
2. Physically based refraction
|
||||
```c++
|
||||
// 角膜区域突起的模型
|
||||
// Alternatively, use a displacement map
|
||||
// height = max(-positionL.z – eyeIrisDepth, 0.0);
|
||||
|
||||
// 球形模型
|
||||
// Plot[Max[1.0 - 18.4 * r * r, 0.0], {r, 0, 0.3}]
|
||||
height = anteriorChamberDepth * saturate( 1.0 - 18.4 * radius * radius );
|
||||
|
||||
// refractedW
|
||||
float w = n * dot( normalW, viewW );
|
||||
float k = sqrt( 1.0 + ( w - n ) * ( w + n ) );
|
||||
float3 refractedW = ( w - k ) * normalW - n * viewW;
|
||||
|
||||
float cosAlpha = dot(frontNormalW, -refractedW);
|
||||
float dist = height / cosAlpha;
|
||||
float3 offsetW = dist * refractedW;
|
||||
float2 offsetL = mul(offsetW, (float3x2) worldInverse);
|
||||
texcoord += float2(mask, -mask) * offsetL;
|
||||
```
|
||||

|
||||
|
||||
代码中首先计算了height,即前房的高度,PPT中height有两种计算方式,分别对应两种眼睛的模型结构,对应的结构写在注释中了。
|
||||
然后计算了refracted,这个是rtr中快速拟合的计算方法,n是空气与介质折射率的比值,关于refracted的推论可以参考:
|
||||
YivanLee:虚幻4渲染编程(人物篇)【第三卷:Human Eye Rendering】
|
||||
[129 赞同 · 12 评论文章](https://zhuanlan.zhihu.com/p/151786832)
|
||||
|
||||
最后一段,先通过frontNormalW与refractedW的点积计算出α角的cos值(上图中的α应该是标识错误,α是-refractedW与frontNormalW的夹角)。然后已知height,通过比值可以计算出refractedW的模长dist。offsetW即为完整的refractedW向量。最后转换到本地空间,乘上眼睛的Mask,加到原本的UV上。
|
||||
之后就是使用偏转后的UV去采样贴图了。
|
||||
与视差相同,这里也是在本地与世界空间中进行的计算,同样会有轴向问题,主要是normalW、viewW和frontNormalW参与的计算,normal与view可以转换到切线空间计算,而frontNormalW代表的是模型向前的朝向,这个必须要指定,不过图方便的话,把frontNormalW改成切线空间法线也不是不可以。
|
||||
|
||||
### 多层复合模型
|
||||
樱花大战cedec2020分享:https://blog.ch-wind.com/cedec2020-new-sakura-wars-note/
|
||||
|
||||
>本作的眼睛分为三个部分,眼白的部分是一个内凹的形状,瞳孔的部分则分为了向内凹的部分和向外突出的部分。
|
||||

|
||||
|
||||
>瞳孔的高光叠加在其突出的半透明部分上。根据摄像角度的不同,各个部分的贴图会分开进行移动,使得在哪个角度高光都能处在一个刚好的位置。
|
||||
控制上,有针对高光上下左右的移动强度与控制移动范围的参数共同作用。
|
||||
|
||||

|
||||
>从左边开始,是作为基础颜色的Albedo,以及用于Mask瞳孔的Alpha贴图,用于在Albedo上进行叠加的spt贴图,以及两张瞳孔高光,以及反应环境的matcapture贴图。
|
||||
虽然很多动画风格的渲染中会省略掉瞳孔中的虹彩部分,但是本作为了提高角色靠近时的效果,进行了详细的绘制,同时为了体现环境的变化与matcap的贴图进行叠加。
|
||||
高光贴图有两张,分别使用不同的UV动画进行控制,用于表现眼睛的湿润感。虽然是很细微的操作,但是对于表现角色的感情非常的有用。
|
||||
|
||||

|
||||
SunnySideUp UnityChan
|
||||
|
||||
### 其他效果实现
|
||||
#### 眼睛高光效果
|
||||
1. 贴图高光。使用事先绘制的高光形状贴图,贴到最外面的。并且使用ViewDirection来控制。设定4个UV Coord, 根据 View=》眼睛的本地坐标系=》Normalize后的向量进行插值。
|
||||
2. ~~PBR的思路~~
|
||||
|
||||
#### Matcap反射效果
|
||||
Matcap材质+球形法线贴图
|
||||
```c++
|
||||
float3 NormalBlend_MatcapUV_Detail = viewNormal.rgb * float3(-1,-1,1);
|
||||
float3 NormalBlend_MatcapUV_Base = (mul( UNITY_MATRIX_V, float4(viewDirection,0)).rgb*float3(-1,-1,1)) + float3(0,0,1);
|
||||
float3 noSknewViewNormal = NormalBlend_MatcapUV_Base * dot(NormalBlend_MatcapUV_Base, NormalBlend_MatcapUV_Detail) / NormalBlend_MatcapUV_Base.b - NormalBlend_MatcapUV_Detail;
|
||||
float2 ViewNormalAsMatCapUV = noSknewViewNormal.rg * 0.5 + 0.5;
|
||||
```
|
||||
|
||||
#### 焦散效果
|
||||
>焦散的表现反倒简单了,直接画在眼睛贴图上都可以,考虑到卡通表达的自由性,焦散是否存在与焦散的形状都可以没有限制,只要好看就行。
|
||||
下图也是miHoYo的分享,可以简单的理解为直接贴张Mask上去,然后用光照方向和菲涅尔去影响强度变化。
|
||||

|
||||
|
||||
使用Mask贴图、NoL与菲尼尔来控制
|
||||
|
||||
# 其他游戏方案
|
||||
* [蓝色协议](/document/UrealEngineNPR渲染实现/蓝色协议的方案.html)
|
||||
|
||||
# 大致方案
|
||||
- 主要分为在材质编辑器中完成大部分渲染
|
||||
- 传递少量参数到Lighting阶段并进行光照计算
|
||||
- 以上两者结合
|
||||
|
||||
## 待实现功能
|
||||
- [ ] 顶点色控制Outline宽度,使用顶点色G。
|
||||
- [ ] 使用[罪恶装备中的lightmap](#lightmap) 控制高光的区域和阈值,包括顶点色可以精准控制高光的强度。
|
||||
|
||||
## 具体实现
|
||||
### 全局设置实现
|
||||
AWorldSettings存储于ULevel中。可以通过Project Settings->Engine->General Settings->Default Classes来修改成其他的类作为默认类。
|
||||
```c#
|
||||
[/Script/Engine.Engine]
|
||||
WorldSettingsClassName=/Script/MySpiffyGame.MySpiffyGameWorldSettings
|
||||
```
|
||||
C++中修改Settings类:`void ULevel::SetWorldSettings(AWorldSettings* NewWorldSettings)`或许可以考虑子系统。
|
||||
|
||||
### Shader与管线变量添加
|
||||
```c#
|
||||
shadowAttenuation = mainLight.shadowAttenuation;
|
||||
|
||||
float _SystemShadowsLevel_var = (shadowAttenuation*0.5)+0.5+_Tweak_SystemShadowsLevel > 0.001 ? (shadowAttenuation*0.5)+0.5+_Tweak_SystemShadowsLevel : 0.0001;
|
||||
float Set_FinalShadowMask = saturate((1.0 + ( (lerp( _HalfLambert_var, _HalfLambert_var*saturate(_SystemShadowsLevel_var), _Set_SystemShadowsToBase ) - (_BaseColor_Step-_BaseShade_Feather)) * ((1.0 - _Set_1st_ShadePosition_var.rgb).r - 1.0) ) / (_BaseColor_Step - (_BaseColor_Step-_BaseShade_Feather))));
|
||||
```
|
||||
|
||||
Ramp相关变量
|
||||
- shadowAttenuation 默认为1(方向光没有这个参数)与 Tweak_SystemShadowsLevel,先暂时合并为HalfLambertFix
|
||||
- Step
|
||||
- Feather
|
||||
|
||||
|
||||
Shader开关变量
|
||||
- Is_LightColor_BaseColor:BaseColor是否受到LightColor影响
|
||||
- Is_LightColor_ShadeColor:1st_ShadeColor是否受到LightColor影响
|
||||
|
||||
FViewUniformShaderParameter添加数据
|
||||
-
|
||||
### 顶点色
|
||||
用于存储一些低精度数据,插值即可
|
||||
- R:
|
||||
- G:描边宽度
|
||||
- B:
|
||||
|
||||
蓝色协议的R:阴影区域标记 与 B:Ao,而罪恶装备使用贴图来传递信息。
|
||||
|
||||
### lightmap
|
||||
,G为阴影控(AO),R为高光强度参数,金属和光滑材质的部分设置的更大一些。B通道:用于照明控制。最大值为高光,反之,值越小高光越淡。
|
||||

|
||||
|
||||
https://zhuanlan.zhihu.com/p/360229590一文中介绍了崩坏3与原神的计算方式
|
||||
|
||||
崩坏3的LightMap计算方式:
|
||||
```c++
|
||||
half4 baseColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv.xy);
|
||||
half4 LightMapColor = SAMPLE_TEXTURE2D(_LightMap, sampler_LightMap, input.uv.xy);
|
||||
half3 ShadowColor = baseColor.rgb * _ShadowMultColor.rgb;
|
||||
half3 DarkShadowColor = baseColor.rgb * _DarkShadowMultColor.rgb;
|
||||
|
||||
//如果SFactor = 0,ShallowShadowColor为一级阴影色,否则为BaseColor。
|
||||
float SWeight = (LightMapColor.g * input.color.r + input.lambert) * 0.5 + 1.125;
|
||||
float SFactor = floor(SWeight - _ShadowArea);
|
||||
half3 ShallowShadowColor = SFactor * baseColor.rgb + (1 - SFactor) * ShadowColor.rgb;
|
||||
```
|
||||
|
||||
二级阴影计算:
|
||||
```c++
|
||||
//如果SFactor = 0,DarkShadowColor为二级阴影色,否则为一级阴影色。
|
||||
SFactor = floor(SWeight - _DarkShadowArea);
|
||||
DarkShadowColor = SFactor * (_FixDarkShadow * ShadowColor + (1 - _FixDarkShadow) * ShallowShadowColor) + (1 - SFactor) * DarkShadowColor;
|
||||
|
||||
// 平滑阴影边缘
|
||||
half rampS = smoothstep(0, _ShadowSmooth, input.lambert - _ShadowArea);
|
||||
half rampDS = smoothstep(0, _DarkShadowSmooth, input.lambert - _DarkShadowArea);
|
||||
ShallowShadowColor.rgb = lerp(ShadowColor, baseColor.rgb, rampS);
|
||||
DarkShadowColor.rgb = lerp(DarkShadowColor.rgb, ShadowColor, rampDS);
|
||||
|
||||
//如果SFactor = 0,FinalColor为二级阴影,否则为一级阴影。
|
||||
SFactor = floor(LightMapColor.g * input.color.r + 0.9f);
|
||||
half4 FinalColor;
|
||||
FinalColor.rgb = SFactor * ShallowShadowColor + (1 - SFactor) * DarkShadowColor;
|
||||
```
|
||||
|
||||
|
||||
**罪恶装备**:
|
||||
对阴影判断阈值的偏移。(见前面着色部分,顶点AO+手绘修正)
|
||||
G : 轮廓线根据与相机的距离扩大多少的系数
|
||||
B : 等高线 Z 轴偏移值
|
||||
A : 轮廓厚度系数。0.5为标准,1为最大厚度,0为无等高线
|
||||
|
||||
### 描边
|
||||
**蓝色协议**:
|
||||
1. 使用Sobel过滤器进行深度检测描边。
|
||||
2. 使用Sobel过滤器进行Id图检测描边。
|
||||
3. 使用Sobel过滤器进行Normal检测描边。用于处理一些难以分ID,深度差又很小的地方,通过获取周围点法线求点乘的方式判断出轮廓。![[08-Assets/Images/ImageBag/UrealEngineNPR/蓝色协议_Normal检测描边.png)
|
||||
4. 预先画好的轮廓(GBuffer)。
|
||||
|
||||
所以使用需要 OutlineId、OutlineWidth(感觉可以传递一个全局Outline信息贴图再通过ID查表来获取,但只能在角色较少时使用)、OutlinePaint 、OutlineZShift(个人感觉不需要)
|
||||
|
||||
### 边缘光
|
||||
|
||||
### RimLighting
|
||||
|
||||
### 接触阴影
|
||||
|
||||
### 面部阴影
|
||||
使用Face ShaderModel,修改法线。描边使用ObjectPivot 缩放进行扩边。
|
||||
|
||||
### 后处理
|
||||
FSceneView存储FFinalPostProcessSettings FinalPostProcessSettings,场景的后处理信息。
|
||||
FBloomOutputs AddBloomPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FBloomInputs& Inputs)
|
||||
|
||||
# GBuffer
|
||||
```c#
|
||||
OutGBufferA = WorldNormal/PerObjectGBufferData
|
||||
OutGBufferB = Metallic/Specular/Roughness/EncodeShadingModelIdAndSelectiveOutputMask(GBuffer.ShadingModelID, GBuffer.SelectiveOutputMask);
|
||||
OutGBufferC = BaseColor/GBufferAO
|
||||
OutGBufferD = GBuffer.CustomData;
|
||||
OutGBufferE = GBuffer.PrecomputedShadowFactors;
|
||||
```
|
||||
```c#
|
||||
GBufferB:Metallic/Specular/Roughness=>ToonHairMask OffsetShadowMask/SpcularMask/SpecularValue
|
||||
OutGBufferD = CustomData.xyzw=》ShaderColor.rgb/NoL
|
||||
OutGBufferE = GBuffer.PrecomputedShadowFactors.xyzw=》 /RimLightMask/DiffuseOffset/RimLightWidth
|
||||
OutGBufferF = velocity => OutlineWidth/OutlineID/OutlinePaint/OutlineZShift
|
||||
```
|
||||
|
||||
```
|
||||
| GBuffer | 表头 |
|
||||
| -------- | ------------------------------------------------------------------------------------- |
|
||||
| GBufferB | OffsetShadowMask SpcularMask SpecularValue EncodeShadingModelIdAndSelectiveOutputMask |
|
||||
| GBufferD | ShaderColor.rgb NoL |
|
||||
| GBufferE | |
|
||||
| GBufferF | ID |
|
||||
```
|
||||
|
||||
## BaseColor与ShadowColor
|
||||
- 原神里ShadowColor还会接收其他物体的阴影投射,没有自投影;蓝色协议可能也没有自投影。
|
||||
|
||||
BaseColor与ShadowColor的过渡需要Step、Feather、Offset等参数,可以直接制作一个HalfLambert的渐变贴图之后使用View传递。因为有多个贴图所以还需要ID贴图指定。但这样需要考虑一个问题:
|
||||
|
||||
- 一个物体上的同一个ID区域的BaseColor与ShadowColor是否都是一样的
|
||||
- 如果不一样就需要再传递一个ShadowColor.rgb到GBuffer里。
|
||||
- 不管如何手绘的补充暗部也是需要加到GBuffer中的
|
||||
|
||||
这决定传递到View里面的渐变贴图是彩色还是暗色
|
||||
|
||||
### 预计算贴图方案(构想)
|
||||
Toon渲染一般会使用HalfLambda。之后使用Feather、Step等参数对过渡边界进行调整
|
||||
使用 渐变贴图查表来实现 渐变、二阶化。以此代替羽化、step等参数。
|
||||
使用ID贴图指定,或者通过BaseColor值来查询?
|
||||
|
||||
## 高光
|
||||
- PBR高光(使用Roughness控制是否可行?是否需要传入GBuffer一个Mask贴图)
|
||||
- 自定义高光:高光贴图、高光颜色、参数化高光形状、多层高光
|
||||
|
||||
## 描边
|
||||
- 原神的描边好像是后处理
|
||||
- 蓝色协议
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/原神_描边.png]]
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/原神截图_描边.png]]
|
||||
|
||||
TODO:考虑使用顶点色来控制宽度,使用顶点色G
|
||||
|
||||
## 多光源
|
||||
主方向光:提供照明与Shadow,其他光只提供照亮效果。
|
||||
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/原神截图_光照.png]]
|
||||
|
||||
## 有关眉毛、表情需要使用 模板功能
|
||||
UTS使用模板
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/UTS表情.png]]
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/UTS表情_StencilOut.png]]
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/UTS表情_StencilMask.png]]
|
||||
|
||||
```
|
||||
Stencil
|
||||
{
|
||||
Ref[_StencilNo] //设置渲染的模板缓存值,0~255
|
||||
Comp[_StencilComp] //模板测试的通过条件,有除了equal,还有Greater、Less、Always、Never等,类似ZTest。
|
||||
Pass[_StencilOpPass] //表示通过模板测试和Z测试(注意是都通过)的像素,怎么处置它的模板值。
|
||||
Fail[_StencilOpFail] //表示通过了模板测试但没通过Z测试的像素,怎么处置它的模板值。
|
||||
}
|
||||
|
||||
_UTS_StencilMode mode = (_UTS_StencilMode)(material.GetInt(ShaderPropStencilMode));
|
||||
switch (mode)
|
||||
{
|
||||
case _UTS_StencilMode.Off:
|
||||
// material.SetInt(ShaderPropStencilNo,0);
|
||||
material.SetInt(ShaderPropStencilComp, (int)_StencilCompFunction.Disabled);
|
||||
material.SetInt(ShaderPropStencilOpPass, (int)_StencilOperation.Keep);
|
||||
material.SetInt(ShaderPropStencilOpFail, (int)_StencilOperation.Keep);
|
||||
break;
|
||||
case _UTS_StencilMode.StencilMask:
|
||||
// material.SetInt(ShaderPropStencilNo,0);
|
||||
material.SetInt(ShaderPropStencilComp, (int)_StencilCompFunction.Always);
|
||||
material.SetInt(ShaderPropStencilOpPass, (int)_StencilOperation.Replace);
|
||||
material.SetInt(ShaderPropStencilOpFail, (int)_StencilOperation.Replace);
|
||||
break;
|
||||
case _UTS_StencilMode.StencilOut:
|
||||
// material.SetInt(ShaderPropStencilNo,0);
|
||||
material.SetInt(ShaderPropStencilComp, (int)_StencilCompFunction.NotEqual);
|
||||
material.SetInt(ShaderPropStencilOpPass, (int)_StencilOperation.Keep);
|
||||
material.SetInt(ShaderPropStencilOpFail, (int)_StencilOperation.Keep);
|
||||
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
七大罪中使用使用了深度测试 Greater Equal.(默认是 less Equal)。但这个方式可能在UE4里不太行,因为UE4的深度测试是全局的?
|
||||
|
||||
这个应该需要创建一个MeshProcessor来实现:
|
||||
```c#
|
||||
FSingleLayerWaterPassMeshProcessor::FSingleLayerWaterPassMeshProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, const FMeshPassProcessorRenderState& InPassDrawRenderState, FMeshPassDrawListContext* InDrawListContext)
|
||||
: FMeshPassProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, InDrawListContext)
|
||||
, PassDrawRenderState(InPassDrawRenderState)
|
||||
{
|
||||
if (SingleLayerWaterUsesSimpleShading(Scene->GetShaderPlatform()))
|
||||
{
|
||||
// Force non opaque, pre multiplied alpha, transparent blend mode because water is going to be blended against scene color (no distortion from texture scene color).
|
||||
FRHIBlendState* ForwardSimpleWaterBlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_InverseSourceAlpha>::GetRHI();
|
||||
PassDrawRenderState.SetBlendState(ForwardSimpleWaterBlendState);
|
||||
}
|
||||
}
|
||||
|
||||
//默认是CF_DepthNearOrEqual,做这个效果可能就要使用CF_DepthFartherOrEqual
|
||||
void SetupBasePassState(FExclusiveDepthStencil::Type BasePassDepthStencilAccess, const bool bShaderComplexity, FMeshPassProcessorRenderState& DrawRenderState)
|
||||
{
|
||||
DrawRenderState.SetDepthStencilAccess(BasePassDepthStencilAccess);
|
||||
if (bShaderComplexity)
|
||||
{
|
||||
// Additive blending when shader complexity viewmode is enabled.
|
||||
DrawRenderState.SetBlendState(TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_One>::GetRHI());
|
||||
// Disable depth writes as we have a full depth prepass.
|
||||
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_DepthNearOrEqual>::GetRHI());
|
||||
}
|
||||
}
|
||||
```
|
||||
设置位置在于FXXXPassMeshProcessor::Process()中的SetDepthStencilStateForBasePass()中。需要bMaskedInEarlyPass为false,GetDepthStencilAccess为DepthRead。
|
||||
```c#
|
||||
const bool bMaskedInEarlyPass = (MaterialResource.IsMasked() || Mesh.bDitheredLODTransition) && MaskedInEarlyPass(GShaderPlatformForFeatureLevel[FeatureLevel]);
|
||||
|
||||
if (bEnableReceiveDecalOutput)
|
||||
{
|
||||
// Set stencil value for this draw call
|
||||
// This is effectively extending the GBuffer using the stencil bits
|
||||
const uint8 StencilValue = GET_STENCIL_BIT_MASK(RECEIVE_DECAL, PrimitiveSceneProxy ? !!PrimitiveSceneProxy->ReceivesDecals() : 0x00)
|
||||
| STENCIL_LIGHTING_CHANNELS_MASK(PrimitiveSceneProxy ? PrimitiveSceneProxy->GetLightingChannelStencilValue() : 0x00);
|
||||
|
||||
if (bMaskedInEarlyPass)
|
||||
{
|
||||
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<
|
||||
false, CF_Equal,
|
||||
true, CF_Always, SO_Keep, SO_Keep, SO_Replace,
|
||||
false, CF_Always, SO_Keep, SO_Keep, SO_Keep,
|
||||
0xFF, GET_STENCIL_BIT_MASK(RECEIVE_DECAL, 1) | STENCIL_LIGHTING_CHANNELS_MASK(0x7)
|
||||
>::GetRHI());
|
||||
DrawRenderState.SetStencilRef(StencilValue);
|
||||
}
|
||||
else if (DrawRenderState.GetDepthStencilAccess() & FExclusiveDepthStencil::DepthWrite)
|
||||
{
|
||||
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<
|
||||
true, CF_GreaterEqual,
|
||||
true, CF_Always, SO_Keep, SO_Keep, SO_Replace,
|
||||
false, CF_Always, SO_Keep, SO_Keep, SO_Keep,
|
||||
0xFF, GET_STENCIL_BIT_MASK(RECEIVE_DECAL, 1) | STENCIL_LIGHTING_CHANNELS_MASK(0x7)
|
||||
>::GetRHI());
|
||||
DrawRenderState.SetStencilRef(StencilValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<
|
||||
false, CF_GreaterEqual,
|
||||
true, CF_Always, SO_Keep, SO_Keep, SO_Replace,
|
||||
false, CF_Always, SO_Keep, SO_Keep, SO_Keep,
|
||||
0xFF, GET_STENCIL_BIT_MASK(RECEIVE_DECAL, 1) | STENCIL_LIGHTING_CHANNELS_MASK(0x7)
|
||||
>::GetRHI());
|
||||
DrawRenderState.SetStencilRef(StencilValue);
|
||||
}
|
||||
}
|
||||
else if (bMaskedInEarlyPass)
|
||||
{
|
||||
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Equal>::GetRHI());
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
# toonshading技术拆解
|
||||
{% simplemindmap %}
|
||||
```
|
||||
- toonshading技术拆解
|
||||
- 描边
|
||||
- 外描边:后处理描边 、 Mesh挤出 2种方式,原神采用了Mesh挤出。
|
||||
- 内描边:后处理、SDF描边、本村线 3种方式
|
||||
- 使用Mesh基础、后处理、模型绘制。(后处理传递Id贴图)
|
||||
- 分阶着色
|
||||
- 二阶化:
|
||||
- 多阶化:
|
||||
- 自定义次表面(暗部)颜色
|
||||
- 颜色过渡(羽化):
|
||||
- lut:待讨论
|
||||
- 高光
|
||||
- 高光贴图
|
||||
- 高光颜色
|
||||
- 参数化高光形状
|
||||
- 多层高光
|
||||
- 阴影
|
||||
- 自定义阴影颜色
|
||||
- 自定义阴影范围
|
||||
- 自定义阴影形状
|
||||
- 阴影过渡(sdf阴影)
|
||||
- 边缘光
|
||||
- 边缘光是否受到光照影响
|
||||
- 多层边缘光
|
||||
- 边缘光衰减(入射角度更明显 or 背光角度更明显)
|
||||
- 多光源支持
|
||||
- PBR支持
|
||||
- PBR和NPR混合
|
||||
- AO
|
||||
- matcap支持
|
||||
- 根据相机角度调整模型(非必要)
|
||||
- Shader内编辑法线
|
||||
- N=_scale * L + N
|
||||
- 眼睛
|
||||
- 反射 环境反射或者matcap支持
|
||||
- 内阴影 AO实现或者画死的内阴影
|
||||
- 瞳孔 瞳孔缩放
|
||||
- 视差和效果 凹凸效果
|
||||
- 高光 自定义高光形状&位置
|
||||
- 高光流动效果
|
||||
- 头发
|
||||
- 各项异性头发
|
||||
- 高光扰动
|
||||
- 高光贴图
|
||||
- 自定义高光属性
|
||||
- 高光天使环
|
||||
- 无各项异性头发
|
||||
- 自定义高光参数
|
||||
- 高光贴图
|
||||
- 高光天使环
|
||||
- 特殊效果
|
||||
- 眉毛/睫毛不受遮挡
|
||||
- 自发光
|
||||
- 阴影内素描效果
|
||||
- 额外效果
|
||||
- 后处理 辉光效果
|
||||
- 之后提到了SunFlare,应该是那个屏幕后处理效果。卡通渲染很依赖体积光,所以自然会有好的效果,假也没关系,假才是对的。谷歌搜SNN Filter https://www.shadertoy.com/view/MlyfWd
|
||||
- 旁边的Kuwahara应该是个类似的算法,64采样。
|
||||
```
|
||||
{% endsimplemindmap %}
|
154
03-UnrealEngine/卡通渲染相关资料/卡通渲染开发计划.md
Normal file
154
03-UnrealEngine/卡通渲染相关资料/卡通渲染开发计划.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# 功能表
|
||||
厚涂部分写在:[[厚涂风格研究与开发笔记]]
|
||||
|
||||
|
||||
#todo 保存UE资产代码
|
||||
```c++
|
||||
if (const auto Package = Outer->GetPackage())
|
||||
{
|
||||
Package->SetDirtyFlag(true);
|
||||
|
||||
UEditorLoadingAndSavingUtils::SavePackages({Package}, true);
|
||||
}
|
||||
```
|
||||
|
||||
知乎提到的渲染功能:
|
||||
- 发尖勾线 https://zhuanlan.zhihu.com/p/405518306
|
||||
- SDF 描边
|
||||
- https://zhuanlan.zhihu.com/p/113190695
|
||||
- https://zhuanlan.zhihu.com/p/360229590
|
||||
- 屏幕空间深度边缘光 Screen Space Depth Rimlight
|
||||
- https://zhuanlan.zhihu.com/p/139290492
|
||||
- 原神实现
|
||||
- https://zhuanlan.zhihu.com/p/435005339
|
||||
- 遮挡时的模糊网点效果
|
||||
- https://zhuanlan.zhihu.com/p/370140711
|
||||

|
||||
|
||||

|
||||
|
||||
按照需求优先级进行排列:
|
||||
- [ ] Anit-Lut功能
|
||||
- [ ] Anit-ToneMapping
|
||||
- [ ] 完整的Anit-Lut功能(虚拟拍摄也会用到)
|
||||
- [ ] 描边改进
|
||||
- [ ] 后处理部分,使用蓝色协议的方案
|
||||
- [ ] MultiDraw BackFace部分
|
||||
- [ ] 绘制轮廓方法落地:本村线、SDF
|
||||
- [ ] 使用添加MeshDrawPass的方式实现比较完美的BackFace
|
||||
- [ ] ToonData与ToonWorldSettings改进
|
||||
- [ ] 增加多个SSS 预积分贴图功能
|
||||
- [ ] 将材质以外的参数都集中到ToonWorldSettings类中,以方便调节
|
||||
- [ ] 面部光影方案改进
|
||||
- [ ] 在GBuffer中添加额外面部自定义法线
|
||||
- [ ] SDF面光方案
|
||||
- [ ] 角色面部Anit-Perspective 与 手指放大效果
|
||||
- [ ] 模仿VRM4U的参数
|
||||
- [ ] 参考AnimMaker
|
||||
- [ ] https://www.patreon.com/posts/56089741
|
||||
- [ ] 后处理
|
||||
- [ ] 原神 辉光效果
|
||||
- [ ] TAA抗锯齿 => Responsive AA,解决Outline模糊问题
|
||||
- [ ] 影视级Bloom实现
|
||||
- [ ] 眉毛效果
|
||||
- [ ] 可以考虑使用TranslucencySortPriority来解决
|
||||
- [ ] 通过MeshDrawPass实现眉毛最前显示效果
|
||||
- [ ] 实现眉毛描边效果
|
||||
- [ ] 额发效果
|
||||
- [ ] 额发阴影效果(衣服阴影效果)
|
||||
- [ ] 实现半透明额发
|
||||
- [ ] 天光与间接光处理
|
||||
- [ ] 为了防止环境光把角色照出立体感,所以计算环境光时,会把法线全部看作世界空间上方向来处理。同时增加了一些参数可以进行一些定制化调整。
|
||||
- [ ] 使用顶点色与第二套UV来修改一些可以实时修改的效果
|
||||
- [ ] 实现PBR <=> Cel 卡通渲染效果的参数切换
|
||||
- [ ] 使用Kawaii插件实现柔体效果
|
||||
- [ ] 后处理边缘光落地
|
||||
- [ ] SSGI 卡通渲染适配?实现AnimMaker 中的一个效果
|
||||
|
||||
## 其他工具制作
|
||||
- [ ] 动画蓝图
|
||||
- [ ] 可交互的动画节点
|
||||
- [ ] Vroid与MMD系列格式的导入工具
|
||||
|
||||
## EOE问题
|
||||
1. 米诺 发布会时段,穿模了。解决方法,给桌子增加深度偏移材质。
|
||||
2. 脸部投影关了好像,角色没有SSS效果,反ToneMapper。
|
||||
3. 适当增加一些手腿与头发布料的效果。
|
||||
4. 眼睛的瞳孔(会缩放)以及高光(会移动)可以改进。
|
||||
|
||||
|
||||
# 改进需求
|
||||
1. 实现脸与脖子之间描边
|
||||
2. 鼻子外部描边
|
||||
3. 手指与手指之间
|
||||
|
||||
## 李兄实现Outline思路
|
||||
### Depth与Normal描边
|
||||
ToonOutlineMain()
|
||||
```c++
|
||||
float3x3 laplacianOperator = float3x3(-1, -1, -1,
|
||||
-1, 8, -1,
|
||||
float3x3 Gx = float3x3( -1, +0, +1,
|
||||
-1, -1, -1);
|
||||
-2, +0, +2,
|
||||
-1, +0, +1);
|
||||
float3x3 Gy = float3x3( +1, +2, +1,
|
||||
+0, +0, +0,
|
||||
-1, -2, -1);
|
||||
```
|
||||
使用GetPixelValue()取得Normal与Depth,之后使用拉普拉斯算子与Sobel算子进行边缘检测:
|
||||
```c++
|
||||
float4 fs0 = s0 * laplacianOperator[0][0];
|
||||
float4 fs1 = s1 * laplacianOperator[0][1];
|
||||
float4 fs2 = s2 * laplacianOperator[0][2];
|
||||
float4 fs3 = s3 * laplacianOperator[1][0];
|
||||
float4 fs4 = s4 * laplacianOperator[1][1];
|
||||
float4 fs5 = s5 * laplacianOperator[1][2];
|
||||
float4 fs6 = s6 * laplacianOperator[2][0];
|
||||
float4 fs7 = s7 * laplacianOperator[2][1];
|
||||
float4 fs8 = s8 * laplacianOperator[2][2];
|
||||
|
||||
float4 sampledValue = fs0 + fs1 + fs2 + fs3 + fs4 + fs5 + fs6 + fs7 + fs8;
|
||||
OutlineMask0 = saturate(1.0 - length(sampledValue)); //Line is black
|
||||
```
|
||||
```c++
|
||||
float4 ds0x = s0 * Gx[0][0];
|
||||
float4 ds1x = s1 * Gx[0][1];
|
||||
float4 ds2x = s2 * Gx[0][2];
|
||||
float4 ds3x = s3 * Gx[1][0];
|
||||
float4 ds4x = s4 * Gx[1][1];
|
||||
float4 ds5x = s5 * Gx[1][2];
|
||||
float4 ds6x = s6 * Gx[2][0];
|
||||
float4 ds7x = s7 * Gx[2][1];
|
||||
float4 ds8x = s8 * Gx[2][2];
|
||||
float4 SGX = ds0x + ds1x + ds2x + ds3x + ds4x + ds5x + ds6x + ds7x + ds8x;
|
||||
|
||||
float4 ds0y = s0 * Gy[0][0];
|
||||
float4 ds1y = s1 * Gy[0][1];
|
||||
float4 ds2y = s2 * Gy[0][2];
|
||||
float4 ds3y = s3 * Gy[1][0];
|
||||
float4 ds4y = s4 * Gy[1][1];
|
||||
float4 ds5y = s5 * Gy[1][2];
|
||||
float4 ds6y = s6 * Gy[2][0];
|
||||
float4 ds7y = s7 * Gy[2][1];
|
||||
float4 ds8y = s8 * Gy[2][2];
|
||||
float4 SGY = ds0y + ds1y + ds2y + ds3y + ds4y + ds5y + ds6y + ds7y + ds8y;
|
||||
|
||||
OutlineMask1 = saturate(2.0 - step(0.9, length(sqrt(SGX * SGX + SGY * SGY))));
|
||||
```
|
||||
这个算法巧妙的地方在于对计算卷积核之后使用length(float4(Normal,Depth))来取得结果;Sobel也是:length(sqrt(SGX * SGX + SGY * SGY)。
|
||||
|
||||
最后使用```OutColor.rgba = OutlineMask0;//lerp(OutlineMask0, OutlineMask1, 0.5);```进行混合。使用OutlineIDMap.a作为宽度控制项,并乘以通过Depth重映射后的变量作为宽度Fix因子。
|
||||
|
||||
### ID描边
|
||||
对ToonIDTexture进行Sobel描边。(只使用了Sobel)
|
||||
|
||||
### 混合结果
|
||||
|
||||
## 蓝色协议的做法
|
||||
### 模型外扩
|
||||
|
||||
### 后处理
|
||||
1. 使用Sobel算子进行深度检测,**只勾数值差别较大的区域**:脸部顶点色定义区域 与 模型外部轮廓。
|
||||
2. 使用Sobel进行ID贴图检测。**只勾数值差别较大的区域**。
|
||||
3. 进行法线点积(dot)检测。**在 深度差异小 以及 同一个ID区域内进行检测**:手指区域。
|
137
03-UnrealEngine/卡通渲染相关资料/卡通渲染相关资料.md
Normal file
137
03-UnrealEngine/卡通渲染相关资料/卡通渲染相关资料.md
Normal file
@@ -0,0 +1,137 @@
|
||||
### 其他项目分享
|
||||
- [ ] 「生と死」を物語る陰影表現とは――『Xenoblade3(ゼノブレイド3)』のキャラクターを魅せる2灯トゥーンシェーディング、世界を描くアップサンプリング【CEDEC+KYUSHU 2022】 https://gamemakers.jp/article/2023_03_22_33475/?fbclid=IwAR0v4cetIP5nKgz1N_xT45FMaoB2ElqZHedu60ZBfhix1hxsxRn6zx5c2ak
|
||||
|
||||
### 卡通渲染总结
|
||||
---
|
||||
- [ ] 米哈游技术总监首次分享:移动端高品质卡通渲染的实现与优化方案 https://zhuanlan.zhihu.com/p/37001473
|
||||
- [ ] [Unite 2018] 米哈游:在Unity中实现高品质的卡通渲染 https://www.bilibili.com/video/BV1Df4y1X7G3/
|
||||
- [ ] 【从负一到零的卡通渲染】卡渲笔记:https://zhuanlan.zhihu.com/p/369443326
|
||||
- [ ] 【从零到零点一的原神卡渲还原】记录还原的尝试和思考 https://zhuanlan.zhihu.com/p/376094989
|
||||
- [ ] 原神角色渲染Shader分析还原 https://zhuanlan.zhihu.com/p/360229590
|
||||
- [ ] 原神图形技术简析及杂谈 https://zhuanlan.zhihu.com/p/260824391
|
||||
- [ ] 卡通渲染小记一(原神篇) https://zhuanlan.zhihu.com/p/374466780
|
||||
- [ ] [译] 崩坏3的卡通渲染实现方式拆解 https://zhuanlan.zhihu.com/p/120908528
|
||||
-
|
||||
---
|
||||
- [ ] 卡通渲染相关小结 https://zhuanlan.zhihu.com/p/83619204
|
||||
- [ ] Unity卡通渲染总结 NPR Toon Shading https://zhuanlan.zhihu.com/p/330599077
|
||||
- [ ] 卡通渲染学习总结 https://zhuanlan.zhihu.com/p/163791090
|
||||
- [ ] 【02】从零开始的卡通渲染-着色篇1 https://zhuanlan.zhihu.com/p/110025903
|
||||
- [ ] 【03】从零开始的卡通渲染-着色篇2 https://zhuanlan.zhihu.com/p/111633226
|
||||
- [ ] 【04】从零开始的卡通渲染-PBR篇 https://zhuanlan.zhihu.com/p/115238808
|
||||
- [ ] 卡通渲染-----关于常规着色效果的那点公式 https://zhuanlan.zhihu.com/p/258615988
|
||||
- [ ] Godot还原 碧蓝幻想Versus GGX 卡通渲染 https://zhuanlan.zhihu.com/p/390781757
|
||||
---
|
||||
新版罪恶装备
|
||||
- [ ] 风格化角色渲染整理之罪恶装备Strive https://zhuanlan.zhihu.com/p/384202640
|
||||
---
|
||||
- [ ] 简单谈谈原神的渲染部分 https://zhuanlan.zhihu.com/p/259589537
|
||||
- [ ] 到目前为止的二次元渲染总结 https://zhuanlan.zhihu.com/p/126668414
|
||||
- [ ] 用SDF处理卡通内描线的锯齿问题 https://zhuanlan.zhihu.com/p/113190695
|
||||
- [ ] 借AI梦境档案谈谈NPR渲染 https://zhuanlan.zhihu.com/p/87619107
|
||||
---
|
||||
- [ ] 【JTRP】Unity HDRP卡通渲染总结 https://zhuanlan.zhihu.com/p/385186333
|
||||
- [ ] 开源JTRP:Unity HDRP ToonShading Render Pipeline https://zhuanlan.zhihu.com/p/126229177
|
||||
---
|
||||
重力眩晕分析
|
||||
- [ ]【01】开篇:重力眩晕2中的渲染效果概览 https://zhuanlan.zhihu.com/p/94581861
|
||||
- [ ]【02】卡通渲染基本光照模型的实现 https://zhuanlan.zhihu.com/p/95986273
|
||||
- [ ]【03】卡通渲染LightMap的使用 https://zhuanlan.zhihu.com/p/97338996
|
||||
- [ ]【04】卡通渲染 次表面散射效果的简易实现 https://zhuanlan.zhihu.com/p/97892884
|
||||
|
||||
---
|
||||
|
||||
### 描边
|
||||
- [ ] High-Quality Real-Time Outline Rendering 描边学习记录 https://zhuanlan.zhihu.com/p/318769754
|
||||
- [ ] 在shader中实现五种描边方法
|
||||
- [ ] 【01】从零开始的卡通渲染-描边篇 https://zhuanlan.zhihu.com/p/109101851
|
||||
- [ ] GPU Pro 1 使用几何着色器的NPR效果 | NPR Effects Using the Geometry Shader——使用几何着色器进行描边
|
||||
- [ ] 在UE4引擎中做卡通描边的一点心得 https://zhuanlan.zhihu.com/p/234535777
|
||||
- [ ] 修复发尖的描边法线 https://zhuanlan.zhihu.com/p/405518306
|
||||
|
||||
### 眼睛头发面部阴影
|
||||
- [ ] 神作面部阴影渲染还原 https://zhuanlan.zhihu.com/p/279334552
|
||||
- [ ] 二次元角色卡通渲染—面部篇 https://zhuanlan.zhihu.com/p/411188212
|
||||
- [ ] 二次元角色卡通渲染—眼睛篇 https://zhuanlan.zhihu.com/p/402861632
|
||||
- [ ] Unity URP】以Render Feature实现卡通渲染中的刘海投影 https://zhuanlan.zhihu.com/p/232450616
|
||||
|
||||
### 场景
|
||||
- [ ] UE4 吉卜力 风格化shader ~ 树 https://zhuanlan.zhihu.com/p/402180364
|
||||
- [ ] [游戏美术文档]UE4风格化草地 https://zhuanlan.zhihu.com/p/355609504
|
||||
- [ ] 虚幻4重现“哈尔的移动城堡”花园(附风格化草地制作分享)https://zhuanlan.zhihu.com/p/272734944
|
||||
- [ ] 虚幻4还原风格化“云海群山”场景(附岩石贴图制作思路)https://zhuanlan.zhihu.com/p/337054665
|
||||
- [ ] UE4中用Niagara实现procedural浪花 https://zhuanlan.zhihu.com/p/100700549
|
||||
- [ ] Kuwahara 滤波UE4卡通渲染基础教程 Part4:Paint Filter https://zhuanlan.zhihu.com/p/74807856
|
||||
- [ ] 卡通风格场景的定制化技术部分 https://zhuanlan.zhihu.com/p/338334377
|
||||
- [ ] PBR光照体系下的卡通渲染光照模型 https://zhuanlan.zhihu.com/p/166147653
|
||||
- [ ] 【翻译】在UE4 中创建风格化的丛林环境 https://zhuanlan.zhihu.com/p/261083501
|
||||
|
||||
### 国外文章翻译
|
||||
- [ ] Cygames21年11月技术分享(上篇)https://zhuanlan.zhihu.com/p/441159789
|
||||
- [ ] 「细节到眼泪、眼睛、汗水,赛马娘3D角色模型技术分享」——Cygames21年11月技术分享(下篇https://zhuanlan.zhihu.com/p/441560356
|
||||
- [ ] 【翻译】NPR角色渲染总结!现在是迈向更高境界之时:https://zhuanlan.zhihu.com/p/345915866
|
||||
- [ ] [简单机翻/多图]《使《蓝色协议》成为“剧场动画品质”的手法是?》https://zhuanlan.zhihu.com/p/339603529
|
||||
- [ ] 翻译《使用UE4开发圣剑传说3的经验分享•角色渲染部分》+效果实现 https://zhuanlan.zhihu.com/p/360587048
|
||||
- [ ] 卡通渲染手游七大罪的技术介绍,一 https://zhuanlan.zhihu.com/p/161326626
|
||||
|
||||
### 其他技术
|
||||
- [ ] 「黑丝」的材质如何用shader实现?数学模型是怎样的?https://www.zhihu.com/question/35094847/answer/61161188
|
||||
- [ ] 遮挡时的模糊网点效果 https://zhuanlan.zhihu.com/p/370140711
|
||||
- [ ] 《Honey Select》捏人剖析 https://zhuanlan.zhihu.com/p/28471808
|
||||
|
||||
|
||||
|
||||
## 参考项目
|
||||
- 《莱莎的炼金工坊12》
|
||||
- 《破晓传说》
|
||||
- 《蓝色协议》https://zhuanlan.zhihu.com/p/229621134?tdsourcetag=s_pctim_aiomsg
|
||||
- 《kurtzpel》
|
||||
- 《守望先锋》
|
||||
- 《二之国》
|
||||
- 原神掰法线 https://www.bilibili.com/video/BV1Nr4y1K7Wc
|
||||
|
||||
## ue4与u3d参考项目
|
||||
- [x] UnityChanToonShader v1、v2、URP
|
||||
- [x] UnityURPToonLitShaderExample:https://github.com/ColinLeung-NiloCat/UnityURPToonLitShaderExample
|
||||
- [x] JTRP:https://github.com/Jason-Ma-233/JasonMaToonRenderPipeline
|
||||
- [x] VRM4U
|
||||
|
||||
## 抓帧工具
|
||||
- 使用 Intel GPA 与 分析3D程序和抓取模型:https://www.cnblogs.com/TracePlus/p/4233606.html
|
||||
|
||||
## u3d资料
|
||||
- 走近卡通渲染——关于Trick的二三事https://learn.u3d.cn/tutorial/cel-shading-trick
|
||||
- 卡通渲染——风格和影视化探索 https://learn.u3d.cn/tutorial/cel-shading-cinematics
|
||||
- 【Unite Tokyo 2018】ToonShader对话环节#1『RealtimeToonShader彻谈』Part2:https://matrix64.github.io/Unite18ToonShader_p2-post/
|
||||
- 〈七つの大罪〉をゲームで!高品質グラフィックを具現化するための技法と開発最適化のご紹介 – Unity Learning Materials:https://learning.unity3d.jp/3227/
|
||||
- スマホVTuber向け揺れモノシステムを「ユニティちゃんライセンス」で無料公開! – Unity Learning Materials:https://learning.unity3d.jp/668/
|
||||
- (466) 【Unite 2017 Tokyo】VR MAGIC! ~キャラクターに命を吹き込んだこの4年間の記録~ - YouTube:https://www.youtube.com/watch?v=nWR816af2dU&feature=youtu.be
|
||||
- Unity Chan研究(3)思维脑图 - 知乎:https://zhuanlan.zhihu.com/p/105047326
|
||||
- (466) 【Unite 2017 Tokyo】Unityで楽しむノンフォトリアルな絵づくり講座:トゥーンシェーダー・マニアクス - YouTube:https://www.youtube.com/watch?v=6aNB9LhSx7g
|
||||
- 【Unite 2017 Tokyo】Unityで楽しむノンフォトリアルな絵づくり講座:トゥーンシェーダー・マニアクス:https://www.slideshare.net/Unite2017Tokyo/unite-2017-tokyounity-75800622
|
||||
|
||||
## UE4资料
|
||||
- 关于matcap材质的效果优化 - 知乎:https://zhuanlan.zhihu.com/p/57775690
|
||||
- UE4_Ramp生成器_仿原神皮肤shader:https://zhuanlan.zhihu.com/p/248998437
|
||||
- UE4-Niagara与材质在二次元的新展开:http://asher.gg/blog/wp-content/uploads/2019/11/UE4-Niagara%E4%B8%8E%E6%9D%90%E8%B4%A8%E5%9C%A8%E4%BA%8C%E6%AC%A1%E5%85%83%E7%9A%84%E6%96%B0%E5%B1%95%E5%BC%80.pdf
|
||||
- UnrealEngine4】从虚幻四的着色模型到NPR:https://zhuanlan.zhihu.com/p/30965016
|
||||
|
||||
### UE4场景
|
||||
百度网盘:
|
||||
- 风格化场景包
|
||||
- 仿原神蒙德场景:Stylized Medieval Village 4.26
|
||||
- 吉普力风格场景:Stylized Village by Meshingun Studio 4.23-4.26.zip https://mega.nz/folder/iiBCiRpa#B0aaIOv32tJhybTe9gRZXg
|
||||
- 比较烂的 stylized-colorful-camp-pack https://www.bilibili.com/video/BV1Gv411T77m?spm_id_from=333.851.dynamic.content.click
|
||||
|
||||
## 其他资料
|
||||
- 知乎收藏夹
|
||||
- GUILTY GEAR Xrd REVELATOR 3D进化出的非照片真实视觉 https://www.cnblogs.com/TracePlus/p/5697705.html
|
||||
- 【翻译】西川善司「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,前篇(1):https://www.cnblogs.com/TracePlus/p/4205798.html
|
||||
- 【翻译】西川善司「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,前篇(2):https://www.cnblogs.com/TracePlus/p/4205834.html
|
||||
- 【翻译】西川善司的「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,后篇:https://www.cnblogs.com/TracePlus/p/4205978.html
|
||||
- artstation市场:https://www.artstation.com/marketplace/p/9nrrR/genshin-impact-character-shader-for-eevee
|
||||
|
||||
风蚀之月Blog
|
||||
- UE4-Fest-圣剑传说3笔记:https://blog.ch-wind.com/unreal-fest-trials-of-mana-3-note/
|
||||
- UE4-Fest-NPR笔记:https://blog.ch-wind.com/ue4-fest-npr-note/
|
||||
- CEDEC2020-新樱花大战笔记(Toon风格的PBR):https://blog.ch-wind.com/cedec2020-new-sakura-wars-note/
|
204
03-UnrealEngine/卡通渲染相关资料/卡通面部阴影控制.md
Normal file
204
03-UnrealEngine/卡通渲染相关资料/卡通面部阴影控制.md
Normal file
@@ -0,0 +1,204 @@
|
||||
---
|
||||
title: 卡通面部阴影控制
|
||||
date: 2023-03-08 10:18:36
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
# 相关资料
|
||||
- [虚幻5渲染编程(风格化渲染篇) 第七卷: Toon shadow control](https://zhuanlan.zhihu.com/p/519728086)
|
||||
- [二次元角色卡通渲染—面部篇](https://zhuanlan.zhihu.com/p/411188212)
|
||||
- [掰法线的方法 SP Normal Convert Shader](https://note.com/sfna32121/n/n8d46090005d1)
|
||||
- 绘制SDF贴图方法
|
||||
- [卡通脸部阴影贴图生成 渲染原理](https://zhuanlan.zhihu.com/p/389668800)
|
||||
- [【教程】使用csp等高线填充工具制作三渲二面部阴影贴图](https://www.bilibili.com/video/BV16y4y1x7J1/)
|
||||
- SDF Shadow Shader
|
||||
- [神作面部阴影渲染还原](https://zhuanlan.zhihu.com/p/279334552)
|
||||
|
||||
|
||||
# SDF面部阴影
|
||||
## Signed Distance Fields
|
||||
- 生成距离场算法: http://www.codersnotes.com/notes/signed-distance-fields/
|
||||
|
||||
>这实质上就是找目标点及左上方四个点中,SDF最小的值。PASS0就是按照从上到下,从左到右的顺序,遍历整个图像,遍历完成之后,对于所有物体外的点,如果距离它最近的物体是在它的左上方,那么它的SDF值就已确定。类似的,PASS1就是按照从下到上,从右到左的顺序,依次比较右下方的四个点,遍历完成之后,对于所有物体外的点,如果距离它最近的物体是在它的右下方,那么它的SDF也已经确定了。两个PASS结合,那么整个图像的SDF就都计算出来了。(其实这里称SDF并不准确,因为只算了物体外到物体边界的距离,是正值,并没有signed一说,只有做完下一步计算物体内到物体外的距离,两个距离相减,才是SDF)
|
||||
|
||||
>第二个grid的GenerateSDF就很好理解了,就是**计算物体内部到外部的距离**。因为一个点要么在物体内要么在物体外,所以两次的SDF值要么全为零(在边界上),要么一个为0,一个为距离值。用:grid1(pixel).sdf - grid2(pixel).sdf就能得到完整的SDF。
|
||||
|
||||
1. 读取传入的贴图,并且构建2个RT,一个黑(inside Zero)一个白(Outside infinitely),当亮度大于0.5(128)时则反转。
|
||||
2. 分别对这2个RT执行 GenerateSDF()。
|
||||
3. 将2个RT记录的距离值相减,获得最终的SDF。
|
||||
|
||||
链接中的代码:
|
||||
```c++
|
||||
struct Point
|
||||
{
|
||||
int dx, dy;
|
||||
int DistSq() const { return dx*dx + dy*dy; }
|
||||
};
|
||||
|
||||
struct Grid
|
||||
{
|
||||
Point grid[HEIGHT][WIDTH];
|
||||
};
|
||||
```
|
||||
|
||||
```c++
|
||||
void Compare( Grid &g, Point &p, int x, int y, int offsetx, int offsety )
|
||||
{
|
||||
Point other = Get( g, x+offsetx, y+offsety );
|
||||
other.dx += offsetx;
|
||||
other.dy += offsety;
|
||||
|
||||
if (other.DistSq() < p.DistSq())
|
||||
p = other;
|
||||
}
|
||||
|
||||
//采用分离式计算SDF
|
||||
//GenerateSDF:计算并且取得当前UV坐标附近(第一次找到)像素最小值(模型使用 0值填充,即dx,dy=0),放到RT中。
|
||||
void GenerateSDF( Grid &g )
|
||||
{
|
||||
// Pass 0
|
||||
for (int y=0;y<HEIGHT;y++)
|
||||
{
|
||||
for (int x=0;x<WIDTH;x++)
|
||||
{
|
||||
Point p = Get( g, x, y );
|
||||
Compare( g, p, x, y, -1, 0 );
|
||||
Compare( g, p, x, y, 0, -1 );
|
||||
Compare( g, p, x, y, -1, -1 );
|
||||
Compare( g, p, x, y, 1, -1 );
|
||||
Put( g, x, y, p );
|
||||
}
|
||||
|
||||
for (int x=WIDTH-1;x>=0;x--)
|
||||
{
|
||||
Point p = Get( g, x, y );
|
||||
Compare( g, p, x, y, 1, 0 );
|
||||
Put( g, x, y, p );
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 1
|
||||
for (int y=HEIGHT-1;y>=0;y--)
|
||||
{
|
||||
for (int x=WIDTH-1;x>=0;x--)
|
||||
{
|
||||
Point p = Get( g, x, y );
|
||||
Compare( g, p, x, y, 1, 0 );
|
||||
Compare( g, p, x, y, 0, 1 );
|
||||
Compare( g, p, x, y, -1, 1 );
|
||||
Compare( g, p, x, y, 1, 1 );
|
||||
Put( g, x, y, p );
|
||||
}
|
||||
|
||||
for (int x=0;x<WIDTH;x++)
|
||||
{
|
||||
Point p = Get( g, x, y );
|
||||
Compare( g, p, x, y, -1, 0 );
|
||||
Put( g, x, y, p );
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```c++
|
||||
int dist1 = (int)( sqrt( (double)Get( grid1, x, y ).DistSq() ) );
|
||||
int dist2 = (int)( sqrt( (double)Get( grid2, x, y ).DistSq() ) );
|
||||
int dist = dist1 - dist2;
|
||||
```
|
||||
|
||||
## SDF面部阴影
|
||||
本质是使用SDF的过度原理(对X张贴图生成SDF贴图),使用HalfNoL来查询一个(0,180)区间的阴影。
|
||||
|
||||
### 效果
|
||||
- SDF面部阴影效果32秒处:https://www.bilibili.com/video/BV1JG4y1r7EP/?spm_id_from=333.337.search-card.all.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
|
||||
|
||||
![[SDFShadowEffect.mp4]]
|
||||
|
||||
### 绘制方法
|
||||
- 等高线绘制方法:https://www.bilibili.com/video/BV16y4y1x7J1/?spm_id_from=autoNext&vd_source=d47c0bb42f9c72fd7d74562185cee290
|
||||
- 注意:等高线曲线需要关闭绘制抗锯齿。
|
||||
- 使用色阶就可以把各个角度的SDF贴图提取出来。
|
||||
- Maya烘焙绘制方法(应该也可以用UE)
|
||||
1. 给脸部模型一个Lambert材质,并且根据X轴角度要求设置好方向光。
|
||||
2. 使用Arnold的RenderToTexture,将贴图输出。
|
||||
3. 使用PS的色阶功能将颜色二值化,并在基础上进行修改。
|
||||
4. **对8张贴图两两计算SDF值,之后进行重映射** 。最终亮度=亮度/8 * index
|
||||
5. 将结果合并到一起。
|
||||
|
||||
### 代码
|
||||
```c++
|
||||
float3 UP = float3(0,1,0); // cs脚本里动态修改
|
||||
float3 Front = float3(0,0,1);
|
||||
float3 Left = cross(UP, Front);
|
||||
float3 Right = -cross(UP, Front);
|
||||
float FrontL = dot(normalize(Front.xz), normalize(L.xz));
|
||||
float LeftL = dot(normalize(Left.xz), normalize(L.xz));
|
||||
float RightL = dot(normalize(Right.xz), normalize(L.xz));
|
||||
|
||||
float lightAttenuation = (FrontL > 0) * min(
|
||||
(surfaceData._lightMap.r > LeftL),
|
||||
1-(1 - surfaceData._lightMap.r < RightL)
|
||||
);
|
||||
```
|
||||
|
||||
## Multi SDFFaceShadow
|
||||
- X轴(正面):1°~90°(8张) =>
|
||||
- X轴(背面-1°~-90°):考虑**单个阴影贴图**或者**边缘光计算方式的阴影**。
|
||||
- Y轴:30°、 90°、150°+ 背面阴影
|
||||
- Y轴(多张贴图):30、50°、70°、90°、110°、130°+ 背面阴影
|
||||
- -60° 、-40° 、-20° 、0、20°、40°
|
||||
- 考虑硬编码写死,所以可以使用不同步长的角度。
|
||||
- 贴图精度可以考虑8bit=>16bit
|
||||
|
||||
>Y轴结果插值可以考虑使用扩散Shader
|
||||
https://www.shadertoy.com/view/tlyfRw
|
||||
ShaderBits的 UV出血填充贴图方法。
|
||||
|
||||
|
||||
# 距离场+法线混合计算
|
||||
https://www.youtube.com/watch?v=T2iI9hbNqLI
|
||||
|
||||
![[SDF_Normal_Shadow.webp]]![[SDF_Normal_Shadow_Effect.mp4]]
|
||||
虽然分享中并没有说明具体怎么计算,不过有了这个思路,做出类似的效果并不是难事,以下简单还原的过程。
|
||||
首先主要代码如下:(图中还有两块区域,即额头偏亮的区域和鼻部底层暗下去的区域,由于并不清楚这两部分的应用场景,下面的计算就忽略这两部分区域,另外也忽略了精度问题)
|
||||
```c++
|
||||
float3 shaodwRamp = tex2D(_ShaodwRamp, input.uv);
|
||||
float3 lightDirH = normalize(float3(L.x, 0, L.z));
|
||||
float NoL = dot(N, lightDirH);
|
||||
float triArea = saturate((shaodwRamp.g - 0.5) * 2);
|
||||
float noseArea = saturate(shaodwRamp.g * 2);
|
||||
|
||||
//lightAtten
|
||||
float lightAtten = dot(lightDirH, forward);
|
||||
lightAtten = saturate(pow(abs(fmod(lightAtten + _TriAreaOffset, 1)
|
||||
- 0.5) * _TriAreaLimitUp, _TriAreaPow) * _TriAreaLimitDown - _TriAreaLimitDown + 1);
|
||||
|
||||
//uvMask
|
||||
float filpU = saturate(sign(dot(L, left)));
|
||||
float cutU = step(0.5, input.uv.x);
|
||||
float uvMask = lerp(1 - cutU, cutU, filpU);
|
||||
|
||||
//NoL
|
||||
float faceShadow = step(0, NoL);
|
||||
//三角区域
|
||||
float triAreaLight = step(lightAtten, triArea) * uvMask;
|
||||
//鼻部区域
|
||||
float noseAreaLight = step(lightAtten, 1 - noseArea) * (1 - uvMask);
|
||||
float noseAreaShadow = (step(lightAtten, noseArea) - 1) * uvMask + 1;
|
||||
|
||||
//最后的阴影混合
|
||||
float Shadow = min(max(max(faceShadow, triAreaLight), noseAreaLight), noseAreaShadow);
|
||||
```
|
||||
|
||||
`SDF取值的思路和上一小节SDF的思路是类似的,也是利用lightAtten的结果来取值,只不过这张图分别记录了两个区域的值,中间值是0.5,三角区域的阈值在(0.5, 1],鼻尖区域的阈值在[0, 0.5)。将这两部分区域重新映射回[0, 1]的区间上就可以使用之前的step操作了,然后利用计算的uvMask屏蔽掉不需要的区域,最后混合三个部分(三角亮区,鼻部亮区,鼻部暗区)的结果就可以了。`
|
||||
|
||||
另外由于SDF与NdotL的结果要互相配合,所以要对lightAtten的结果重新映射以便控制,重新映射的结果如下图的紫色曲线(链接内有每个参数的值)。
|
||||

|
||||
|
||||
# Matcap
|
||||
又到了万能的Matcap出场了,《七つの大罪 光と闇の交戦 : グラクロ》的技术分享中讲述了这种方式,作者在分享中讲到使用这种方式的原因:「根据(动画)演出中非现实的阴影设定,比起物理事实更重视感情的传达」
|
||||
|
||||
在大部分情况下卡通画面不需要保证光照的正确性,只要画面中的效果是感观舒适,就是可行的。所以在非动态光照的下,使用Matcap表现角色的光影结构切分是非常简单并且效果还不错的方式。
|
||||
|
||||

|
324
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/YusufUmar_kama分析.md
Normal file
324
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/YusufUmar_kama分析.md
Normal file
@@ -0,0 +1,324 @@
|
||||
---
|
||||
title: YusufUmar_kama分析
|
||||
date: 2022-08-15 13:31:52
|
||||
excerpt:
|
||||
tags: 卡通渲染
|
||||
rating: ⭐⭐
|
||||
---
|
||||
|
||||
- Skin
|
||||
- Hair
|
||||
- Cloth
|
||||
- Glod
|
||||
- Lotus
|
||||
- LotusLeaf
|
||||
- Water Surface
|
||||
|
||||
# 皮肤
|
||||
根节点使用了Principed BSDF,GGX,RandomWalk(Fixed Radius)。Subsurface值为0.01(开启了SSS效果),其他为默认参数。
|
||||
|
||||
使用Ucupaint Skin连接Pin:
|
||||
- Color
|
||||
- Roughness
|
||||
- Normal
|
||||
|
||||
## 要点:
|
||||
1. 基底:皮肤颜色float3(0.846873,0.602632,0.512498)、Roughness 0.3、Metallic 0 。Subsurface:0.01
|
||||
2. **皮肤哑光效果**使用Noise贴图并且使用ddx/ddy实现。
|
||||
3. [[#yP Layer Fake Lighting(全身)]]效果,模拟出皮肤的SSS以及环境光照效果,增强厚涂皮肤的质感。
|
||||
4. [[#yP Layer Red Skin (肩部区域与手指)]]效果,模拟皮肤的SSS效果,增强厚涂皮肤的质感。
|
||||
5. [[#yP Layer Skin AO (皮肤上的AO效果)]],模拟皮肤上的AO,
|
||||
|
||||
## 贴图
|
||||
>项目使用了4K贴图,并且将4x4个贴图会在一起,并且通过平移UV与使用不同UV通道方式来切换。
|
||||
- `~Ucupaint Skin Image Atlas` Face UV(0.25,0.75)贴图作为Alpha来赋予**脸部**Roughness(内部Overrider 0.5)。
|
||||
- `~Ucupaint Skin Image Atlas` Face UV(0 00.5)腮红Mask
|
||||
- `~Ucupaint Skin Image Atlas` Face UV(0.75,0.75)腮红斜杠Mask
|
||||
- `~Ucupaint Skin Image Atlas` Face UV(0.25,0.75)嘴唇红Mask
|
||||
- `~Ucupaint Skin Image Atlas2`UVMap UV(0.25,0)贴图作为Alpha来赋予**身体**Roughness(内部Overrider 0.6,Alpha * 0.75)。
|
||||
- `~Ucupaint Skin Image Atlas2` UVMap UV(0.25,0.25)贴图作为Alpha来调整**身体水渍**Roughness(内部Overrider 0,Alpha)。
|
||||
- `~Ucupaint Skin Image Atlas` UVMap UV (0,0.75) 是脸上的水渍贴图,用作**脸部水渍**高度、透明度、Roughness,最后计算出Color、Roughness、Normal。(内部BaseColor Overrider:0 Roughness Overrider:0.1,Alpha)。
|
||||
|
||||
## Group
|
||||
### ~yPL Check Input Normal
|
||||
- StartNormalFilter:因为最外部的Normal输入是float3(999,999,999)。所以输出的是float3(0.5,0.5,1.0)。
|
||||
```hlsl
|
||||
float3 StartNormalFilter(float3 InNormal)
|
||||
{
|
||||
float Ratio=InNormal.z > 250 ? 1 : 0;
|
||||
return lerp(InNormal,float3(0.5,0.5,1.0),Ratio);
|
||||
}
|
||||
```
|
||||
|
||||
### ~yPL Tangent Process_Copy
|
||||
通过UV来控制切线法线与次级法线。该节点需要对应的UV通道,Body模型里除了UVMap,有Face、Pupil、~TL Temp Paint UV。
|
||||
- Face Tangent Process:2个参数输入都是1。
|
||||
- Pupil Tangent Process:2个参数输入都是1。
|
||||
- UVMap Tangent Process:2个参数输入都是1。
|
||||
|
||||
https://www.bilibili.com/video/BV1RD4y1d7c3?spm_id_from=333.337.search-card.all.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
|
||||
|
||||
### ~yP Lyaer Noise
|
||||
经过测试,好像没有区别
|
||||
1. 使用Neighbor UV(将UVMap的Tangent、Bitangent转换到Face的切线空间)来获得东西南北4个偏移方向。传入Layer Noise Source,得到Source、东西南北5个Noise随机Height值。
|
||||
2. 将Noise随机高度值从(0,1)->(-1,1),之后与PrevHeight融合(这里不融合)
|
||||
3. Height=Height/MaxHeight,之后将Height从(-1,1)->(0,1)
|
||||
4. 使用ddx/ddy的方式计算出Face切线空间的Noise Bump Normal,再使用Tangent与Bitangent计算出世界法线。
|
||||
|
||||
#### ~yPL Neighbor UV (Other UV)
|
||||
使用`~yPL World2Tangent`计算 2个新向量:
|
||||
- FaceTangent->(UVMapTangent,UVMapBitangent)=x
|
||||
- FaceBitangent->(UVMapTangent,UVMapBitangent)=y
|
||||
|
||||
之后乘以1/Width或1/Height之后偏移UV。
|
||||
|
||||
##### ~yPL World2Tangent
|
||||
将一个世界空间向量转换到指定的切线空间中。~~将Face UV的次级法线与身体UV的切线法线与次级法线做点乘来取得新的向量。~~
|
||||
```c++
|
||||
float3 World2Tangent(float3 Bitangent,float3 UVMapTangent,float3 UVMapBitangent)
|
||||
{
|
||||
float x=dot(Bitangent,UVMapTangent);
|
||||
float y=dot(Bitangent,UVMapBitangent);
|
||||
float z=dot(Bitangent,float(0.5,0.5,1));
|
||||
return float3(x,y,z);
|
||||
}
|
||||
```
|
||||
|
||||
https://zhuanlan.zhihu.com/p/447671623
|
||||
|
||||
#### ~yP Layer Noise Source
|
||||
将坐标传入NoiseTexture取得噪点值,NoiseTexture的参数Scale5=>2000
|
||||
- Source_group
|
||||
- Source_n
|
||||
- Source_s
|
||||
- Source_e
|
||||
- Source_w
|
||||
|
||||
#### ~yPL Height Process Smooth
|
||||
使用`~yPL Height Process`计算Source、东西南北5个,之后使用`~yPL Pack ONSEW`将Source、N、S合成一个HeightONS,E、S合成ES;`~yPL Pack NSEW`将Source、N、S合成AlphaONS,E、S合成ES。
|
||||
|
||||
##### ~yPL Height Process
|
||||
将Height (0~1)=>(-1,1)区间
|
||||
- Height= (Value-0.5) * 2 * ValueMaxHeight;
|
||||
- Alpha=Alpha * RemainingAlpha * Intensity;
|
||||
- NormalAlpha=Alpha * Intensity;
|
||||
|
||||
#### ~yPL Height Mix Smooth
|
||||
- 解包ONS、EW
|
||||
- 计算lerp(Prev,Height,Alpha)
|
||||
- 打包成ONS、EW
|
||||
|
||||
#### ~yPL Normal Process Smooth
|
||||
- 解包ONS、ES
|
||||
- Value=(Value /MaxHeight) * 0.5 +0.5;
|
||||
- 调用`~yPL Fine Bump`
|
||||
|
||||
##### ~yPL Fine Bump
|
||||
Tangent与Bitangent为Face UV通道的。
|
||||
|
||||
- Vector=float3( (w-e) * BumpHeight, (s-n) * BumpHeight, 1);
|
||||
- Vector=Normalize(Vector);
|
||||
- ~yPL Tangent2World(Vector,Tangent,Bitangent);
|
||||
- return Vector;
|
||||
|
||||
###### ~yPL Tangent2World
|
||||
将一个切线空间向量转换到指定的世界空间中。
|
||||
https://zhuanlan.zhihu.com/p/447671623
|
||||
|
||||
## 贴图Roughness赋予Group
|
||||
外部传入0.3,未调整区域会一直为0.3。
|
||||
1. 使用`~yP Layer Face Roughness`使用Face UV(偏移0.25,0.75)通道采样`~Ucupaint Skin Image Atlas`贴图作为Alpha来调整脸部Roughness(内部Overrider 0.5)。
|
||||
2. 使用`~yP Layer Face Roughness`使用UVMap UV(偏移0.25,0)通道采样`~Ucupaint Skin Image Atlas2`贴图作为Alpha来调整Roughness(内部Overrider 0.6,Alpha * 0.75)。
|
||||
3. 使用`~yP Layer Face Roughness`使用UVMap UV(偏移0.25,0.25)通道采样`~Ucupaint Skin Image Atlas2`贴图作为Alpha来调整Roughness(内部Overrider 0,Alpha)。
|
||||
|
||||
### ~yP Layer Face Roughness(脸部Roughness)
|
||||
- Overrider:0.5
|
||||
最外层传入Roughness为0.3,Roughness使用`~Ucupaint Skin Image Atlas`贴图对0.3~0.5进行插值。
|
||||
1. 使用偏移后Face 通道UV(Location 0.25 m 0.75 m 0,Scale 0.25 0.25 1)采样`~Ucupaint Skin Image Atlas`贴图,作为Alpha。
|
||||
2. return Lerp( Roughness, 0.5, Alpha);
|
||||
|
||||
#### ~yPL Mod Override Color
|
||||
使用Gamma调整OverriderColor颜色,Intensity是OverriderColor与原始颜色的插值。
|
||||
|
||||
### ~yP Layer Solid Color 8.001(身体Roughness)
|
||||
- Overrider:0.6
|
||||
1. 使用偏移后UVMap 通道UV(Location 0.25 m 0 0,Scale 0.25 0.25 1)采样`~Ucupaint Skin Image Atlas2`贴图,作为Alpha。
|
||||
2. return Lerp( Roughness, 0.6f, Alpha * 0.75);
|
||||
|
||||
### ~yP Layer Solid Color 8(身体Roughness 水滴光滑效果)
|
||||
- Overrider:0
|
||||
1. 使用偏移后UVMap 通道UV(Location 0 0 0,Scale 0.25 0.25 1)采样`~Ucupaint Skin Image Atlas2`贴图,作为Alpha。
|
||||
2. return Lerp( Roughness, 0, Alpha );
|
||||
|
||||
### ~yP Layer Solid Color 6.002(脸部Roughness 水滴处的Color、Roughness、Normal)
|
||||
- BaseColor Overrider:0
|
||||
- Roughness Overrider:0.1
|
||||
该节点用于制作水渍效果,Color最终使用TransitionAO输出的结果;
|
||||
1. 使用`~yPL Neighbor UV`计算东南西北方向的UV偏移值。
|
||||
2. 使用Source与东南西北4个坐标偏移过坐标,在偏移(0,0.75)后,采样`~Ucupaint Skin Image Atlas`贴图。
|
||||
|
||||
#### ~yPL Transition AO_Copy
|
||||
将InputRPG与AO Color(默认为0)进行混合。**增加水滴处的AO效果**
|
||||
|
||||
### ~yP Layer Solid Color.002(眼白白色)
|
||||
将眼白区域从皮肤颜色设置成眼白的颜色。
|
||||
|
||||
### ~yP Layer Blush(腮红)
|
||||
脸上的腮红效果。
|
||||
1. 对float3(0.749082 0.025 1)执行Gamma0.455,作为绘制颜色。
|
||||
2. 使用偏移后Face 通道UV(Location 0 0.5 0,Scale 0.25 0.25 1)采样`~Ucupaint Skin Image Atlas`贴图,作为Alpha。与外部传入颜色进行混合。
|
||||
|
||||
### ~yP Layer Solid Color 7(腮红上的红色斜杠)
|
||||
1. 使用偏移后Face 通道UV(Location 0.75 0.75 0,Scale 0.25 0.25 1)采样`~Ucupaint Skin Image Atlas`贴图,作为Alpha。与外部传入颜色进行混合。
|
||||
|
||||
### ~yP Layer Lips(嘴唇 红色)
|
||||
1. 使用偏移后Face 通道UV(Location 0.25 0.5 0,Scale 0.25 0.25 1)采样`~Ucupaint Skin Image Atlas`贴图,作为Alpha。与外部传入颜色进行混合。
|
||||
|
||||
### ~yP Layer Tongue(舌头 红色 以及法线调整)
|
||||
这里使用了顶点色作为Mask。
|
||||
1. 使用偏移后Face 通道UV(Location 0.25 0.5 0,Scale 0.25 0.25 1)采样`~Ucupaint Skin Image Atlas`贴图,作为Alpha。
|
||||
|
||||
### ~yP Layer Nose Blush(鼻尖处的红)
|
||||
1. 使用偏移后Face 通道UV(Location 0.5 0.5 0,Scale 0.25 0.25 1)采样`~Ucupaint Skin Image Atlas`贴图,作为Alpha。
|
||||
|
||||
### ~yP Layer Solid Color 6(眼白上部的黑色阴影)
|
||||
1. 使用偏移后Face 通道UV(Location 0.75 0.25 0,Scale 0.25 0.25 1)采样`~Ucupaint Skin Image Atlas`贴图,作为Alpha。
|
||||
|
||||
---
|
||||
### ~yP Layer Pupil
|
||||
虹膜底层(灰色),Roughness为0。
|
||||
|
||||
### ~yP Layer Solid Color 1.001
|
||||
虹膜底色(红)
|
||||
|
||||
### ~yP Layer Solid Color 2
|
||||
虹膜下部高光(粉色)
|
||||
|
||||
### ~yP Layer Solid Color 3
|
||||
虹膜下部的自定义高光(白色)
|
||||
|
||||
### ~yP Layer Solid Color 4
|
||||
虹膜底部高光(白色)
|
||||
|
||||
### ~yP Layer Solid Color.003
|
||||
瞳孔(黑色)
|
||||
|
||||
---
|
||||
### ~yP Layer Group
|
||||
将之前计算身体、脸部细节与瞳孔合并到一起。
|
||||
1. ~~将NormalHeight Group与NormalHeight Alpha Group(这2个Pin没有连接外部数据,默认都为0),解包Source与4方向 的用于计算法线的Height值以及Alpha。~~
|
||||
2. 使用Mask Pupil顶点色作为Mask以及Alpha,将身体、脸部细节与瞳孔混的BaseColor、Metallic、Roughness、Normal合在一起。
|
||||
3. 瞳孔区域的Metallic为0,Normal为0。Roughness为0。
|
||||
|
||||
### ~yP Layer Solid Color 1(眉毛)
|
||||
1. 使用偏移后Face 通道UV(Location 0.25 0 0,Scale 0.25 0.25 1) Source以及东西南北4方向偏移过的UV,去采样`~Ucupaint Skin Image Atlas`与`~Ucupaint Skin Image Atlas 1`。实际上用的是`~Ucupaint Skin Image Atlas 1`mask。
|
||||
2. 使用Extra Lines(耳朵里的一些线条Mask,为了让耳朵有立体感)作为Mask,以20%的占比与眉毛Mask合并到一起。
|
||||
3. Color使用 float3(0.073) 与传入颜色混合;Roughness使用0.66与传入Roughess混合;Norma使用模型Normal与之前计算的Normal混合。
|
||||
|
||||
### ~yP Layer Fake Lighting(全身)
|
||||
模拟出皮肤SSS以及环境光照效果。
|
||||
1. 使用`~yPL Hemi_Copy.005`计算取得Ramp混合Alpha。
|
||||
2. 传入ColorRamp节点,取得Color与Alpha,Color之后还会执行Gamma(0.454)。
|
||||
3. 使用Head的顶点色做Mask,减淡50%。之后再对整个Alpha * 0.5。
|
||||
4. 使用Alpha对传入颜色进行混合,最后输出。
|
||||
|
||||
#### ~yPL Hemi_Copy.005
|
||||
1. 指定一个摄像机空间的法线向量 ViewNormal,转换到世界空间。与模型法线做点乘。
|
||||
2. 将点乘结果(-1,1) -> (0,1)后输出,作为Ramp混合Alpha。
|
||||
|
||||
### ~yP Layer Red Skin (肩部区域与手指)
|
||||
模拟出皮肤SSS的红润效果。
|
||||
1. 使用偏移后UVMap 通道UV(Location 0.75 0.5 0,Scale 0.25 0.25 1)采样`~Ucupaint Skin Image Atlas`贴图,作为Alpha。
|
||||
2. Alpha=Alpha * 0.25;
|
||||
3. return lerp( Color,指定的红色,alpha);
|
||||
|
||||
### ~yP Layer Solid Color 6.004(耳朵)
|
||||
耳朵加上些AO。
|
||||
|
||||
### ~yP Layer Skin AO (皮肤上的AO效果)
|
||||
和[[#~yP Layer Fake Lighting(全身)]]一样:
|
||||
1. 采用dot(ViewVector,Normal)传入ColorRamp,得到Color作为Alpa。
|
||||
2. Alpha=Alpha * 0.75;
|
||||
3. return lerp(Color,指定AO颜色,Alpha);
|
||||
|
||||
# 头发
|
||||
与身体大致相同,但区别在于:
|
||||
- Anisotropic:0=>1
|
||||
- Tangent:连接Tangent节点(ForTangent UV通道)
|
||||
- 连接Pin:
|
||||
- Color
|
||||
- Roughness
|
||||
- ColorAlpha
|
||||
|
||||
## 要点:
|
||||
- `~yP Layer Gradient.003`提亮批发部分亮度,提高层次感觉。
|
||||
- `~yP Layer Solid Color.005`模拟出一个正面打光的AO阴影效果。
|
||||
|
||||
## Group
|
||||
### ~yP Layer Solid Color 1.002
|
||||
1. 使用顶点色(Mask VCol,除了眉毛处有点黑其他都是白色)作为Alpha2传入`~yPL Straight Over Mix`与外部传入的Color与ColorAlpha进行计算。()
|
||||
2. 最终输出Color与ColorAlpha。
|
||||
|
||||
### ~yP Layer Gradient.003
|
||||
对披下的长发区域进行梯度提亮。
|
||||
1. 使用`~yP Modifiers Gradient`计算Color与Alpha之后与输入的Color进行混合。
|
||||
|
||||
### ~yP Layer Fake Lighting.001
|
||||
模拟出一个头顶往下打光的灯光效果。
|
||||
1. 使用dot(WorldNormal,指定世界坐标正面朝上的Vector)制作Alpha
|
||||
2. 将Alpha传入ColorRamp取得Ramp值。
|
||||
3. Color=Ramp * 0.1+Color;
|
||||
|
||||
#### ~yPL Hemi_Copy.002
|
||||
1. 使用dot(WorldNormal,指定世界坐标正面向上的Vector)
|
||||
2. 将点乘值 (-1,1)=>(0,1)
|
||||
3. 进行**正反面**fix后输出。(反面不应该收到光照)
|
||||
|
||||
### ~yP Layer Solid Color.005
|
||||
同`~yP Layer Fake Lighting.001`,模拟出一个正面打光的AO阴影效果。
|
||||
|
||||
# 衣服
|
||||
- 次表面方法换成了Christensen-Burley(但依然没有开启次表面)
|
||||
- 有意思的是开启了ClearCoat材质效果,让衣服有了一些变色的感觉
|
||||
|
||||
## 要点
|
||||
1. `~yP Layer Solid Color 3.001` 叠加了一个从下往上的Ramp,让上部分的衣服显得更紫,更加不透一些。
|
||||
2. `~yP Layer Solid Color 2.001` 实现出一种菲尼尔效果。
|
||||
3. `~yP Layer Fake Lighting 1.001` AO贴图与Fake灯光混合。(应该只有AO效果)
|
||||
4. `~yP Layer Fake Lighting 1`
|
||||
|
||||
## GroupNode
|
||||
### yP Layer Solid Color 1.003_remove
|
||||
用于设置2个袖口的颜色与Alpha 。
|
||||
|
||||
### ~yP Layer Solid Color.006
|
||||
用于设置身前肚兜的颜色与Alpha。
|
||||
1. Fake了一个从下往上打的灯光,并且设置了Ramp
|
||||
2. 使用`~yPL Straight Over Mix`对外部传入的Color与Alpha进行混合。
|
||||
|
||||
### ~yP Layer Solid Color 3.001
|
||||
叠加了一个从下往上的Ramp(根据UV),让上部分的衣服显得更紫,更加不透一些。
|
||||
|
||||
### ~yP Layer Solid Color 4.001
|
||||
对2个袖子的开头与结尾处叠加一些紫色。根据`~Ucupaint Cloth Image Atlas` (0.25,0.25)
|
||||
|
||||
### ~yP Layer Pattern Sleeve
|
||||
袖子上的图案。
|
||||
|
||||
### ~yP Layer Solid Color 5.001
|
||||
胸前衣服的图案。
|
||||
|
||||
### ~yP Layer Solid Color 2.001
|
||||
实现出一种菲尼尔效果。
|
||||
1. dot(指定摄像机空间向量 float3(0,0,1),WorldNormal),传入Ramp,作为Alpha。
|
||||
2. 与传入Alpha、Color,以及内部指定颜色进行混合。
|
||||
|
||||
### ~yP Layer Fake Lighting 1.001
|
||||
1. dot(指定摄像机空间向量 float3(0,0,-1),WorldNormal),传入Ramp,作为Alpha。
|
||||
2. 使用贴图`Cloth AO`的值 与 Ramp混合。
|
||||
|
||||
### ~yP Layer Fake Lighting 1
|
||||
对衣服上的褶皱添加高光效果。
|
||||
|
||||
### ~yP Layer Solid Color 6.003
|
||||
增加菲尼尔AO效果。
|
162
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/YusufUmar其他作品分析.md
Normal file
162
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/YusufUmar其他作品分析.md
Normal file
@@ -0,0 +1,162 @@
|
||||
---
|
||||
title: YusufUmar其他作品分析
|
||||
date: 2022-08-23 16:52:42
|
||||
excerpt:
|
||||
tags: 卡通渲染
|
||||
rating: ⭐
|
||||
---
|
||||
# Saber
|
||||
## 要点
|
||||
- Saber_~yP Layer Solid Color 7:使用dot(摄像机空间float3(0,0,1),WorldNormal)来FakeLight,效果表现为胸部附近的SSS效果。
|
||||
- **Saber_~yP Layer Skin AO**:增加身体区域的SSS与AO效果。**使用AO Mask贴图作为Ramp的Alpha,取得Ramp颜色**;还是用UV Mapping Color生成颜色,传入Ramp,来取得一个黑白Alpha,与之前的Color以4:1的比例混合。**做到一种次表面散射衰减,以及AO的双层效果**。
|
||||
|
||||
## Eye
|
||||
- Saber_~yP Layer Solid Color.004
|
||||
- Saber_~yP Layer Solid Color 1.002
|
||||
- Saber_~yP Layer Solid Color 11
|
||||
|
||||
## Face
|
||||
- Saber_~yP Layer Solid Color 2.002
|
||||
- Saber_~yP Layer Solid Color 3.002
|
||||
- Saber_~yP Layer Solid Color 12:通过贴图Mask来增加自发光的方式来提亮某一些部位。
|
||||
|
||||
## Body
|
||||
- Saber_~yP Layer Solid Color 9:调整身体Roughness以及眼睫毛BaseColor
|
||||
- Saber_~yP Layer Solid Color 10:调整身体Roughness以及高光
|
||||
- Saber_~yP Layer Solid Color 5.001
|
||||
|
||||
## FakeLight
|
||||
- Saber_~yP Layer Solid Color 7:FakeSSS
|
||||
- Saber_~yP Layer Fake Lighting.002:FakeLight
|
||||
|
||||
# Saber2
|
||||
## 要点
|
||||
- ~yP Layer Solid Color.006:绘制边缘处的粉色Layer,模拟了SSS效果。
|
||||
- ~yP Layer Solid Color.005:绘制边缘处的较暗粉色Layer,模拟了AO与GI。
|
||||
|
||||
## Body
|
||||
- ~yP Layer Solid Color.006
|
||||
- ~yP Layer Solid Color.005
|
||||
- ~yP Layer Solid Color 3.002:使用自发光对皮肤进行整体提亮。
|
||||
|
||||
## Eye
|
||||
- ~yP Layer Solid Color 9
|
||||
- ~yP Layer Solid Color 10
|
||||
- ~yP Layer Solid Color 4.002:眼白
|
||||
- ~yP Layer Solid Color 5.002
|
||||
- ~yP Layer Solid Color 11:睫毛
|
||||
|
||||
## Face
|
||||
- ~yP Layer Solid Color 2.003:腮红
|
||||
- ~yP Layer Solid Color 8:唇色
|
||||
|
||||
# Shuten DoujiTexture
|
||||
displacement主要是做出腿甲的凹凸效果。
|
||||
AO节点用于调整AO效果,让暗处更加暗,亮度更加亮。
|
||||
|
||||
## 要点
|
||||
- ~yP Layer Solid Color 8.001:FakeLighting以增加SSS质感。
|
||||
- ~yP Layer BodySkin AO:增加身体的细节AO,已增加立体感。
|
||||
- ~yP Layer BodySkin Cavity:使用CavityMap提亮指定区域。
|
||||
- ~yP Layer Solid Color 9.001:dot(World,指定法线) + 2层Ramp实现的一个类似描边的效果来增加质感。
|
||||
|
||||
## Knee
|
||||
- ~yP Layer Solid Color.002
|
||||
- ~yP Layer Solid Color 3.001
|
||||
- ~yP Layer Noise.001
|
||||
- ~yP Layer Solid Color 13
|
||||
- ~yP Layer Solid Color 11.001
|
||||
- ~yP Layer Solid Color 11
|
||||
- ~yP Layer Solid Color 14
|
||||
- ~yP Layer pattern_knee_indentation.png
|
||||
- ~yP Layer pattern_knee:膝盖上的腿甲
|
||||
- ~yP Layer Tileable eroded scratch metal texture background .000
|
||||
|
||||
## Face
|
||||
- ~yP Layer Solid Color 1.002:眼线
|
||||
- ~yP Layer Solid Color 6.001:眼白
|
||||
- ~yP Layer Solid Color 8.002:眼白阴影效果。
|
||||
- ~yP Layer Solid Color 4.001:腮红
|
||||
- ~yP Layer Solid Color 7.001:腮红上的漫画线
|
||||
- ~yP Layer Solid Color 10.001:嘴唇颜色
|
||||
|
||||
## Horn
|
||||
- ~yP Layer Solid Color 2.001
|
||||
- ~yP Layer Solid Color 12
|
||||
|
||||
## Body
|
||||
- ~yP Layer BodySkin AO:增加身体的细节AO,已增加立体感。
|
||||
- ~yP Layer BodySkin Cavity:使用CavityMap提亮指定区域。
|
||||
- ~yP Layer Solid Color 5.001
|
||||
- ~yP Layer Solid Color 8.001:FakeLighting以增加SSS质感。
|
||||
- ~yP Layer Solid Color 9.002:边缘光效果。
|
||||
- ~yP Layer Solid Color 9.001:dot(World,指定法线) + 2层Ramp实现的一个类似描边的效果来增加质感。
|
||||
|
||||
# workou
|
||||
## 要点
|
||||
- ~yP Layer Skin AO:AO效果
|
||||
- ~yP Layer Blush.003:膝盖与肩膀上的神色区域,模拟了一些SSS效果。
|
||||
- ~yP Layer Fake Lighting:FakeLighting全身效果,模拟了SSS效果。
|
||||
|
||||
## Body
|
||||
- ~yP Layer Skin AO:AO效果
|
||||
- ~yP Layer Solid Color 9:贴图控制的局部照亮效果
|
||||
- ~yP Layer Blush.003:膝盖与肩膀上的神色区域,模拟了一些SSS效果。
|
||||
- ~yP Layer Fake Lighting:FakeLighting全身效果,模拟了SSS效果。
|
||||
- ~yP Layer Fake Lighting 1.001:使用FakeLighting的方式,实现了类似边缘描线的效果。
|
||||
- ~yP Layer Roughness Tweak.002:调整腿部的Roughness。
|
||||
- ~yP Layer Roughness Tweak.003
|
||||
- ~yP Layer Solid Color 2:FakeLighting,调整B的SSS效果。
|
||||
- ~yP Layer Solid Color 7:身上的痣
|
||||
- ~yP Layer Fake Lighting 2.001:胸部的高光点
|
||||
|
||||
## Mirror
|
||||
- ~yP Layer Solid Color 4
|
||||
- ~yP Layer Solid Color 3:腮红
|
||||
|
||||
## Face
|
||||
- ~yP Layer Eye:眼白
|
||||
- ~yP Layer Pupil:虹膜颜色
|
||||
- ~yP Layer Brow:眼睫毛
|
||||
- ~yP Layer Solid Color 8:Unknow
|
||||
- ~yP Layer Pupil Dot:瞳孔
|
||||
- ~yP Layer Solid Color.006:虹膜下方的焦散高光
|
||||
- ~yP Layer Solid Color 1.002:眼睛两侧的眼白
|
||||
- ~yP Layer Solid Color 1.006:眼睛上方的阴影
|
||||
- ~yP Layer Blush:腮红
|
||||
- ~yP Layer Blush.002:第二层腮红
|
||||
- ~yP Layer Nail:Unknow
|
||||
- ~yP Layer Tongue:估计是舌头
|
||||
- ~yP Layer Teeth:调整牙齿区域的亮度
|
||||
- ~yP Layer Roughness Tweak:调整脸部腮红的Roughness
|
||||
- ~yP Layer Roughness Tweak.001
|
||||
- ~yP Layer Fake Lighting 2:侧脸的阴影
|
||||
|
||||
# YangGuiFei
|
||||
## 要点
|
||||
- ~yP Layer Skin AO:身体AO
|
||||
- ~yP Layer Fake Lighting:通过FakeLighting模拟SSS效果。
|
||||
|
||||
## Body
|
||||
- ~yP Layer Skin AO:身体AO
|
||||
- ~yP Layer Fake Lighting:通过FakeLighting模拟SSS效果。
|
||||
- ~yP Layer Fake Lighting 2:大腿上的高光
|
||||
- ~yP Layer Fake Lighting 3.001:大腿边缘光效果
|
||||
|
||||
## Face
|
||||
- ~yP Layer Solid Color 4:唇色
|
||||
- ~yP Layer Solid Color 3:腮红
|
||||
- ~yP Layer Eye:眼白
|
||||
- ~yP Layer Pupil:虹膜颜色
|
||||
- ~yP Layer Brow:睫毛
|
||||
- ~yP Layer Pupil Dot:瞳孔
|
||||
- ~yP Layer Solid Color 6:大概率是眼睛的阴影
|
||||
- ~yP Layer Solid Color.006:虹膜下部的焦散高光效果
|
||||
- ~yP Layer Solid Color 1.002:眼睛上部的阴影
|
||||
- ~yP Layer Solid Color 1.006:眼睛上部的阴影
|
||||
- ~yP Layer Blush:腮红
|
||||
- ~yP Layer Blush.001:第二层腮红
|
||||
- ~yP Layer Nail:Unknow
|
||||
- ~yP Layer Fake Lighting:通过FakeLighting模拟SSS效果。
|
||||
- ~yP Layer Fake Lighting 1.001:Eye相关的FakeLighting,效果未知。
|
||||
- ~yP Layer Fake Lighting 3:脸部边缘光效果。
|
97
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/YusufUmar厚涂手法总结.md
Normal file
97
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/YusufUmar厚涂手法总结.md
Normal file
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: YusufUmar厚涂手法总结
|
||||
date: 2022-08-24 14:29:06
|
||||
excerpt:
|
||||
tags: 卡通渲染
|
||||
rating: ⭐⭐
|
||||
---
|
||||
|
||||
## 前言
|
||||
总结主要以YusufUmar的kama的皮肤材质为主。其他作品作为补充。
|
||||
|
||||
## 皮肤
|
||||
材质模型使用Blender默认的Principed BSDF节点(可以理解为多个BSDF进行混合):
|
||||
- 标准的迪士尼模型,基底材质参数为:皮肤颜色float3(0.846873,0.602632,0.512498)、Roughness 0.3、Metallic 0 。Subsurface:0.01
|
||||
- SSS材质的模型为RandomWalk(Fixed Radius),以0.01~0.005的比例进行混合。
|
||||
|
||||
下图为最终结果->使用默认参数的Principed BSDF Node
|
||||

|
||||
|
||||
### 增加质感
|
||||
1. ~yP Layer Noise为法线增加Noise效果,体现为**皮肤哑光效果**。
|
||||
2. yP Layer Fake Lighting效果,模拟出皮肤的SSS以及环境光照效果,增强厚涂皮肤的质感。
|
||||
3. yP Layer Red Skin (肩部区域与手指)效果,模拟皮肤的SSS效果,增强厚涂皮肤的质感。
|
||||
4. yP Layer Skin AO (皮肤上的AO效果),模拟皮肤上的AO,
|
||||
|
||||
#### Noise
|
||||

|
||||
|
||||
#### FakeLighting
|
||||

|
||||
|
||||
#### RedSkin
|
||||

|
||||
|
||||
#### SkinAO
|
||||

|
||||
|
||||
#### Outline
|
||||
使用一个稍微扩大的模型渲染描边效果。
|
||||
|
||||
## 头发与衣服
|
||||
头发与衣服也使用了多种Ramp效果叠加。
|
||||
|
||||
## 后处理效果
|
||||
|
||||
## 思路转化
|
||||
YusufUmar的思路还是偏向于3D辅助绘制,也就是将一个Shader效果(以GroupNode为单位)作为一个图层,之后层层叠加出效果。效果主要分为:
|
||||
- 原始光照,Blender中灯光、环境光效果。
|
||||
- FakeLighting:通过制定dot(摄像机空间向量,WorldNormal)的方式获取到一个Alpha,之后通过映射成一个ColorRamp再叠加上去。(虽然效果看起来可以,但这么做其实是错误的,但移动端可以使用)。
|
||||
- RedSkin:使用绘制贴图(顶点绘制)的方式制定若干区域与Alpha之后通过映射成一个ColorRamp再叠加上去。
|
||||
- SkinAO:通过制定dot(摄像机空间向量,WorldNormal)的方式获取到一个Alpha,之后通过映射成一个ColorRamp再叠加上去。
|
||||
|
||||
主要的思路就是使用各种数据算出的Alpha来映射ColorRamp,并叠加到角色上。这需要原画美术的支持,来实现在物理正确的基础上**加料**。
|
||||
|
||||
### 光照
|
||||
主光可以UE的ShaderModel中根据全局变量中的DirectionLightVector来判断,进而进行调整(设置主光Ramp)。
|
||||
但有一个问题,**主光的效果如何和其他点光效果平衡?**
|
||||
|
||||
YusufUmar的作品中也会使用FakeLighting(Shader计算+Mask)来实现局部照亮这种额外打光效果。**是否可以通过LightingChannel,在ShaderModel中判断?**
|
||||

|
||||
|
||||
|
||||
因为这种补光的点光只会在特定镜头出现,出了过场动画肯定要去掉。**脑洞:搞成Pose驱动的灯光效果?**
|
||||
|
||||
### FakeSSS效果
|
||||
#### FakeLighting(环境光SSS效果)
|
||||
YusufUmar的方法不精确!比如耳朵以及锁骨区域这些应该比较透的的确却不一点都不透。
|
||||
|
||||
个人认为最好的方法是:
|
||||
- 使用UE的SSS渲染功能的点采样,求出透明度,作为映射参数之一。
|
||||
- 使用一个可调整的曲线Asset作为皮肤的LUT来实现SSS效果。
|
||||
- 或许SSS部分也需要类似**Lambert -> HalfLambert** 的调整?
|
||||
|
||||
#### RedSkin
|
||||
在UE使用Layered Material方式,将不同颜色的SSS材质混合到一起。
|
||||
- 让美术绘制混合用的贴图。
|
||||
- 使用Layered Material进行混合。
|
||||
|
||||
这个绘制类型的Ramp效果多次出现,能很好的提供那种厚涂的效果。
|
||||
|
||||
### AO
|
||||
使用贴图+SSAO。
|
||||
**最好能做到使用AO来偏移SSS效果,而不是进行直接的阴影叠加。**
|
||||
|
||||
### 质感增加
|
||||
**皮肤哑光效果**使用Noise贴图并且使用ddx/ddy实现。
|
||||
|
||||
## 其他改进
|
||||
因为作者能力与Blender的限制,所以没有考虑天光、GI、HalfLambert等因素。所以
|
||||
1. 将ShaderModel的Lambert -> HalfLambert。
|
||||
2.
|
||||
|
||||
美术可能需要调整的相关Ramp:
|
||||
- SSS效果
|
||||
- 主光Ramp
|
||||
- 天光与环境光
|
||||
- AO (只能修改默认参数)
|
34
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/厚涂画师作品分析.md
Normal file
34
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/厚涂画师作品分析.md
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: 厚涂画师作品分析
|
||||
date: 2022-10-13 20:40:47
|
||||
excerpt:
|
||||
tags: 卡通渲染
|
||||
rating: ⭐⭐
|
||||
---
|
||||
|
||||
## 分类标准
|
||||
每个作者的作品区分:
|
||||
- 场景的时间与环境
|
||||
- 室内/室外
|
||||
- 大致时间
|
||||
|
||||
渲染特征使用以下进行评级:
|
||||
- 画面颜色分布
|
||||
- 亮处与暗部的亮度分布
|
||||
- 饱和度分布
|
||||
- 色相
|
||||
- 次表面
|
||||
- 次表面强度
|
||||
- 粉嫩程度
|
||||
- 皮肤高光
|
||||
- 天光
|
||||
- AO
|
||||
- Bloom/Glow
|
||||
- 环境光
|
||||
- 其他绘制
|
||||
- 丝袜
|
||||
- 湿润的皮肤
|
||||
- 后处理
|
||||
- Bloom
|
||||
|
||||
## 画师作品具体分析
|
19
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/厚涂绘画过程笔记.md
Normal file
19
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/厚涂绘画过程笔记.md
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: 厚涂笔记
|
||||
date: 2022-08-10 15:25:48
|
||||
excerpt:
|
||||
tags: 卡通渲染
|
||||
rating: ⭐
|
||||
---
|
||||
|
||||
|
||||
## 厚涂的绘画流程
|
||||
[B站厚涂教程](https://www.bilibili.com/video/BV1PY411J7Af?p=1)的流程
|
||||
1. 绘制基本色块
|
||||
2. 绘制阴影区域(这步完成就有基本的赛璐璐效果了)
|
||||
3. 对阴影区域进行涂抹(方向为暗->亮)以及模糊处理,使之具有比较自然的过度效果。 
|
||||
4. 绘制第二层光影细节
|
||||
5. 使用喷枪工具给皮肤添加腮红效果,部分阴影区域也可以使用之前绘制的Mask进行饱和度与颜色调节。
|
||||
6. 添加头发以及眼睛细节
|
||||
7. 添加天光、反射、其他灯光效果以及物体高光,来凸显质感与灯光气氛。
|
||||
8. 最后添加细节
|
155
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/厚涂风格实时二次元渲染(0)-前言.md
Normal file
155
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/厚涂风格实时二次元渲染(0)-前言.md
Normal file
@@ -0,0 +1,155 @@
|
||||
由简单的二值化到厚涂二渲染
|
||||
|
||||
|
||||
|
||||
**NPR**作为一个视觉效果导向的渲染方向,其实是有着很强的商业化前景。因为对于真实感的要求不高,只是“看上去好看就可以”,所以很多时候复杂的物理现象可以直接绕过,或是使用很快但效果并不好近似折衷。在NPR中由于绝大部分是非真实感的渲染,大多数时候对一个提供真实感补正的物理现象具体表现效果要求并不高。因此在性能开销方面以及移动端方面都有着很强的应用前景。
|
||||
|
||||
NPR的大部分效果其实对于TA的物理数学功底要求并不算很高 ,那么究竟是对什么要求高呢?答案是:**审美**。可以说既然你要做NPR,至少要对要做的NPR的艺术领域有足够高的热爱和理解。比如你要做二次元风格的渲染的话,就要看过各种各样画风的动漫,对于哪种画风更受喜爱也要有所了解,自己也要清楚自己喜欢什么画风,也要清楚这种画风的优势所在。另外,即使是各种非真实感的画风,通常各种效果也是基于物理现象的夸张化而已,都能够找到物理现象的本源,一般来说,将相应的物理现象在PBR的实现方法进行各种套路魔改,就可以实现相应的在NPR里面的效果。
|
||||
|
||||
二次元渲染模型和写实向渲染模型另外还有一个自然而然的区别,即写实向渲染通常是为实现一个效果而使用不同的物理方法,二次元渲染模型则通常实现不同的效果而使用不同的套路,因为真实感是亘古不变的,但“好看”的定义却大有不同。常为我们所接受的米哈游《崩坏3》、《原神》、《星穹铁道》中都是使用了“赛璐璐风格二次元渲染”,其表现为“二值化”,即:将亮部与暗部使用离散的方式分割,以得到扁平化的效果。
|
||||
|
||||

|
||||
|
||||
原神中的角色刻晴
|
||||
|
||||
但其实二值化也未必只有两个值,只要体现了**离散**特性的光照模型都可以称之为二值化,是将一个光照阶段进行了二值化。动漫中其实也会出现高光部,来表现一个表面凸出且反射度较高的特性(例如兄贵光滑的肌肉)
|
||||
|
||||

|
||||
|
||||
引用:https://zhuanlan.zhihu.com/p/437313927
|
||||
|
||||
二值化通常的做法其实就是一个条件判断,将N dot L 的值与阈值判断,高于阈值的呈现亮部特点,低于阈值的呈现暗部特点。但是,现在的做法通常是将条件判断做成了一个映射Map,称作**Ramp Map**,如果要用RampMap实现常规的二值化,只需将RampMap做成只有两个颜色的就可以。如果要做三值化,只需要做成三个颜色。甚至如果要用常规的兰伯特模型,只需要做成黑到白的渐变,就相当于没有进行这次映射。RampMap比起条件判断是更加灵活的。
|
||||
|
||||

|
||||
|
||||
游戏《嗜血代码》中野体现了一定的二值化特性,但是阴影处相对更加柔和,这只需要你在二值化的RampMap跳变处做一个小小的渐变就可以实现。
|
||||
|
||||
|
||||
|
||||
赛璐璐风格作为日式二次元动漫最常见的画风,其实是相对最为人接受的。《原神》的爆火也证明了它的可行性。它的优势是显而易见的。
|
||||
|
||||
1.渲染成本较低(简单的光照模型,自然会比真实向渲染的开销更低)
|
||||
|
||||
2.下限高,不会出现太穿帮的渲染(二值化,非你即我,十分可控)
|
||||
|
||||
3.普遍接受度高(简单的画风往往不至于令人厌恶)
|
||||
|
||||
|
||||
|
||||
但是,很少有人分析赛璐璐风格的缺点,他的缺点其实也是存在的
|
||||
|
||||
1.上限较低
|
||||
|
||||
在玩家口味普遍刁钻的现在,简单的二值化渲染有时不能满足他们对细腻画风的追求,《幻塔》和《崩坏3》的早期角色就是例子。
|
||||
|
||||

|
||||
|
||||
《幻塔》的着色
|
||||
|
||||

|
||||
|
||||
冰八
|
||||
|
||||
冰巴刚出那时候,大家对手机游戏角色的要求并不高。可以看到幻塔的角色大概和冰巴精细度差不多,但幻塔的美术就被吐槽了。我个人认为是时代发展的原因。再来看看崩坏3的后续角色
|
||||
|
||||

|
||||
|
||||
艾莉希亚
|
||||
|
||||
这其中有一个十分明显的变化,即模型的精细程度。
|
||||
|
||||

|
||||
|
||||
大面积的白色
|
||||
|
||||

|
||||
|
||||
大面积的单调肉色
|
||||
|
||||

|
||||
|
||||
不太负责任的直接上一个渐变色的袜子
|
||||
|
||||
当模型细节堆砌的不够多,简单的二值化就会显得效果过于简单,会产生一定的粗糙感。因此,二值化对角色设计有要求:即细节必须足够多,也就是说角色必须佩戴足够多的饰品,衣服上必须有足够多的花纹,这其实是对角色设计的一种限制。
|
||||
|
||||
与艾莉希亚相同,《原神》也是通过这种取巧的手段来实现在二值化下留白而不给人带来太多粗糙感的。
|
||||
|
||||

|
||||
|
||||
提纳里身上各种各样的饰品,白色绸缎上华丽的花纹
|
||||
|
||||

|
||||
|
||||
十分复杂的细节
|
||||
|
||||

|
||||
|
||||
就算是做个黑丝也要往上面加星星避免单调感
|
||||
|
||||
在《原神》中,你无法找到任何一个角色是简单的,这无疑会对角色设计带来更高的验收要求和限制。
|
||||
|
||||
即使赛璐璐风格渲染有着一定的局限性,它依然是二次元渲染的必修课,
|
||||
|
||||
非常推荐知乎大佬flashyiyi的文章
|
||||
|
||||
[到目前为止的二次元渲染总结 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/126668414)
|
||||
|
||||
[一些较少人提过的二次元渲染方法 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/539950545)
|
||||
|
||||
在第一篇文章中,他提到:
|
||||
|
||||

|
||||
|
||||
实际上,pixiv上有很多传统二值化的绘画作品,但是在热门榜上经常霸榜的画风却往往是绘画更加细腻的风格。
|
||||
|
||||

|
||||
|
||||
一张曾经在热门栏的原神插画
|
||||
|
||||
米哈游也在积极探索在NPR下更细腻的渲染表现,比如
|
||||
|
||||

|
||||
|
||||
米哈游的《桃源恋歌》中的八重樱
|
||||
|
||||

|
||||
|
||||
可爱的鹿鸣
|
||||
|
||||
但也都并不能称得上是完美的“二次元渲染”
|
||||
|
||||
《桃源恋歌》中的三层Ramp手段,使得肉体不仅具有渐变的特性,也保留了跳变的特性,但由于时代较早,这个ramp我觉得不算特别好看。另外头部和身体的渲染区别有点太大,有点像是“接头霸王”
|
||||
|
||||
而《鹿鸣》更像是个邻家小妹,皮肤的次表面散射材质确实Q弹,以及各种高技术力的唯美表现,但是这个不够二次元……在某些时候给人一种既真实又虚假的“恐怖谷”的感觉。(这个只是我个人的感受,因人而异,希望大家不要喷我)
|
||||
|
||||

|
||||
|
||||
高能手办团
|
||||
|
||||
游戏《高能手办团》中,探索插画风格渲染的方式是直接使用了PBR,然后在头发等部位故意使用塑料的材质,做出手办的感觉,其实直接使用PBR在日厂的二次元作品是比较常见的。
|
||||
|
||||

|
||||
|
||||
PBR VS NPR 各有千秋
|
||||
|
||||
PBR给人的感觉是更加细腻,而NPR给人的感觉是更加二次元。
|
||||
|
||||
那么在PBR与NPR之间我们如何找到一种折衷?实际上pixiv已经给了我们一种答案:厚涂。
|
||||
|
||||
厚涂的角色由于具有一定的真实感物理特性,使得它在无论真实感的场景下还是在二次元感的渲染场景下,都不会显得过于违和。大面积的肉色堆叠,由于能够看到肌肤的明暗变化,也不会让人觉得粗糙和单调。同时,作为新兴的二次元画风:厚涂,也不至于会让人觉得不够“二次元”。
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
厚涂风格的玛修同学
|
||||
|
||||
|
||||
|
||||
本专题将会分享我在厚涂二次元渲染风格探索中的心得体会~从人物的各种部位到场景的各种材质,只要有时间并且有相关研究成果就会分享出来~
|
||||
|
||||
|
||||
|
||||
赛璐璐风格的二次元渲染套路(例如面部法线修正),以及一些简单常用的PBR套路(例如次表面散射模型),是本专题的前置知识,不过不用担心,用到前置知识的地方我会进行资料的引用。
|
||||
|
||||
求三连~
|
251
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/厚涂风格实时二次元渲染(2)-厚涂风的皮肤渲染.md
Normal file
251
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/厚涂风格实时二次元渲染(2)-厚涂风的皮肤渲染.md
Normal file
@@ -0,0 +1,251 @@
|
||||
注意:本专题也许会大面积展示人体皮肤渲染结果,这可能会引起你的不适。
|
||||
|
||||
### 0.准备工作
|
||||
|
||||
在专题开始之前,你需要完成二值化的光照模型以及outline
|
||||
|
||||
[【01】从零开始的卡通渲染-描边篇 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/109101851)
|
||||
|
||||
[【02】从零开始的卡通渲染-着色篇1 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/110025903)
|
||||
|
||||
可以看这位大佬的二值化渲染模型。
|
||||
|
||||
另外,如果对赛璐璐风格的二次元渲染仍存疑惑,我的建议是
|
||||
|
||||
[RealToon (An Anime/Toon Shader) | VFX 着色器 | Unity Asset Store](https://link.zhihu.com/?target=https%3A//assetstore.unity.com/packages/vfx/shaders/realtoon-an-anime-toon-shader-65518)
|
||||
|
||||

|
||||
|
||||
realtoon效果
|
||||
|
||||
这个18刀的着色器包含了完整源码,还内置了对实时光线追踪的支持,18刀的学费不算贵喔。
|
||||
|
||||
光追二次元的内容放到第九篇说吧,这里就不展开了。
|
||||
|
||||
### 1.皮肤的平滑光照
|
||||
|
||||
显而易见,插画风格与赛璐璐风格最大的区别就在于,皮肤对于不同的光照情况有着不同的表现。
|
||||
|
||||

|
||||
|
||||
原神的插画
|
||||
|
||||

|
||||
|
||||
原神正作渲染结果
|
||||
|
||||
我们对皮肤颜色进行抓取
|
||||
|
||||

|
||||
|
||||
原神插画的皮肤颜色
|
||||
|
||||

|
||||
|
||||
原神渲染的皮肤颜色
|
||||
|
||||
可以看到,原神的插画相比原神游戏中的渲染结果,其色彩能够展示出一定的PBR特点,例如Specular高光,还有次表面散射的效果。不过,画面整体依然很二次元,因此我们是可以从赛璐璐风格着色器的基础上进行魔改得到我们期待的厚涂风格皮肤的。
|
||||
|
||||
因此,我们在原本赛璐璐风格的渲染基础上直接引入Specular高光和次表面散射。与PBR不同的是:
|
||||
|
||||
1.我们不必对其物理真实性太过较真,只要他能对我们的画面进行补正就是好文明。
|
||||
|
||||
2.保证所有效果可以通过参数调整权重,因为NPR保证视觉好看是第一要务。
|
||||
|
||||
因此,Specular可以简单用 SpecularColor∗SpecularIndensity∗dot(normal,lightDir) 实现,也可以用 SpecularColor∗SpecularIndensity∗dot(normal,viewDir) ,我个人倾向于后者。
|
||||
|
||||
其中, SpecularColor 是Color, SpecularIndensity 是灰度图。
|
||||
|
||||
实现。当然,如果物体的SpecularColor会因部位而异,那么可以和 SpecularIndensity 一起合并成一个含颜色的贴图。
|
||||
|
||||
次表面散射方面,可以考虑使用pre-integrated模型。
|
||||
|
||||

|
||||
|
||||
预积分的次表面散射
|
||||
|
||||
BSSRDF与BTDF,以及pre-integrated模型的学习: [三鳥:实时皮肤渲染综述](https://zhuanlan.zhihu.com/p/462124664)
|
||||
|
||||
简单的说一下BSSRDF。
|
||||
|
||||

|
||||
|
||||
射入皮肤的光呈现漫反射的性质
|
||||
|
||||
当光照射到皮肤表面后,光会在皮肤下的油脂层进行多次散射,在它射出时,它体现漫反射的性质。不过,之所以他体现出漫反射的性质,是因为光射入皮肤后,多次散射会导致最终值差距较大,因此在皮肤下的油脂层中存在着各种方向混乱的散射,最终射出的光就会呈现漫反射的性质了。
|
||||
|
||||

|
||||
|
||||
BSSRDF导致的结果
|
||||
|
||||
这就导致光束大概照到人体表面是这个样子的。那么如何得到这个预积分图呢?除了上面文章中提到的screen space blur的办法,也可以使用基于拟合函数的方法。
|
||||
|
||||
[【译 】Disney2015-将BRDF扩展至集成次表面散射的BSDF - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/345518461)
|
||||
|
||||
这两种方法,都可以得到一个类似下图的渲染结果。
|
||||
|
||||

|
||||
|
||||
可爱的光圈
|
||||
|
||||
然后在渲染结果上以以1r采样,水平方向上以**受光强度**N⋅L采样,就可以得到预积分图。
|
||||
|
||||
如果你实在整不明白,你直接把别人的预积分结果拿去用也无所谓……反正大学时期大家应该也都抄过别人的**作业**吧。
|
||||
|
||||

|
||||
|
||||
在我们得到预积分图后,我们就可以考虑如何使用。
|
||||
|
||||

|
||||
|
||||
预积分贴图
|
||||
|
||||
常见的pre-integrated模型刚好是以 N·L 为横坐标的,这与我们的RampMap一致。此时此刻我们的赛璐璐风格RampMap应该还是一个1N大小的LUT,将这个一维LUT与pre-integrated二维LUT直接逐行相乘,得到一个新的RampMap,但是1N的RampMap拓展为了N*N,纵坐标的1/r是在球面上的,如何应用到我们的复杂网格渲染中呢?
|
||||
|
||||
请参考[Pre-Integrated Skin Shading 的常见问题解答 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/384541607) Q&A第五节
|
||||
|
||||

|
||||
|
||||
Jeffrey Zhuang的Q&A
|
||||
|
||||
因此,将 ΔNormal/ΔPosition 作为纵坐标就可以了(笑)。
|
||||
|
||||
仅仅是换一张图,就解决了皮肤的次表面散射问题。
|
||||
|
||||
至此,皮肤的平滑光照问题就已经解决。
|
||||
|
||||

|
||||
|
||||
赛璐璐Only
|
||||
|
||||

|
||||
|
||||
加入了BSSRDF和Specular后的效果
|
||||
|
||||
(这里头发和五官的渲染已经是完结版本,所以有种降维打击的感觉)
|
||||
|
||||
可以看到,在加入SSS和Specular之后,我们的皮肤已经有一股插画的感觉了,并且开销并不算很高。
|
||||
|
||||
求求审核哥哥姐姐了,给个过吧,真的很需要展示皮肤各个角度的渲染效果……
|
||||
|
||||
### 2.皮肤的厚涂化表现
|
||||
|
||||
实际上到此为止的皮肤效果就已经不错了,但是不够二次元了,因为我们上述的操作都是PBR的,真实感太高有时候也不是一件好事。举个例子,在下一篇我会谈到一个叫做face normal fix的操作,这会导致脸很平面。如果你的肉体立体感太足的化,就会显得你是一个**接头霸王**。
|
||||
|
||||
**实际上,在绘画过程中,画师不仅仅要考虑到皮肤的真实感,还要考虑到画面整体的协调性。**
|
||||
|
||||
假如我们使用了基于物理的次表面散射,这会让我们的皮肤在**高强度的直射光**下的物理表现更为明显。但是与此同时,在反射光下的影响就会变得更弱。
|
||||
|
||||
**注意**!下述方法论非常**不**基于物理,这可能会引起你的不适!
|
||||
|
||||
为了让皮肤的质感更为可控,我们采用第二章N*N的LUT,但这张LUT我们不基于物理去生成,而是跟着**感觉**走
|
||||
|
||||
实际上,我们需要一个散射光强度与直射光相补正,而这个光就是天空光。
|
||||
|
||||
1.天空光是蓝色的,所以我们考虑让皮肤看上去有蓝色的散射感。
|
||||
|
||||
2.与此同时,在皮肤边缘,我希望有一个体现BTDF的效果,在二次元下,我希望这个颜色是粉粉的。
|
||||
|
||||
3.散射感在直射光射到的地方肯定会体现的不充足,在直射光射不到的地方才会比较多。
|
||||
|
||||
4.第二条说的BTDF肯定也得是和光照强度绑定的呀!
|
||||
|
||||
综上四条,我们下一步控制皮肤质感的LUT,必然有一维是 N·L ,用以表示直射强度。考虑到皮肤边缘需要BTDF,另一个维度应该是 N·V 。
|
||||
|
||||
这样的话我们依然需要LUT,只是这次的LUT不需要积分(毕竟也不基于物理),
|
||||
|
||||
直接凭感觉随便拿PS什么的画一下就好了。
|
||||
|
||||
这个补正方法的灵感来源于一个效果很好的Fake SSS的着色:
|
||||
|
||||
[Shader Forge - A visual, node-based shader editor | Page 54 - Unity Forum](https://link.zhihu.com/?target=https%3A//forum.unity.com/threads/shader-forge-a-visual-node-based-shader-editor.222049/page-54%23post-1903768)
|
||||
|
||||

|
||||
|
||||
二次元可以使用的LUT
|
||||
|
||||
以天空为背景色,直射光使用肤色作为相应,然后边缘使用粉粉嫩嫩的颜色。
|
||||
|
||||
至于这块儿棕色,因为我们是半兰伯特模型,所以一般而言不会采样到太多。
|
||||
|
||||
最后一步就是厚涂化了,我们回首再对我们的厚涂做一点考察
|
||||
|
||||

|
||||
|
||||
厚涂玛修
|
||||
|
||||
|
||||
|
||||
厚涂是发源于**西方油画**的一种绘画技法。 画家在画布上使用很厚的颜料,利用笔刷或画刀进行涂抹,就像抹奶油一样,故名“厚涂”
|
||||
|
||||
在这样的绘画工艺下,导致厚涂色块十分分明。因为是用很厚的颜料,所以渐变实际上是离散的,这就和我们的赛璐璐有一点像了。
|
||||
|
||||
离散的同时,由于两个颜料的相互融合,边界却又十分的柔和
|
||||
|
||||

|
||||
|
||||
柔和的边界
|
||||
|
||||
我们直接对上面的补正LUT做处理就好了。先downsampling,再用朴素的方式放大,然后用一下小半径的均值滤波,就可以得到一张有厚涂特色的补正LUT了
|
||||
|
||||
把这张LUT和上面pre-integrated以相同的方式(但纵坐标轴不同)也应用进去,就完成90%的效果啦
|
||||
|
||||

|
||||
|
||||
可以看到这时候效果已经很不错了。
|
||||
|
||||
|
||||
|
||||
### 3.一点小方法
|
||||
|
||||
(1)另类高光
|
||||
|
||||

|
||||
|
||||
肩头上有高光
|
||||
|
||||
可以看到有些二次元厚涂作品有明显不基于物理的高光,这是因为二次元美少女每一个都滑溜溜的。
|
||||
|
||||
这个效果在我们使用了不基于物理的补正LUT后非常简单,在LUT的左上角点一个光点就可以了。
|
||||
|
||||
效果:
|
||||
|
||||
|
||||
|
||||
(2)屏幕空间补光
|
||||
|
||||

|
||||
|
||||
肩头存在补光
|
||||
|
||||
二次元角色的光照实际上都是以好看为第一要务的。其实许多动漫都存在“两个人面对面,但是却都向光”的情况。对于二次元美少女而言,光补的越多,就越好看(雾)
|
||||
|
||||
[Unity URP Shader 与 HLSL 自学笔记六 等宽屏幕空间边缘光 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/365339160)
|
||||
|
||||
可以在这篇文章学习这个套路。因为我也是学的别人的,就不在这里班门弄斧啦。
|
||||
|
||||

|
||||
|
||||
补光后的效果,会让人感觉更有绘画感。
|
||||
|
||||
此外,将这个效果复制一份并以 normal·viewDir lerp一下,可以做出BDTF的效果
|
||||
|
||||

|
||||
|
||||
### 4.最终效果预览
|
||||
|
||||
最终渲染结果
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
为了保证气氛一致,角色受环境光的影响很大
|
||||
|
||||

|
||||
|
||||
最终的渲染效果之看看你的
|
||||
|
||||
感谢大家的学习!下一期会更新对角色面部的处理!
|
||||
|
||||
渲染群群号:817341015
|
168
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/厚涂风格渲染文档.md
Normal file
168
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/厚涂风格渲染文档.md
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
title: 厚涂风格渲染文档
|
||||
date: 2022-09-13 15:25:32
|
||||
excerpt: 摘要
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
# 厚涂元素分析
|
||||
|
||||
# 原理说明
|
||||
## Material
|
||||
>与ToonStandard、ToonHair不同,Roughness与Metallic的作用恢复成UE默认的逻辑。
|
||||
这个材质可以理解为在一个Lambert材质上叠上了一层层的效果。
|
||||
|
||||
- BaseColor:基础颜色贴图
|
||||
- Metallic:控制反射强度。
|
||||
- Roughness:控制高光形状
|
||||
- Specular:控制高光强度。
|
||||
- Opacity:具体参考[[#Material#Opacity]]
|
||||
- ShadowSideColor(ToonSkin为SubsurfaceColor):次表面颜色贴图,用于控制身体各部位的次表面颜色。比如嘴唇、手指等部位因为毛细血管较多会显得比较红。美术需要根据需求进行绘制。
|
||||
- ToonDataA:
|
||||
- R:ShadowOffset,用于偏移赛璐璐的光影分界面位置。 ToonSkin未使用。
|
||||
- G:用于指定ToonDataID,与ToonWorldSettings中ToonData的Index对应。
|
||||
- B:为天光渲染结果与ShadowSideColor的混合因子。ToonSkin未使用。
|
||||
- A:未使用
|
||||
- ToonDataB:
|
||||
- RGB:OutlineColor
|
||||
- A:OutlineMask
|
||||
- ToonDataC:
|
||||
- RGB:描边用的IDTexture
|
||||
- A:OutlineWidth
|
||||
|
||||
ToonCustomOutput:
|
||||
这3个参数与间接照明有关(体积光照贴图)因为没有对应场景所以没办法进行测试。
|
||||
- InSubsurfaceColorForIndirect:修改次表面颜色对于DiffuseColorForIndirect的影响,默认为0,也就是不影响。
|
||||
- InDiffuseIndirectLightingMask:漫反射间接照明Mask
|
||||
- InSubsurfaceIndirectLightingMask:次表面间接照明Mask
|
||||
|
||||
### Opacity
|
||||
该贴图用于控制次表面的Transmission所使用的的预积分Ramp(值越大次表面颜色会越明显)。使用Marmoset Toolbag烘焙出曲率贴图Curvate、厚薄度贴图Thickness为基础进行辅助绘制最终得到Opacity贴图。
|
||||
|
||||
我们需要对贴图进行Clamp,去除较低的颜色(Thickness选择大腿处的颜色为最低颜色),之后将其重映射。
|
||||

|
||||
Curvate贴图我们只需要0.5~1部分的数据,所以在Clamp(0.5,1)之后再将其重映射。
|
||||

|
||||

|
||||
|
||||
## ToonWorldSettings说明
|
||||
为了解决延迟渲染不能传递大量参数的问题,本人设计这种通过ID查询对应全局贴图来获取数据的方法。对应的参数在ToonRenderingWorldSettings中设置并且绘制即可。
|
||||
|
||||
ToonData目前只有2个参数是有效果:
|
||||
- FalloffRampCurve:控制光影过渡曲线。
|
||||
- BloomMultiplier:控制指定部位的“辉光”效果的强度。
|
||||
|
||||
ToonSkinData:
|
||||
- FalloffRampCurve:控制灯光光照的光影过渡曲线。置空则使用默认的**Lambert**衰竭曲线进行计算。
|
||||
- TransmissionRampCurve:控制灯光光照的次表面效果的渐变曲线。置空则使用**1-Lambert** 衰竭曲线进行计算。
|
||||
- SkyLight
|
||||
- SubsurfaceColorAlphaForSkyLight:控制天光产生的SSS效果。
|
||||
- SubsurfaceColorOpacityMultiplier:控制Opacity对于天光SSS的影响强度。
|
||||
- IBL参考[[#ReflectionEnvironmentAndSky#IBL]]
|
||||
- IBLMultiplier:HDR贴图反射效果强度。
|
||||
- IBLMultiplierRangeByLuminanceMax:
|
||||
- IBLMultiplierRangeSmooth:HDR贴图反射过渡效果。
|
||||
- IBLMultiplierRangeAOMask:AO对于HDR贴图反射的影响倍率。
|
||||
- BloomMultiplier:控制指定部位的“辉光”效果的强度。
|
||||
|
||||
### 全局贴图说明
|
||||
这些贴图都存放在`Engine\Content\EngineMaterials\ToonTexture`中。为了保证效果需要确保这些贴图的CompressionSettings为`VectorDisplacementmap(RGBA8)`。其中ToonDataTexture与ToonSkinData贴图的Filter设置成`Nearest`。
|
||||
|
||||
- ToonStandard与ToonHair相关:
|
||||
- ToonRampTexture:ToonStandard与ToonHair的存储调整阴影分界面偏移与软硬度Ramp数据的贴图,通过ID来采样指定的行。
|
||||
- ToonDataTexture :ToonStandard与ToonHair的存储Toon数据贴图,通过ID来采样指定的行。
|
||||
- ToonSkin相关:
|
||||
- PreIntegratedToonSkinBRDFTexture :ToonSkin专用的PreIntegrated贴图。
|
||||
- ToonSkinFalloffMaskTexture :ToonSkin专用的存储调整阴影分界面偏移与软硬度Ramp数据的贴图,通过ID来采样指定的行。
|
||||
- ToonSkinTransmissionMaskTexture :ToonSkin用于调整Transmission Alpha的贴图,通过ID来采样指定的行。
|
||||
- ToonSkinDataTexture:ToonSkin的存储Toon数据贴图,通过ID来采样指定的行。
|
||||
|
||||
## 渲染功能
|
||||
### BasePass
|
||||
|
||||
### DiffuseIndirectAndAO
|
||||
- SSGI:建议单帧画面开启。
|
||||
开启SSGI:
|
||||
.png)
|
||||
关闭SSGI:
|
||||
.png)
|
||||
|
||||
### Lighting
|
||||
- ShadeModel ToonSkin
|
||||
- 预积分实现。
|
||||
|
||||
### ReflectionEnvironmentAndSky
|
||||
- 天光需要为Dynamic并且开启Shadow选项。
|
||||
- 是否开启距离场对会让天光产生AO效果.理论上开启距离场AO后的结果才是正确的,但效果并不理想,所以需要调整一下天光下半球的颜色,也可以作为添加细节的方法(最好还是通过HDR贴图)
|
||||
|
||||
半球0灰度:
|
||||
.png)
|
||||
半球20灰度:
|
||||
.png)
|
||||
|
||||
天光对于SSS效果影响,提升其SSS效果也可以使用ToonRenderingWorldSettings 的SubsurfaceColorAlphaForSkyLight与SubsurfaceColorOpacityMultiplier。
|
||||
关闭天光:
|
||||

|
||||
开启天光:
|
||||

|
||||
|
||||
#### IBL
|
||||
主要是为了实现类似sakimichan的部分画作有这种天光产生的效果:
|
||||

|
||||

|
||||

|
||||
这样的效果一般可以通过打光来实现,下图是拍摄于5~6月 早上7:20~7:40分。可以看到与上图类似的冷暖打光效果。
|
||||

|
||||
|
||||
经过若干尝试,最后使用像素亮度来判断暗部与亮度Mask。最后使用ToonRenderingWorldSettings 中的IBL相关属性来控制效果。
|
||||

|
||||
|
||||
这里用了一个0.01的Smooth以及5的Multiper:实际上不会那么明显。
|
||||

|
||||
|
||||
其他游戏比如破晓传说也使用IBL来进行辅助打光。
|
||||
|
||||
将包括辅助光在内的灯光烘烤成立方体地图(制作光照环境HDR贴图)
|
||||
- 旋转以配合光源的光轴
|
||||
- 将光面与光源的方向对齐
|
||||
- 有方向性的正确遮蔽
|
||||
- 亮度和对比度:艺术家调整的
|
||||
- 皮肤和眼睛:对比度低
|
||||
- 在UE4中,这可以很容易地在编辑器中改变。
|
||||

|
||||
|
||||

|
||||
|
||||
### 后处理
|
||||
相关的选项位于后处理盒子-RenderingFeatures-Toon中。
|
||||
|
||||
#### ToonBloom
|
||||
只有一个UseToonBloom用于开启/关闭ToonBloom效果。其余的参数与自带的Bloom通用。与自带的Bloom的区别在于可以通过ToonData来控制Bloom强度,以此来实现一些降低某一些区域亮度很高的区域(自发光)的Bloom效果,不会出现过曝情况。反之也可以使得一些不太量的地方拥有较大的Bloom效果。
|
||||
|
||||
因为使用PBR的方式来处理Bloom,很容易导致泛光部分发白。而我们需要的其实是一个带颜色的光晕。
|
||||

|
||||
|
||||
#### SNN
|
||||
油画效果。实现是的是一个近弱远强的油画效果。
|
||||
- SNNPower:用于开关SNN效果。
|
||||
- SNNSampleRadius:SNN的采样半径(远处)。采样半径越大油画效果越强。
|
||||
- SNNMinSampleRadius:SNN的近距离插值的最小采样半径。0即为近距离不采样。
|
||||
- SNNStartInterpolateDistance:SNN强度开始插值的距离
|
||||
- SNNEndInterpolateDistance:SNN强度结束插值的距离
|
||||
|
||||
## 用户使用说明
|
||||
### 前置步骤
|
||||
1. 在插件管理器确认UTToonRendering插件已经启用,并在ProjectSettings->Engine->GeneralSettings->DefaultClasses中,将WorldSettingsClass设置成`ToonRenderingWorldSettings`。
|
||||
2. 检查天光是否为Dynamic,以及是否开启Shadow,同时调节Lower Hemisphere Is Solid Color,让其稍微亮一些。如果使用距离场阴影,那角色需要有胶囊体,场景也需要构建较为准确的距离场
|
||||
|
||||
### 角色制作顺序
|
||||
1. 绘制贴图 (建议使用Kama的材质)
|
||||
1. 除了基础的BaseColor、Metallic、Roughness、Specular、AO贴图外,SubsurfaceColor与Opacity尤为重要。
|
||||
2. 根据需求来绘制ToonDataB的OutlineColor、IDMapForOutline与OutlineMask贴图。
|
||||
2. 在ToonRenderingWorldSettings中设置新的ToonSkinData,并且在材质中修改ToonDataID使其对应。
|
||||
1. FalloffRampCurve与TransmissionRampCurve比较重要。
|
||||
2. 调节SubsurfaceColorAlphaForSkyLight 与SubsurfaceColorOpacityMultiplier 。
|
||||
3. 适当调节后处理。
|
||||
|
||||
### 虚拟拍摄问题
|
||||
在Project - Engine - Rendering - PostProcessing 中将Enable Alpha Channel Support in PostProcessing 设置成`Allow Throught Tonemapper`。默认是`Disable`。
|
1072
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/厚涂风格研究与开发笔记.md
Normal file
1072
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/厚涂风格研究与开发笔记.md
Normal file
File diff suppressed because it is too large
Load Diff
512
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/知乎FlashYiYi的卡通渲染分享.md
Normal file
512
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/知乎FlashYiYi的卡通渲染分享.md
Normal file
@@ -0,0 +1,512 @@
|
||||
---
|
||||
title: 知乎FlashYiYi的卡通渲染分享
|
||||
date: 2022-10-12 14:43:12
|
||||
excerpt:
|
||||
tags: 卡通渲染
|
||||
rating: ⭐⭐
|
||||
---
|
||||
## 目录
|
||||
- [[#一些较少人提过的二次元渲染方法]]:https://zhuanlan.zhihu.com/p/539950545
|
||||
- [[#低成本皮肤渲染 Pre-integrated Skin]]:https://zhuanlan.zhihu.com/p/35628106
|
||||
- [[#卡通风格场景的定制化技术部分]]:https://zhuanlan.zhihu.com/p/338334377
|
||||
- [[#PBR光照体系下的卡通渲染光照模型]]:https://zhuanlan.zhihu.com/p/166147653
|
||||
|
||||
## 低成本皮肤渲染 Pre-integrated Skin
|
||||
GPU Gem3的预积分文章:https://developer.nvidia.com/gpugems/gpugems3/part-iii-rendering/chapter-14-advanced-techniques-realistic-real-time-skin
|
||||
|
||||
曲率烘焙代码(Unity):
|
||||
```c++
|
||||
v2f vert(appdata_full v)
|
||||
{
|
||||
v2f o;
|
||||
o.uv = fmod(v.texcoord.xy, 1);
|
||||
o.pos = float4(o.uv * 2 - 1, 0, 1);
|
||||
o.pos.y = -o.pos.y;
|
||||
o.vertex = v.vertex.xyz;
|
||||
o.normal = v.normal;
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target
|
||||
{
|
||||
float3 worldPos = i.vertex;
|
||||
float3 worldBump = normalize(i.normal);
|
||||
float cuv = length(fwidth(worldBump)) / length(fwidth(worldPos)) / 10000 * _CurveFactor;
|
||||
return fixed4(cuv, cuv, cuv,1);
|
||||
}
|
||||
```
|
||||
|
||||
## PBR光照体系下的卡通渲染光照模型
|
||||
### 处理偏色
|
||||
所有HDR的卡通渲染游戏都会遇到这个问题:ToneMaping以后饱和度变低了。更糟糕的是纹理的对比度也降低了,导致图片丢失了大量细节,甚至连嘴都看不到了。
|
||||
通常只能在贴图上反向增加饱和度和对比度,但非常不直观,而且精度损失严重。饱和度可以后处理加回去,对比度可没辙。
|
||||
我用了个自己的骚方法:拟合ACES公式的逆运算。
|
||||
|
||||
> color * (2.51 * color + 0.03)) / (color * (2.43 * color + 0.59) + 0.14
|
||||
|
||||
这是ACES的一个简化公式,只保证了基本亮度。我用Excel拟合了它的逆运算,结果如下:
|
||||
> 3.4475 * color * color * color - 2.7866 * color * color + 1.2281 * color - 0.0056
|
||||
|
||||
不准确但是差不多了。然后用它来处理传入的固有色贴图,通过和原本的贴图颜色lerp选择一个合适的混合比例,就可以在最终显示上还原出和原贴图几乎一样的结果。
|
||||
|
||||
它的缺点是同样参数在暗处会显得对比度有些过高,想做的完美还需要根据光照强度来调整lerp的比例(注意是光照强度而不是最终辐射度,因为我们要保证是的纹理内容的对比度,乘过纹理后就没法得知这个原始的对比度了)
|
||||
|
||||
如果可以回避偏色问题,那么绘制贴图的时候就可以直接采用原画的结果。和PBR不同,卡通渲染是个极度依赖原画能力的课题,你没有固定的材质库可用,也没有什么预定参数,决定结果美观的就是贴图的质量,出现偏色是致命的。
|
||||
|
||||
而且暗部对比过高的现象也证明了,仅仅靠改贴图颜色无法解决这个问题。
|
||||
|
||||
而且在ACES下好看的材质纹理,对比度需要非常高,也非常难看,一般美术很难直接画或者选出符合要求的颜色,大家都是在用色板的经验数值取色,实际操作根本不可能做到符合ACES的偏色要求。
|
||||
|
||||
所以这可能是本文最有价值的东西。
|
||||
|
||||
当然,更聪明的做法是根本不用ACES,甚至不用ToneMaping。一个固定亮度,而且没有室内外切换的卡通渲染游戏其实根本不需要ToneMaping,一切都可以原样画出来,毕竟物理光照规则对它来讲并没有意义。
|
||||
|
||||
严格来讲,甚至不需要线性空间。在卡通渲染下,实际上伽马空间的光照结果更加美观。否则PS也不会用伽马作为图层的混合方式了。
|
||||
|
||||
但是如果你还打算用PBR来节约画师的工作量,还希望借用PBR的那些“好东西”,就需要保留ToneMaping和正确的物理光照模型。现在的修正偏色方案,就是个折中的方法。
|
||||
|
||||
### 自定义Bloom
|
||||
这基本是个卡通渲染游戏都会做的东西,但这次我分别处理了物体的面光和背光Bloom,可以让亮部单独产生Bloom的效果。
|
||||
|
||||
从“物理正确”角度,其实也是需要这样做的。因为Bloom的强弱其实代表了受光的强弱,但是卡通渲染的光照强度并不会完全通过颜色表达出来。为了保持画面的饱和度,亮部的实际亮度并不会太高,而暗部的亮度常常也不能过暗。
|
||||
|
||||
因此,将Bloom和颜色剥离开来,反而更加“物理正确”。
|
||||
|
||||
卡通渲染还特别喜欢让头发和皮肤具有更高的Bloom强度,使得它们看上去更加具有光泽。
|
||||
|
||||
而为了保持发光物体的饱和度,实际颜色值不能过高,从而也需要用高Bloom值来补齐原本想要的泛光。
|
||||
|
||||
我也想过是否可以把实际亮度和颜色在光照体系下就直接拆分开,把亮度当作RGB之外的第4个颜色分量,然后用“亮度”值直接算Bloom,这样处理Additive物体时也更加方便(现在我直接屏蔽了Additive物体的Bloom值写入)。
|
||||
|
||||
但这样改的东西满多的,所有涉及光照的部分都要改,而且这需要让Bloom值是个HDR的值……所以暂时没动。
|
||||
|
||||
真要认真做,可能那才是最完美的方案。
|
||||
|
||||
## 卡通风格场景的定制化技术部分
|
||||
### ToneMapping
|
||||

|
||||
|
||||
在使用标准ACES的时候,如果亮度调的较低,暗部细节就会严重丢失(图2),而如果调高亮度至能看清暗部细节,亮部细节就会丢失(图3)。
|
||||
|
||||
其中一个方案是:可以考虑在无偏色的原始颜色和偏色后的颜色根据亮度做一个插值,保证小于1的区域是无偏色的,然后渐渐过渡到需要偏色的区域。然而我试了下,好像我之前的反转校色方案依然可用(图3)
|
||||
|
||||

|
||||
|
||||
做法是在图2的基础上对人物纹理的颜色输出做以下处理(处理强度可以通过和原颜色lerp来控制)
|
||||
>color = 3.4475 * color ^ 3 - 2.7866 * color ^ 2 + 1.2281 * color - 0.0056
|
||||
|
||||
其实这个公式相当于预处理了一次纹理,将纹理的高光和暗部区域拉长了,这样就不怕明暗细节被ACES破坏了。
|
||||
本来想换个正常点的解决方案,结果……至少这也是个可行的解决方案。
|
||||
|
||||
### 半影色调(Penumbra Tint)
|
||||
|
||||

|
||||
|
||||
就是上图这样的东西。Unity在HDRP里竟然提供了这个功能,生造了个词叫Penumbra Tint,也是满符合Unity自己的人设的。
|
||||
|
||||
我是当初研究新海诚动画的画面特征的时候发现的这个东西。这个元素,也是大部分模仿新海诚风格渲染作品所缺失的最后一步。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
我起初也觉得这个元素是个见缝插针加饱和度的“艺术设计”,直到我在实景中看到了这个现象(拍的不好,肉眼看更明显)
|
||||
|
||||

|
||||
|
||||
我也不知道这玩意儿是怎么出来的,泊油马路也不是次表面材质啊?
|
||||
|
||||
总之,从结果上来看,人眼似乎是可以接受这种“在明暗交界处饱和度提升甚至换个颜色”的表现的。所以作为风格化作品,强化这个表现也就一点问题都没有。
|
||||
|
||||
|
||||
|
||||
这个元素比较类似次表面材质的效果,所以也可以用类似(差不多是完全一样)的方式来实现。分为两部分:
|
||||
|
||||
- 投影部分在软阴影部分叠个额外颜色就好了,次表面就是这么做的,区别就是UE的实现里,亮部也同样需要叠加颜色。
|
||||
- 而非投影部分的次表面特性有两个实现。一个是预积分LUT,相比普通Ramp还需要用曲率修正LUT UV的V坐标,否则半影部分在缓慢曲率上会过宽。另一个就是基于屏幕空间卷积的SSS,可以很完美的实现这个效果。
|
||||
|
||||
所以这个玩意儿大概真的就是个次表面材质特性的艺术延申?万物皆次表面?
|
||||
|
||||

|
||||
|
||||
或者也可以是上图这样的情景:虽然整体光照都是橙色,但是亮面一侧由于亮度过高导致了泛白,半影部分恰好卡在了不会泛白的亮度区域,保留了原色,最终形成了这个视觉效果。
|
||||
|
||||
总之,这两个视觉效果如果用物理打光实现,出现条件较为苛刻。所以在技术实现上,要提供“脱离亮度限制,硬把这个效果挖出来”的功能。其颜色和强度应该由光源和材质双方决定。
|
||||
|
||||
如果不希望增加延迟光照下GBuffer的数量,也可以完全由光照决定半影颜色和强度,仅通过材质蒙版来过滤不希望出现这个效果的物体。这个效果也可以仅通过增加半影处饱和度来实现,这样不依赖GBuffer也可以让不同物体的半影颜色互不相同。
|
||||
|
||||

|
||||
原神人物上的Ramp其实也是基于这个现象的产物。对于缺乏细节的二值化光照,这是一个很好的补充细节。
|
||||
|
||||
### GI
|
||||
GI的比重,是区分风格化渲染和真实感渲染的一个很重要的标志。(这里的GI主要指漫反射GI)
|
||||
|
||||
GI是表达真实感很重要的一个要素,只要把GI做对,画面就会显得真实。换言之,只要GI是对的,不管怎么做看着都会像照片——而这是风格化渲染需要重点避免的地方。
|
||||
|
||||
一个方案是干脆去掉GI,手动补光,退化回PBR之前的模式,典型的例子就是原神。缺点自然是光照平,且单调。但是正因为单调,控制起来也容易,每个地方的颜色都可以单独处理。
|
||||
|
||||
另一个方案是反过来加强GI,这同样也能达到“不真实”的目的,色彩区域的融合也可以让多个物件浑然一体。缺点就是控制起来比较困难,只能做成啥样就啥样。
|
||||
|
||||
日式卡通风格是从纸上作画转移过来的,自然平涂色块会比较多(毕竟人手绘制GI难度较高),所以风格比较接近无GI的效果。如果想符合日系风格,GI就是一个需要压低的元素。
|
||||
|
||||
比如下图的情况,如果放任GI的染色效果,这么大片的绿色草地必然会影响到房子墙壁的颜色,无法保持这么高的色彩对比差异。
|
||||

|
||||
|
||||
### Bloom
|
||||
Bloom从物理角度应该是一种高亮度产生的视觉现象。
|
||||
|
||||
但因为NPR体系下的颜色经过了各种特殊指定,并不和实际现实亮度直接对应,很容易出现纯白区域不希望Bloom,而高饱和低亮度区域反而希望Bloom的情况。
|
||||
其次,Bloom同时也是一种画面上的低频信息,为了营造一种柔和的画面“氛围”,我们也希望增加这种低频信息的比例。而强行拉高整体Bloom强度又会导致高亮度区域过曝。
|
||||
|
||||
所以我们需要一个自定义的Bloom缩放系数。因为从崩三开始的游戏都实装了这个特性,是否要实现它一般并不会有疑问。
|
||||
|
||||
具体实现的时候,最佳方案其实是准备一个单独的Half通道以储存一个HDR范围的亮度值,并在frag中将原始颜色的亮度值计算出并存入。只有这样,才能保证Alpha Blend, Additive,以及各种特殊混合模式下的Bloom值的正确性。
|
||||
|
||||
通常,我们并不愿意花费一个16bit的通道来实现这个效果,只愿意使用一个8bit的通道,所以只会储存一个Bloom的缩放值。这样在透明混合的时候就会出现各种麻烦。
|
||||
|
||||
**目前我比较倾向的方案是:**
|
||||
放弃透明物体的Bloom值自定义功能,透明物体阶段并不写入Bloom值,这样也节约了带宽。而为了最终计算Bloom结果正确,必须将透明物体和不透明物体分别渲染,然后在Bloom阶段重新组合。(为什么必须这样解释起来很麻烦,信我就行了)
|
||||
|
||||
这个分离透明物体渲染的功能UE本来就提供了,只需要修改Bloom部分的RT组合,实现还是很简单的。而且通过配置r.SeparateTranslucencyScreenPercentage还可以获得半透物体降分辨率功能,还可以根据帧率自动进行,基本上不会不开,分离渲染导致的性能代价也就无所谓了。
|
||||
|
||||
事实上,这就是原神的方案,所以估计也没啥人会反对。
|
||||
(原神的透明物体分辨率过低的问题确实严重。我觉得可以将部分需要精度的透明材质转为Mask材质来解决问题,Mask通过TAA抖动模拟单层半透的效果还是不错的)
|
||||
|
||||
虽然Flare,Bloom,Volume Light从物理角度是不同的光学现象,但它们都是画面的低频信息提供方,也可以近似处理。
|
||||
风格化渲染实际上非常需要这类低频信息增强画面的柔和感,以及缓解纯色块的单调感。除了Bloom,Flare和体积光同样也是实现这一点的很好用的工具。
|
||||
|
||||
严格来讲,最理想的方案其实是执行两次Bloom,一次无视物理亮度给画面整体做一次模糊叠加,第二次根据亮度做正常Bloom模拟实际的辉光物理现象。但因为Bloom的性能成本较高,当然希望一次做完。
|
||||
|
||||
配置Bloom参数的时候要考虑倒这一点。两个Bloom的功能其实是不同的。
|
||||
|
||||
## 投影
|
||||
投影的风格化依然遵守分频规则。
|
||||
- 投影或者保持边缘清晰,或者保持一个大范围的模糊过渡,而不要在两种模式之间暧昧不清。
|
||||
- 同时,需要表达一个简单清晰的形状。清晰边缘不要有尖锐的锯齿,模糊边缘的透明过渡不要有坑坑洼洼的锯齿转角。
|
||||
- 同时,投影的强度依然不要暧昧不清。或者是清晰可见的,或者就直接没有,这样才能避免脏污感。
|
||||
|
||||
这些条件加在一起给投影制造了很多写实渲染没有的麻烦。
|
||||
|
||||
人物通常都需要清晰的投影边线,避免和二值化的画面元素冲突。而人物的装饰尖角又是常见元素,投影面多为弧面,这导致投影在不同方向下非常容易出现下面的凌乱边缘。
|
||||
<iframe src="https://vdn.vzuu.com/SD/863f8180-510e-11eb-9c56-e6e44722d8a0.mp4?disable_local_cache=1&bu=078babd7&c=avc.0.0&f=mp4&expiration=1665570973&auth_key=1665570973-0-0-fb8c37c9b91f50fb4d73f7a55b85b02e&v=ali&pu=078babd7" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>
|
||||
|
||||
注意看小腿和后脑勺上的投影(确实许多人会看不到问题在哪吧……)
|
||||
|
||||
利用距离场简化投影形状是比较直接的解决方案,但性能较差。目前比较靠谱的是手动隐藏部分难看的投影,或者使用代理投影物体。
|
||||
|
||||
### AO
|
||||
上面说过了,现在我们要的不光是AO,它同时也包含了Local光投影的部分。
|
||||
|
||||
具体的要求则是:
|
||||
- 需要明显的AO,但是只能出现在墙角,柜子后面这种地方,范围要比较大,且边缘要尽量柔软。
|
||||
- 而小的结构周围则不能出现AO,人物身上尤其不能有。
|
||||
|
||||
### 笔触化
|
||||
总有人想靠“水彩”“油画”“蜡笔”创造特殊的风格化效果。玩家想要“和画一样”的体验,那就将画面做得和画一样。
|
||||
|
||||
虽然是有实时解决方案,但实际效果却不尽人意。
|
||||
|
||||

|
||||
|
||||
### 日式动漫的摄影技术
|
||||
https://zhuanlan.zhihu.com/p/20202161
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### 还可以参考
|
||||
有人在Unity尝试实现了对应的方法
|
||||
https://zhuanlan.zhihu.com/p/363790714
|
||||
|
||||

|
||||
|
||||
较为微弱的光感
|
||||

|
||||
添加了ハイライト
|
||||
|
||||
个人认为添加了这个Highlight之后才跟《动画基础知识大百科》那案例差不多……
|
||||

|
||||
|
||||
影パラ将角色稍微压暗一些
|
||||
|
||||
|
||||
具体的实现方法由于相对复杂一些,将在下文详细阐述
|
||||
总之,将光感+ハイライト叠加上去,将影パラ乘上去,就获得我们的最终结果了
|
||||

|
||||
|
||||
## 一些较少人提过的二次元渲染方法
|
||||
### 逆向Tonemaping
|
||||
常态的做法是利用调色图层和切换不同画笔颜色来尝试一个比较好的结果,但这其实是在用人工做机器应该做的事情,费时费工而且控制精度还不高。
|
||||
既然目标就是让呈现效果和原画中一致,那就用算法算出一致的效果就好了。
|
||||
[【UE4】FilmicTonemapperを打ち消してみた - てんちょーの技術日誌](https://link.zhihu.com/?target=https%3A//shop-0761.hatenablog.com/entry/2019/10/14/221751)
|
||||
UE4本来就提供了一个现成的逆向ACES函数可供调用,效率并不低,也可以牺牲一些精确度将其更换成单矩阵版本的。
|
||||
```text
|
||||
return FilmToneMapInverse(col);
|
||||
```
|
||||
(需要在Custom Node节点中include "Engine/Private/TonemapCommon.ush")
|
||||
上面的文章则自己实现了一个可以对应更改过Tonemaping曲线情况的更复杂的逆向ACES算法,性能比原版低了很多。但可以沿着它的思路再进行优化。
|
||||
|
||||
### 加权生成描边法线
|
||||
[flashyiyi:修复发尖的描边法线](https://zhuanlan.zhihu.com/p/405518306)
|
||||
|
||||
之前讲过一次,用法线所处的三角形夹角作为权重进行加权混合。
|
||||
其实这个算法也是许多DCC软件自动法线平滑的算法,我们一开始不用纯粹是自己菜,直接平均的结果怎么可能是对的。
|
||||
|
||||
### SDF内描边
|
||||
[flashyiyi:用SDF处理卡通内描线的锯齿问题](https://zhuanlan.zhihu.com/p/113190695)
|
||||
|
||||
这个方案最初就是我提的,实际应用效果还不错,也就是侧面观察的时候会有些瑕疵。
|
||||
除了可以确保描线不出现马赛克外,还可以随视角距离变更宽度,和Backface描边的缩放效果保持统一(几乎无法相互分辨)。
|
||||
|
||||
这个方法生成的描线在靠近观察的时候很容易产生扭曲,但这种扭曲的质感反而会形成一股笔触的感觉,也不全是坏事(接受不了老老实实画本村线去)
|
||||

|
||||
|
||||
原图的描线并没有擦除,凑合看看 现在我使用的SDF生成工具是下面这个:
|
||||
[https://github.com/Chlumsky/msdfgen](https://link.zhihu.com/?target=https%3A//github.com/Chlumsky/msdfgen)
|
||||
|
||||
还可以选择生成多通道SDF来保留尖角。但实际情况下需要锐利尖角的情况很少,我用的依然是单色SDF的版本。
|
||||

|
||||
|
||||
### 烘焙顶点曲率
|
||||

|
||||
Ramp的窄过渡区域在部分情景下实际上是希望保持等宽的,否则在平直平面上会散开。
|
||||
烘焙曲率基本可以解决这个问题。
|
||||
不要烘焙到纹理,顶点曲率基本够用,还省去了模糊的步骤。在计算平滑法线的同时顺便计算一下就好了。
|
||||
除了Ramp外,也可以用来控制边缘光照的宽度。
|
||||
|
||||
### 半视角方向自阴影
|
||||
现在一般都会用一个逐物体的Shadowmap来绘制角色的自阴影,以便获得较高的精度。
|
||||
但锐利的自阴影依然容易在光照方向和切线接近平行的时候,出现杂边。
|
||||
|
||||
改变逐物体阴影的光照方向,将原始的光照方向和视角方向相加归一化作为阴影方向,基本可以避免混乱边缘的出现,一般人也看不出区别(代价是即使光照不变,视角变化的时候阴影位置也会有轻微变动,但意外的并不会有违和感)
|
||||
|
||||
### 反转阴影投射屏蔽背面Shadowmap
|
||||
把ShadowCast时的背面剔除改为正面剔除。
|
||||
|
||||
这样一般凸多边形物体的投影就不会固定生成在自己的背光处了,但依然会生成到其他物体上。如此一来,我们就可以通过改变NoL的光照范围,让人物背后也可以获得更大范围的光照,这也是强制调整光照仰角所必须的。
|
||||
|
||||

|
||||
|
||||
但人物并不是完全的凸多边形,所以自阴影其实还是会出现在物体背面,只不过不是“全都会出现”而已。实际情况只要调参不要太过分就不会露馅。
|
||||
|
||||
(当然,如果没自阴影,一开始也没这个问题。但自阴影还是要有的。)
|
||||
|
||||
### RAMP下的多光照体系
|
||||
二次元渲染用的二分法光照有一些固有的问题:
|
||||
|
||||
- 多个二分法光照同时出现,很容易导致光照边线的交错,变得难看。一般只会有一个二分法的光照。
|
||||
- Ramp本身就是一个单光照体系下的产物,Ramp颜色本身就包含了环境光的结果。那么环境光应该如何和直接光混合呢?如果环境光是额外叠加上去的,最终呈现的效果就不是Ramp图所期望的。
|
||||
|
||||
>我选择的办法是,Ramp结果独立计算,计算完直接乘到其他混合完毕的光照结果上。这样能完整保留Ramp本身的特征。
|
||||
|
||||
>Ramp外的光照如果直接根据NoL计算,始终会有塑胶感。现在常见的方法是忽略法线,或者取视角方向作为法线。我选择的是后者。环境光也同样处理。
|
||||
如此一来,整个人物会使用一个统一的,平直的光照结果。这样就能保持二次元渲染的平面特征,同时和周围物体的亮度保持一致。
|
||||
|
||||
>更关键的是,它解决了“面光处看到的阴影不能暗(通透)”和“背光和阴影下的人物依然要变暗,保持和周围物体亮度一致”之间的矛盾。因为面光处的整个人是整体变亮的,而背光处是整体变暗的,通过视角变化以不易察觉的方式在两个亮度之间过渡。
|
||||
|
||||
### 法线无关的光照渐变区域
|
||||
我将用于代替光照方向的视角方向,改为了一个基于视角的筒状模型:
|
||||
|
||||
这样就可以让角色获得一些和法线无关的光照渐变区域,不至于任何时候都只有统一的光照颜色。
|
||||
|
||||

|
||||
看视频才能懂是怎么做的,根据灯光在屏幕空间的位置为圆心,根据半径绘制一个Alpha来控制光照强度。
|
||||
|
||||
- **表现不出光照的方向性**
|
||||
|
||||
用和周围光照相关的高亮度边缘光来体现光照方向,身体上出现的高光也使用同样的规则。
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
此外,这个方案本质修改的是法线,所以可以很大程度兼容基于SH的烘焙光照。烘焙和实时在默认配置下只有少量的区别。
|
||||
|
||||
而这个方案改完后会很大程度忽略物体法线,但细节法线又是表现材质的重要环节。因此,我后来又将细节法线重新应用于修改后的法线上,尽可能保留了原本的材质感(对于PBR的部分,这点是很重要的)
|
||||
|
||||
这个平面光也可以和原本的正常光照混合使用(以多一点塑料感为代价增加一些光照变化,通常也可以接受)
|
||||
|
||||
|
||||
比较类似
|
||||

|
||||
|
||||
<iframe src="https://vdn3.vzuu.com/SD/856046aa-99ed-11eb-b557-e2a3e4b29ac2.mp4?disable_local_cache=1&bu=078babd7&c=avc.0.0&f=mp4&expiration=1665648277&auth_key=1665648277-0-0-c02d32bd3343ac9f90b23c6aef54ac37&v=tx&pu=078babd7" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>
|
||||
|
||||
最终方案 将渐变的起点轨迹调整为圆弧
|
||||
|
||||
既然方块不行,那干脆用圆得了,让渐变的起始点都在圆上,那它的渐变旋转想必是非常平滑的,
|
||||
那么这次的代码就非常简洁了:
|
||||
|
||||
```cpp
|
||||
float2 newUV_SS = (scrPos - centerPosSS) / float2(lightRampWidth, lightRampHeight);
|
||||
|
||||
float2 intersectPoint = normalize(lightDirVS.xy) * _RampCircleSize.x;
|
||||
float lightRamp = max(0, 1 + dot(normalize(lightDirVS.xy), newUV_SS - IntersectPoint));
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
经过简单的调整,这个方案果然让渐变的旋转“自然”了许多。
|
||||
|
||||
而影パラ就是用光感的值进行一些调整后获得,就不再多说了。
|
||||
|
||||
那么接下来讲讲关于Highlight的部分,
|
||||
其实Highlight很明显就是边缘光,那先按边缘光的做法做着,
|
||||
关于边缘光,其实大家也非常熟悉了,个人采用的算法是
|
||||
**pow(saturate(1 - dot(viewDirectionWS, normal)), _RimLightSmoothness)**
|
||||
非常易懂的算法,笔者也尝试过使用次表面散射来模拟,但发现跟普通边缘光的效果相差无几,还是采用了上述简单易懂的边缘光算法。
|
||||
|
||||

|
||||
|
||||
|
||||
接下来结合之前算的渐变值来进行边缘光的范围限制就行了
|
||||
|
||||

|
||||
|
||||
|
||||
效果正如各位之前所看到的那样
|
||||
|
||||
### 影颜色叠加
|
||||
二次元渲染的阴影颜色是不能直接乘在原始颜色上的,因为会越来越暗,饱和度也会变低。
|
||||
|
||||
一个常见的做法是,亮部一张图,暗部一张图,不采用乘法而使用Lerp作为两者的插值,这样就能确保暗处的颜色可以自由配置。即使在使用Ramp的情况下,也可以将Ramp的A通道指定明暗贴图的插值依据。
|
||||
|
||||
但这种做法多多少少有些笨重,两张图画起来也麻烦。
|
||||
|
||||
而原神为了省掉ShadowColor这张图,采用了多个Ramp选择+Index标记这种方法,最终也会遇到同样的问题。
|
||||
|
||||
其实这个问题用GGXX的方案会比较好。它混合阴影的方式是先加一个白色然后再乘阴影颜色,这样阴影就能突破原颜色的限制,最终结果很大概率是符合要求的。加的这个白色的亮度会决定阴影的亮度,这样二级阴影的颜色也可以被同时确定。
|
||||
|
||||
Ramp方案下,依然可以用A通道来表示这个附加的白色的强弱,最终更好地通过Ramp来规定暗部颜色。
|
||||
|
||||
### 从烘焙光照中获取光照方向
|
||||
在没有直线光存在的情况下,Ramp光照将会消失,效果会很不好。
|
||||
|
||||
通常方案是寻找场景里最近的一个点光源来作为Ramp光照的依据,可以用Volume来实现。原神也是有这个效果的。
|
||||
|
||||
然而实际上,烘焙的SH光照数据里,SH1就是此处光源的大致方向。将它拿出来归一化一下就可以用了。
|
||||
|
||||
### 用UV2来偏移高光
|
||||
目前卡通高光一般都是画死的,视角只会影响亮度。
|
||||
|
||||
画死当然不好。所以一个折中方案是让高光区域可以随视角进行一定的偏移(也有缩放的做法,总之要让它有一定变化)
|
||||
|
||||
我没有选择把高光画在主纹理上,而是画在另一个地方,并使用UV2。这张图在UV2上的角度和缩放将会决定它在视角变化时的偏移方向和幅度,于是也可以用来模拟肩膀处的光点以及大腿上的竖条高光。
|
||||
|
||||
而且这样一来,高光图将会使用独立的贴图精度和范围。不会出现整个人高光就这么几处,却要占据覆盖人体的整张通道图的情况。而且它还可以和细节图案纹理共用UV2。
|
||||
|
||||
并不是什么特别的做法,但我看基本没人提,网络上的全是FLowmap一类的玩意儿。图案整体偏移,用第二套UV控制才是最简单的做法。
|
||||
|
||||
### 透射
|
||||

|
||||
|
||||
一个比较简单的效果,让薄衣服可以违反NoL的规则始终被照亮。手动标记区域。
|
||||
|
||||
可以很容易做出所谓“通透感”。
|
||||
|
||||
|
||||
|
||||
基于这个原理,我还在人物边缘根据法线加了一些次表面散射的效果,但说实在话效果并不明显。
|
||||
|
||||

|
||||
|
||||
估计很难看出两者的区别,而且还需要画区域屏蔽鼻子处的漏光
|
||||
|
||||
## 头部FOV修正
|
||||
|
||||

|
||||
|
||||
将脑袋压扁来修复FOV造成的畸变是离线常用的方法,但我还没见谁真的在常态的游戏流程中使用。主要原因在俩:
|
||||
|
||||
- 如果你一直使用50左右的低FOV,畸变问题并不显著。动作游戏多使用低FOV,只有射击游戏才会用90的高FOV。所以这不是一个急需解决的问题。剧情过场中也可以(本来也应该)降低FOV。
|
||||
- 压扁物体会导致各种各样的问题(如排序问题和穿模),深度值可不是随便能改的东西。
|
||||
|
||||
对于前者,我必须得说,本来人家离线动画里用的FOV也不高。人家选择压扁脑袋,是为了获得“极致”的表现。畸变不明显又不代表没有畸变。
|
||||
|
||||
对于后者,这就是个技术问题。而它是可以解决的。
|
||||
|
||||
|
||||
|
||||
我选择的方案不是压扁模型(A移动到B),而是使用结果等价的“平移”(B')来取代压扁,保持深度的正确。
|
||||
|
||||
(实际使用的时候是既压扁也平移,选择其中顶点偏移距离最短的结果,即B'')
|
||||
|
||||

|
||||
|
||||
篇幅有限我就不解释了,就是个线性代数问题。
|
||||
|
||||

|
||||
|
||||
### 柔光光照
|
||||
卡通光照大幅忽略了物体法线,在使用颜色光照的时候,很容易导致整个物体的颜色都变得非常单一。
|
||||
|
||||
参考离线2D动画的打光技巧,我选择让光照结果通过柔光的方法应用到BaseColor上。
|
||||
|
||||
但最后,我分析柔光的混合公式,总结了一个近似的,能够更直接地实现我的目的的公式。
|
||||
|
||||
A * lerp(B,Lum(B),A)
|
||||
|
||||
即,原图中的色彩通道亮度越高,光照的饱和度就越低。这样图象中较亮的部分就不会受到光照颜色的影响,但暗处则会正常受到影响,效果如下:
|
||||

|
||||
|
||||

|
||||
如果光源是白光,则和之前没有任何区别。
|
||||
|
||||
不过,实际使用的时候,这个效果也会导致人物很难被颜色光改变颜色,需要和正常的光照结果再Lerp一下。
|
||||
|
||||
### Diffusion
|
||||
GGXX和蓝色协议都在分享里提到了Diffusion,解释说是另一个Bloom,但却没有后续的细节。
|
||||
|
||||
我参考动画流程,采用了小卷积高斯模糊后和原图像素取Max的做法(变亮混合模式)
|
||||
|
||||
因为是取Max,并不会提高亮处的亮度,只会拉高暗部的亮度,符合我们的需要。而且这样的效果是普通Bloom无法实现的(普通Bloom亮部怎么调都肯定会更亮)
|
||||
|
||||
|
||||
|
||||
不过为了做出蓝色协议原图的效果,我还额外再用了一次叠加混合(相乘)。亮度会下降一些,且颜色变深。具体怎么用看实际情况吧。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
由于只是一个小卷积模糊和简单计算,额外性能成本其实不高。
|
||||
|
||||
|
||||
|
||||
### SNN预处理纹理
|
||||
|
||||

|
||||
|
||||
就是蓝色协议介绍的场景纹理处理方法,效果是让远处的纹理呈现色块化质感,看上去更像画。
|
||||
|
||||
我采用的是在图片预处理阶段对各级Mip分别应用SNN滤镜的方法,低Mip和高Mip的卷积核一样。但由于图片大小不同,低Mip变化小,高Mip变化大。
|
||||
|
||||
最后就能自然地形成上图的效果,且渲染成本为零。
|
||||
|
||||
### 染色雾
|
||||
重点在于,NPR场景会使用一个写实绝对不可能用的手段:
|
||||
|
||||
对渲染结果直接用叠加混合(乘法混合)。
|
||||
|
||||
因为这是物理世界中不存在的现象,但是却是动画后期非常常见的做法。
|
||||
|
||||
所谓染色雾指的是:计算雾效的时候不用Additive混合,也不用Alpha Blend混合,而是用Multiply混合(当然全用Multiply也不行,这只是一个额外的效果)
|
||||
|
||||
这使得你可以很轻易地控制场景各部分的颜色,而不导致原有信息大幅丢失。
|
||||
|
||||
实现方面用一个Multiply混合的读深度的透明物体就可以,也可以在原本的雾效计算中加入此元素。
|
||||
|
||||
(之前也有人搞过Multiply光照,本质上和Multiply雾是一回事)
|
||||
|
||||

|
||||
当然美术能不能用好这个工具就是另一个问题了。
|
||||
(我并不是在嘲讽,而是这类体系外的工具,对于已经形成体系的美术来讲确实很难融入他的体系内。而缺失这部分工具正是场景始终不“卡通”的其中一个重要原因)
|
||||
|
||||
蓝色协议的间接光按距离染色也是这个机制的一个应用。
|
103
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/知乎骨鱼子的厚涂实现.md
Normal file
103
03-UnrealEngine/卡通渲染相关资料/厚涂风格笔记/知乎骨鱼子的厚涂实现.md
Normal file
@@ -0,0 +1,103 @@
|
||||
---
|
||||
title: 知乎骨鱼子的厚涂实现
|
||||
date: 2022-10-12 14:25:32
|
||||
excerpt:
|
||||
tags: 卡通渲染
|
||||
rating: ⭐
|
||||
---
|
||||
## 前言
|
||||
主要使用多个LUT混合的方式来实现厚涂的效果。
|
||||
|
||||
  
|
||||
|
||||
## 文章摘要
|
||||
[[厚涂风格实时二次元渲染(0)-前言]]
|
||||
[[厚涂风格实时二次元渲染(2)-厚涂风的皮肤渲染]]
|
||||
|
||||
## 什么是厚涂与半厚涂
|
||||
https://zhuanlan.zhihu.com/p/467348694
|
||||
|
||||
厚涂是发源于**西方油画**的一种绘画技法。 画家在画布上使用很厚的颜料,利用笔刷或画刀进行涂抹,就像抹奶油一样,故名“厚涂”
|
||||
在这样的绘画工艺下,导致厚涂色块十分分明。因为是用很厚的颜料,所以渐变实际上是离散的,这就和我们的赛璐璐有一点像了。
|
||||
离散的同时,由于两个颜料的相互融合,边界却又十分的柔和
|
||||
|
||||

|
||||
|
||||
柔和的边界
|
||||
|
||||
为了让皮肤的质感更为可控,我们采用第二章N*N的LUT,但这张LUT我们不基于物理去生成,而是跟着**感觉**走
|
||||
|
||||
实际上,我们需要一个散射光强度与直射光相补正,而这个光就是天空光。
|
||||
1.天空光是蓝色的,所以我们考虑让皮肤看上去有蓝色的散射感。
|
||||
2.与此同时,在皮肤边缘,我希望有一个体现BTDF的效果,在二次元下,我希望这个颜色是粉粉的。
|
||||
3.散射感在直射光射到的地方肯定会体现的不充足,在直射光射不到的地方才会比较多。
|
||||
4.第二条说的BTDF肯定也得是和光照强度绑定的呀!
|
||||
|
||||
综上四条,我们下一步控制皮肤质感的LUT,必然有一维是 N·L ,用以表示直射强度。考虑到皮肤边缘需要BTDF,另一个维度应该是 N·V 。
|
||||
这样的话我们依然需要LUT,只是这次的LUT不需要积分(毕竟也不基于物理),
|
||||
直接凭感觉随便拿PS什么的画一下就好了。
|
||||
|
||||
这个补正方法的灵感来源于一个效果很好的Fake SSS的着色:
|
||||
[Shader Forge - A visual, node-based shader editor | Page 54 - Unity Forum](https://link.zhihu.com/?target=https%3A//forum.unity.com/threads/shader-forge-a-visual-node-based-shader-editor.222049/page-54%23post-1903768)
|
||||
|
||||

|
||||
|
||||
二次元可以使用的LUT
|
||||
|
||||
以天空为背景色,直射光使用肤色作为相应,然后边缘使用粉粉嫩嫩的颜色。
|
||||
|
||||
至于这块儿棕色,因为我们是半兰伯特模型,所以一般而言不会采样到太多。
|
||||
二次元可以使用的LUT
|
||||
|
||||
把这张LUT和上面pre-integrated以相同的方式(但纵坐标轴不同)也应用进去,就完成90%的效果啦
|
||||
|
||||

|
||||
|
||||
赛璐璐Only
|
||||

|
||||
|
||||
### 3.一点小方法
|
||||
(1)另类高光
|
||||

|
||||
|
||||
肩头上有高光
|
||||
可以看到有些二次元厚涂作品有明显不基于物理的高光,这是因为二次元美少女每一个都滑溜溜的。
|
||||
这个效果在我们使用了不基于物理的补正LUT后非常简单,在LUT的左上角点一个光点就可以了。
|
||||
|
||||
(2)屏幕空间补光
|
||||

|
||||
肩头存在补光
|
||||
二次元角色的光照实际上都是以好看为第一要务的。其实许多动漫都存在“两个人面对面,但是却都向光”的情况。对于二次元美少女而言,光补的越多,就越好看(雾)
|
||||
|
||||
[Unity URP Shader 与 HLSL 自学笔记六 等宽屏幕空间边缘光 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/365339160)
|
||||
可以在这篇文章学习这个套路。因为我也是学的别人的,就不在这里班门弄斧啦。
|
||||

|
||||
补光后的效果,会让人感觉更有绘画感。
|
||||
此外,将这个效果复制一份并以 normal·viewDir lerp一下,可以做出BDTF的效果
|
||||

|
||||
|
||||
厚涂作品:
|
||||
https://www.zhihu.com/question/411686903/answer/1666001786
|
||||
|
||||
## 要素组成分析
|
||||
### 1.皮肤的平滑光照
|
||||
显而易见,插画风格与赛璐璐风格最大的区别就在于,皮肤对于不同的光照情况有着不同的表现。
|
||||
|
||||
>常见的pre-integrated模型刚好是以 N·L 为横坐标的,这与我们的RampMap一致。此时此刻我们的赛璐璐风格RampMap应该还是一个1N大小的LUT,将这个一维LUT与pre-integrated二维LUT直接逐行相乘,得到一个新的RampMap,但是1N的RampMap拓展为了N*N,纵坐标的1/r是在球面上的,如何应用到我们的复杂网格渲染中呢?
|
||||
|
||||
|
||||
>为了让皮肤的质感更为可控,我们采用第二章N*N的LUT,但这张LUT我们不基于物理去生成,而是跟着**感觉**走
|
||||
|
||||
实际上,我们需要一个散射光强度与直射光相补正,而这个光就是天空光。
|
||||
|
||||
1. 天空光是蓝色的,所以我们考虑让皮肤看上去有蓝色的散射感。
|
||||
2. 与此同时,在皮肤边缘,我希望有一个体现BTDF的效果,在二次元下,我希望这个颜色是粉粉的。
|
||||
3. 散射感在直射光射到的地方肯定会体现的不充足,在直射光射不到的地方才会比较多。
|
||||
4. 第二条说的BTDF肯定也得是和光照强度绑定的呀!
|
||||
|
||||
综上四条,我们下一步控制皮肤质感的LUT,必然有一维是 N·L ,用以表示直射强度。考虑到皮肤边缘需要BTDF,另一个维度应该是 N·V 。
|
||||
这样的话我们依然需要LUT,只是这次的LUT不需要积分(毕竟也不基于物理),
|
||||
直接凭感觉随便拿PS什么的画一下就好了。
|
||||
这个补正方法的灵感来源于一个效果很好的Fake SSS的着色:
|
||||
[[Shader Forge - A visual, node-based shader editor | Page 54 - Unity Forum](https://link.zhihu.com/?target=https%3A//forum.unity.com/threads/shader-forge-a-visual-node-based-shader-editor.222049/page-54%23post-1903768)](https://forum.unity.com/threads/shader-forge-a-visual-node-based-shader-editor.222049/page-54#post-1903768)
|
||||

|
332
03-UnrealEngine/卡通渲染相关资料/在Ue4中实现对二次元模型进行透视校正.md
Normal file
332
03-UnrealEngine/卡通渲染相关资料/在Ue4中实现对二次元模型进行透视校正.md
Normal file
@@ -0,0 +1,332 @@
|
||||
## 前言
|
||||
之前想使用UE4实现IOchair通过Blender晶格变形,实现的二次元角色面部透视矫正效果。在使用HLSL写了1/3晶格效果和IOchair沟通后,才发现这个功能只需要通过在摄像机坐标系下调整模型顶点的Z值即可。但要在Ue4的材质编辑器中实现这个功能就必须知道Ue4的VertexShader处理过程,于是我花了些时间研究了一下。
|
||||
|
||||
**效果实现思路**:
|
||||
一句话解释就是在模型的顶点坐标转换到摄像机坐标后,在乘以投影矩阵前对其Z值进行调整,以实现纸片人的效果。
|
||||
|
||||

|
||||
|
||||
参考了以下这篇文章:
|
||||
https://zhuanlan.zhihu.com/p/268433650?utm_source=ZHShareTargetIDMore
|
||||
|
||||
## IOchair的补充
|
||||
透视矫正的作用补充一下,还有:
|
||||
1. 减弱AO,空间扁了AO变浅,更二维
|
||||
2. 清理前发投影,刘海在额头上的投影变得整齐美观
|
||||
3. 顺滑高光,高光连续感更强
|
||||
|
||||
下图为IOchair的演示图:
|
||||

|
||||

|
||||
|
||||
个人认为:以上说法是建立在使用默认光照的情况,一般的卡通渲染都会选择重写光照来控制面部阴影表现。
|
||||
|
||||
## BasePassVertexShader
|
||||
代码位于BasePassVertexShader.usf中。可以看得出WorldPosition就是模型的世界坐标,WorldPositionExcludingWPO为没有WorldPositionOffset的版本。WorldPosition加上offset后,乘以MVP矩阵最后输出Output.Position。
|
||||
|
||||
以下是我精简过的代码以方便阅读:
|
||||
```hlsl
|
||||
/** Entry point for the base pass vertex shader. */
|
||||
void Main(
|
||||
FVertexFactoryInput Input,
|
||||
OPTIONAL_VertexID,
|
||||
out FBasePassVSOutput Output,
|
||||
out float OutGlobalClipPlaneDistance : SV_ClipDistance)
|
||||
{
|
||||
|
||||
uint EyeIndex = 0;
|
||||
ResolvedView = ResolveView();
|
||||
|
||||
FVertexFactoryIntermediates VFIntermediates = GetVertexFactoryIntermediates(Input);
|
||||
float4 WorldPositionExcludingWPO = VertexFactoryGetWorldPosition(Input, VFIntermediates);
|
||||
float4 WorldPosition = WorldPositionExcludingWPO;
|
||||
float4 ClipSpacePosition;
|
||||
|
||||
float3x3 TangentToLocal = VertexFactoryGetTangentToLocal(Input, VFIntermediates);
|
||||
FMaterialVertexParameters VertexParameters = GetMaterialVertexParameters(Input, VFIntermediates, WorldPosition.xyz, TangentToLocal);
|
||||
|
||||
// Isolate instructions used for world position offset
|
||||
// As these cause the optimizer to generate different position calculating instructions in each pass, resulting in self-z-fighting.
|
||||
// This is only necessary for shaders used in passes that have depth testing enabled.
|
||||
{
|
||||
WorldPosition.xyz += GetMaterialWorldPositionOffset(VertexParameters);
|
||||
}
|
||||
|
||||
{
|
||||
float4 RasterizedWorldPosition = VertexFactoryGetRasterizedWorldPosition(Input, VFIntermediates, WorldPosition);
|
||||
ClipSpacePosition = INVARIANT(mul(RasterizedWorldPosition, ResolvedView.TranslatedWorldToClip));
|
||||
Output.Position = INVARIANT(ClipSpacePosition);
|
||||
}
|
||||
|
||||
#if USE_GLOBAL_CLIP_PLANE
|
||||
OutGlobalClipPlaneDistance = dot(ResolvedView.GlobalClippingPlane, float4(WorldPosition.xyz - ResolvedView.PreViewTranslation.xyz, 1));
|
||||
#endif
|
||||
#if USE_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS
|
||||
Output.BasePassInterpolants.PixelPositionExcludingWPO = WorldPositionExcludingWPO.xyz;
|
||||
#endif
|
||||
#endif
|
||||
Output.FactoryInterpolants = VertexFactoryGetInterpolants(Input, VFIntermediates, VertexParameters);
|
||||
|
||||
OutputVertexID( Output );
|
||||
}
|
||||
|
||||
```
|
||||
其他两个函数:
|
||||
```
|
||||
float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
||||
{
|
||||
return TransformLocalToTranslatedWorld(Intermediates.UnpackedPosition);
|
||||
}
|
||||
|
||||
float4 VertexFactoryGetRasterizedWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float4 InWorldPosition)
|
||||
{
|
||||
return InWorldPosition;
|
||||
}
|
||||
```
|
||||
|
||||
## 矩阵计算
|
||||
### TranslatedViewProjectMatrix
|
||||
以下是相关的计算代码:
|
||||
```c++
|
||||
ViewUniformShaderParameters.TranslatedWorldToClip = InViewMatrices.GetTranslatedViewProjectionMatrix();
|
||||
|
||||
inline const FMatrix& GetTranslatedViewProjectionMatrix() const
|
||||
{
|
||||
return TranslatedViewProjectionMatrix;
|
||||
}
|
||||
|
||||
TranslatedViewProjectionMatrix = GetTranslatedViewMatrix() * GetProjectionMatrix();
|
||||
|
||||
inline const FMatrix& GetTranslatedViewMatrix() const
|
||||
{
|
||||
return TranslatedViewMatrix;
|
||||
}
|
||||
|
||||
inline const FMatrix& GetProjectionMatrix() const
|
||||
{
|
||||
return ProjectionMatrix;
|
||||
}
|
||||
```
|
||||
|
||||
### Project矩阵
|
||||
UE4的矩阵与U3D使用的标准OPENGL式的矩阵不同:
|
||||
|
||||
1. OpenGL中Z坐标被映射到[-1, 1]。而Ue4映射到[0,1],这使得"W"值变为1而不是-1。
|
||||
2. Ue4对其所有的透视矩阵应用了一个矩阵转置。
|
||||
|
||||
UE4 | Value | Unity | Value
|
||||
---|---|---|---
|
||||
[0,0]| 1.0f / FMath::Tan(HalfFOV) |[0,0]| 2.0F * near / (right - left);
|
||||
[1,1]| Width / FMath::Tan(HalfFOV) / Height| [1,1]| 2.0F * near / (top - bottom);
|
||||
[2,0]| 0.0F| [0,2] | (right + left) / (right - left);
|
||||
[2,1]| 0.0F| [1,2] | (top + bottom) / (top - bottom);
|
||||
[2,2]| ((MinZ == MaxZ) ? (1.0f - Z_PRECISION) : MaxZ / (MaxZ - MinZ))| [2,2]| -(far + near) / (far - near);
|
||||
[3,2]| -MinZ * ((MinZ == MaxZ) ? (1.0f - Z_PRECISION) : MaxZ / (MaxZ - MinZ)) | [2,3]| -(2.0F * far * near) / (far - near);
|
||||
[2,3]| 1.0f| [3,2]| -1.0f
|
||||
|
||||
#### 自定义投射矩阵效果
|
||||
继承ULocalPlayer类,重写CalcSceneView函数:
|
||||
```
|
||||
FSceneView * UOffAxisLocalPlayer::CalcSceneView(FSceneViewFamily * ViewFamily, FVector &amp;OutViewLocation, FRotator &amp;OutViewRotation, FViewport * Viewport, FViewElementDrawer * ViewDrawer, EStereoscopicPass StereoPass)
|
||||
{
|
||||
|
||||
FSceneView* View = ULocalPlayer::CalcSceneView(ViewFamily, OutViewLocation, OutViewRotation, Viewport, ViewDrawer, StereoPass);
|
||||
|
||||
if (View)
|
||||
{
|
||||
FMatrix CurrentMatrix = View-&gt;ViewMatrices.GetProjectionMatrix();
|
||||
|
||||
float FOV = FMath::DegreesToRadians(60.0f);
|
||||
FMatrix ProjectionMatrix = FReversedZPerspectiveMatrix(FOV, 16.0f, 9.0f, GNearClippingPlane);
|
||||
View->UpdateProjectionMatrix(ProjectionMatrix);
|
||||
}
|
||||
return View;
|
||||
}
|
||||
```
|
||||
最后在Project Settings->General Settings->Local Player Class设置定义的新类即可。
|
||||
|
||||
详细内容可以参考:http://www.geodesic.games/2019/03/27/projection-matrices-in-unreal-engine/<br>
|
||||
作者还写了插件:https://github.com/GeodesicGames/SimpleOffAxisProjection
|
||||
|
||||
#### 具体计算代码
|
||||
```
|
||||
/** Calculates the projection matrix using this view info's aspect ratio (regardless of bConstrainAspectRatio) */
|
||||
ENGINE_API FMatrix CalculateProjectionMatrix() const;
|
||||
FMatrix FMinimalViewInfo::CalculateProjectionMatrix() const
|
||||
{
|
||||
FMatrix ProjectionMatrix;
|
||||
ProjectionMatrix = FReversedZPerspectiveMatrix(
|
||||
FMath::Max(0.001f, FOV) * (float)PI / 360.0f,
|
||||
AspectRatio,
|
||||
1.0f,
|
||||
GNearClippingPlane );
|
||||
}
|
||||
```
|
||||
```
|
||||
FORCEINLINE FPerspectiveMatrix::FPerspectiveMatrix(float HalfFOV, float Width, float Height, float MinZ, float MaxZ)
|
||||
: FMatrix(
|
||||
FPlane(1.0f / FMath::Tan(HalfFOV), 0.0f, 0.0f, 0.0f),
|
||||
FPlane(0.0f, Width / FMath::Tan(HalfFOV) / Height, 0.0f, 0.0f),
|
||||
FPlane(0.0f, 0.0f, ((MinZ == MaxZ) ? (1.0f - Z_PRECISION) : MaxZ / (MaxZ - MinZ)), 1.0f),
|
||||
FPlane(0.0f, 0.0f, -MinZ * ((MinZ == MaxZ) ? (1.0f - Z_PRECISION) : MaxZ / (MaxZ - MinZ)), 0.0f)
|
||||
)
|
||||
{ }
|
||||
|
||||
FORCEINLINE FReversedZPerspectiveMatrix::FReversedZPerspectiveMatrix(float HalfFOV, float Width, float Height, float MinZ, float MaxZ)
|
||||
: FMatrix(
|
||||
FPlane(1.0f / FMath::Tan(HalfFOV), 0.0f, 0.0f, 0.0f),
|
||||
FPlane(0.0f, Width / FMath::Tan(HalfFOV) / Height, 0.0f, 0.0f),
|
||||
FPlane(0.0f, 0.0f, ((MinZ == MaxZ) ? 0.0f : MinZ / (MinZ - MaxZ)), 1.0f),
|
||||
FPlane(0.0f, 0.0f, ((MinZ == MaxZ) ? MinZ : -MaxZ * MinZ / (MinZ - MaxZ)), 0.0f)
|
||||
)
|
||||
{ }
|
||||
```
|
||||
|
||||
### View矩阵
|
||||
```
|
||||
void FViewMatrices::UpdateViewMatrix(const FVector& ViewLocation, const FRotator& ViewRotation)
|
||||
{
|
||||
ViewOrigin = ViewLocation;
|
||||
|
||||
FMatrix ViewPlanesMatrix = FMatrix(
|
||||
FPlane(0, 0, 1, 0),
|
||||
FPlane(1, 0, 0, 0),
|
||||
FPlane(0, 1, 0, 0),
|
||||
FPlane(0, 0, 0, 1));
|
||||
|
||||
const FMatrix ViewRotationMatrix = FInverseRotationMatrix(ViewRotation) * ViewPlanesMatrix;
|
||||
|
||||
ViewMatrix = FTranslationMatrix(-ViewLocation) * ViewRotationMatrix;
|
||||
|
||||
// Duplicate HMD rotation matrix with roll removed
|
||||
FRotator HMDViewRotation = ViewRotation;
|
||||
HMDViewRotation.Roll = 0.f;
|
||||
HMDViewMatrixNoRoll = FInverseRotationMatrix(HMDViewRotation) * ViewPlanesMatrix;
|
||||
|
||||
ViewProjectionMatrix = GetViewMatrix() * GetProjectionMatrix();
|
||||
|
||||
InvViewMatrix = ViewRotationMatrix.GetTransposed() * FTranslationMatrix(ViewLocation);
|
||||
InvViewProjectionMatrix = GetInvProjectionMatrix() * GetInvViewMatrix();
|
||||
|
||||
PreViewTranslation = -ViewOrigin;
|
||||
|
||||
TranslatedViewMatrix = ViewRotationMatrix;
|
||||
InvTranslatedViewMatrix = TranslatedViewMatrix.GetTransposed();
|
||||
OverriddenTranslatedViewMatrix = FTranslationMatrix(-PreViewTranslation) * ViewMatrix;
|
||||
OverriddenInvTranslatedViewMatrix = InvViewMatrix * FTranslationMatrix(PreViewTranslation);
|
||||
|
||||
// Compute a transform from view origin centered world-space to clip space.
|
||||
TranslatedViewProjectionMatrix = GetTranslatedViewMatrix() * GetProjectionMatrix();
|
||||
InvTranslatedViewProjectionMatrix = GetInvProjectionMatrix() * GetInvTranslatedViewMatrix();
|
||||
}
|
||||
```
|
||||
### 相关变量定义位置
|
||||
UE4已经帮我们计算好相关的矩阵,一些矩阵与摄像机相关变量位于一下文件中:
|
||||
```
|
||||
// SceneData.USH
|
||||
float4x4 LocalToWorld;
|
||||
float4x4 WorldToLocal;
|
||||
|
||||
// SceneView.cpp
|
||||
// ResolvedView
|
||||
ViewUniformShaderParameters.ViewToTranslatedWorld = InViewMatrices.GetOverriddenInvTranslatedViewMatrix();
|
||||
ViewUniformShaderParameters.TranslatedWorldToClip = InViewMatrices.GetTranslatedViewProjectionMatrix();
|
||||
ViewUniformShaderParameters.WorldToClip = InViewMatrices.GetViewProjectionMatrix();
|
||||
ViewUniformShaderParameters.ClipToWorld = InViewMatrices.GetInvViewProjectionMatrix();
|
||||
ViewUniformShaderParameters.TranslatedWorldToView = InViewMatrices.GetOverriddenTranslatedViewMatrix();
|
||||
ViewUniformShaderParameters.TranslatedWorldToCameraView = InViewMatrices.GetTranslatedViewMatrix();
|
||||
ViewUniformShaderParameters.CameraViewToTranslatedWorld = InViewMatrices.GetInvTranslatedViewMatrix();
|
||||
ViewUniformShaderParameters.ViewToClip = InViewMatrices.GetProjectionMatrix();
|
||||
ViewUniformShaderParameters.ViewToClipNoAA = InViewMatrices.GetProjectionNoAAMatrix();
|
||||
ViewUniformShaderParameters.ClipToView = InViewMatrices.GetInvProjectionMatrix();
|
||||
ViewUniformShaderParameters.ClipToTranslatedWorld = InViewMatrices.GetInvTranslatedViewProjectionMatrix();
|
||||
ViewUniformShaderParameters.ViewForward = InViewMatrices.GetOverriddenTranslatedViewMatrix().GetColumn(2);
|
||||
ViewUniformShaderParameters.ViewUp = InViewMatrices.GetOverriddenTranslatedViewMatrix().GetColumn(1);
|
||||
ViewUniformShaderParameters.ViewRight = InViewMatrices.GetOverriddenTranslatedViewMatrix().GetColumn(0);
|
||||
ViewUniformShaderParameters.InvDeviceZToWorldZTransform = InvDeviceZToWorldZTransform;
|
||||
ViewUniformShaderParameters.WorldViewOrigin = InViewMatrices.GetOverriddenInvTranslatedViewMatrix().TransformPosition(FVector(0)) - InViewMatrices.GetPreViewTranslation();
|
||||
ViewUniformShaderParameters.WorldCameraOrigin = InViewMatrices.GetViewOrigin();
|
||||
ViewUniformShaderParameters.TranslatedWorldCameraOrigin = InViewMatrices.GetViewOrigin() + InViewMatrices.GetPreViewTranslation();
|
||||
```
|
||||
|
||||
### 具体实现与效果
|
||||
将这个Custom节点直接连到WorldPositionOffset即可。
|
||||
```
|
||||
//float3 WorldPosition
|
||||
//float ZClampValue
|
||||
//float3 ObjectPosition
|
||||
|
||||
// const float4x4 localToWorld=Primitive.LocalToWorld;
|
||||
const float4x4 WorldToView=ResolvedView.TranslatedWorldToView;
|
||||
const float4x4 ViewToWorld=ResolvedView.ViewToTranslatedWorld;
|
||||
|
||||
float3 ViewSpacePosition=INVARIANT(mul(WorldPosition, WorldToView));
|
||||
float pivortZ=INVARIANT(mul(ObjectPosition, WorldToView)).z;
|
||||
ViewSpacePosition.z=(ViewSpacePosition.z-pivortZ)/ZClampValue+pivortZ;
|
||||
return INVARIANT(mul(ViewSpacePosition,ViewToWorld))-WorldPosition;
|
||||
```
|
||||
|
||||
这里我使用原神中莫娜作为演示模型,使用的是Unlit自发光材质:
|
||||
|
||||
|
||||

|
||||
原始模型
|
||||
|
||||

|
||||
缩放0.5
|
||||
|
||||

|
||||
缩放0.3
|
||||
|
||||

|
||||
缩放0.2
|
||||
|
||||
## 思考与改进
|
||||
这个压扁操作本质上为了降低模型的透视效果,使得角色的脸部更加的平面化。但目前这个做法比较粗暴,还有很大的改进空间:
|
||||
|
||||
1. 我们只需要调整角色脸面向相机且较为靠近相机的顶点。
|
||||
2. 透视效果与fov、相机坐标下模型的Z具有线性关系,可以根据这些关系来调整“压扁值”。
|
||||
3. 调整的方向是鼻子、嘴、眼睛等具有重点轮廓区域。不对脸部进行较大数值的调整,使得阴影不会产生较大的形变。
|
||||
|
||||
### 重新计算法线
|
||||
IOchair通过Blender晶格变形实现该效果的。而DCC工具中的晶格变形会重建顶点法线。所以我也需要在UE4中实现这个功能。经过404查询,得知在GPU精粹有中解决方法:
|
||||
|
||||
1. 微分附近顶点求得法线。
|
||||
2. 使用雅可比矩阵。
|
||||
|
||||
第一种方法比较简单,只需要通过ddx与ddy计算WorldPosition,即可得到切线法线值。
|
||||
第二种方法我会在学习之后补上。
|
||||
|
||||
回顾第一种方法时还找到有意思的资料:
|
||||
- 通过ddx、ddy与Cross去除平滑法线效果:
|
||||

|
||||

|
||||
https://answers.unrealengine.com/questions/29087/disable-terrain-smoothing.html#answer-30100
|
||||
|
||||
- 通过Noise生成世界法线的方法:
|
||||

|
||||
https://odederell3d.blog/tag/ddy/
|
||||
|
||||
PS.晶格变形用的是FreeFormDeformation算法,直接搜索Lattice Deformation是找不到资料的。
|
||||
|
||||
|
||||
|
||||
### 3.3.1 视场(Field of View)的影响
|
||||
先来解释一下视场,视场即FOV,在摄影中指相机可以接收影像的角度范围,也可以称为视野。
|
||||
> 由于显示比例的原因,FOV在水平与垂直方向上的数值不同,以下未作说明的情况下默认为水平FOV。
|
||||
|
||||
FOV越大视野越宽,透视的变形就越大,下图可以很好的展示这种特征。
|
||||
|
||||

|
||||
|
||||
How Focal Length Affects Viewing Angle
|
||||
|
||||
下图是人眼的水平视场的范围示意,±30°是中央视野区域,±60°是双眼水平视场。所以游戏中的FOV也遵循这个规则,能够修改的范围通常都在60 - 120之间。
|
||||
|
||||

|
||||
|
||||
人眼的水平视场能力(Horizontal Field of View, FOV)
|
||||
而FOV的高低会很大程度上的会影响角色表现,特别是特写时的面部区域。卡通角色更适用于在较低的FOV中表现效果,而第三人称视角中通常使用到的FOV数值要远大于适合卡通表现的FOV。
|
||||
下面的视频中是 5 - 60 之间的 FOV (Vertical) 变换,可以明显的看出FOV变化时面部轮廓的变形。
|
||||
_5 - 60的 FOV (Vertical),在16 : 9的显示比例下,换算成水平FOV是 8.88 - 91.52 之间_
|
||||
很多卡通角色的模型在制作时面部正面会选择做扁,一部分原因就是为了抵消高FOV带来的面部轮廓变形。
|
35
03-UnrealEngine/卡通渲染相关资料/演讲笔记/LoveLive All Star笔记.md
Normal file
35
03-UnrealEngine/卡通渲染相关资料/演讲笔记/LoveLive All Star笔记.md
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: LoveLive All Star笔记
|
||||
date: 2022-10-13 17:36:10
|
||||
excerpt:
|
||||
tags: 卡通渲染
|
||||
rating: ⭐
|
||||
---
|
||||
原文:CEDEC 2020 - 高品質かつ低負荷な3Dライブを実現するシェーダー開発 ~『ラブライブ!スクールアイドルフェスティバル ALL STARS』(スクスタ)の開発事例~
|
||||
|
||||
## 描边
|
||||
采用BackFace挤出的方法。
|
||||
- 考虑相机距离与Fov宽度修正
|
||||
- 支持模型内部轮廓线
|
||||
- 可通过定点色微调
|
||||
|
||||
### 模型内部轮廓线
|
||||

|
||||
|
||||
使用PixelShader对顶点色的B通道进行二值化所得。
|
||||
|
||||
### 顶点色
|
||||
R:轮廓粗细
|
||||
G:把不需要轮廓压入模型
|
||||
B:控制轮廓粗细
|
||||
|
||||
## 灯的轮廓光效果
|
||||

|
||||
View空间的方向光强度Ramp。FlashYiYi有说过(**存疑**)。
|
||||
- 方向をZ軸(デフォルト)にすれ䜀、逆光表現䛾リムライト
|
||||
- 方向を設定すれ䜀、平行光源䛾ライティング
|
||||
|
||||
根据方向光的DirectionVector来Fake一个方向光Ramp;如果方向是完全背对角色则使用RimLighting效果。
|
||||
|
||||
## 边缘光与轮廓线的混合
|
||||

|
118
03-UnrealEngine/卡通渲染相关资料/演讲笔记/蓝色协议的方案.md
Normal file
118
03-UnrealEngine/卡通渲染相关资料/演讲笔记/蓝色协议的方案.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# 蓝色协议
|
||||
- https://zhuanlan.zhihu.com/p/405540279
|
||||
- https://zhuanlan.zhihu.com/p/229621134
|
||||
|
||||
## 顶点色存储数据
|
||||
用于存储一些低精度数据,插值即可
|
||||
- R:阴影区域标记
|
||||
- G:描边宽度(描边Mask)
|
||||
- B:Ao
|
||||
|
||||
第二套顶点色:
|
||||
- R:深度偏移,用处下文会提到
|
||||
- G:用来区分内轮廓不同部位的ID
|
||||
导入时被存到了UV Channel 1中。
|
||||
|
||||
## BaseColor、ShadowColor
|
||||
- 假设固定一个全局HSV值通过偏移来获取ShadowColor。通过方向光的阴影来对ShadowColor与BaseColor插值。
|
||||
- 使用HalfNol,之后使用step(Threshold,HalfNol)
|
||||

|
||||
- 使用一个GBuffer存储NoL信息来减少相关二值化参数。
|
||||
|
||||
局部阴影(贴图绘制)与自投影阴影的叠加,推荐使用虚幻的阴影与AO叠加算法。
|
||||

|
||||
|
||||
## 多光源策略
|
||||
只有主光在计算中会计算暗部颜色,这样就能防止多光源时,暗部被反复提亮的问题。除此之外,剩下的点光源只用于提亮,并且忽略法线,只计算距离衰减,防止照出难看的结构。这样的效果还是挺二次元的。
|
||||
|
||||
## 高光
|
||||

|
||||

|
||||
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/蓝色协议_自定义形状高光.png]]
|
||||
|
||||
SpcularMask高光,理论上这部分应该在Lighting阶段计算的,但是第二步的绘制SpecularMask、ID?、XY轴心偏移。感觉这部分是材质编辑里计算出来得高光。
|
||||
|
||||
## 天光与间接光处理
|
||||
为了防止环境光把角色照出立体感,所以计算环境光时,会把法线全部看作世界空间上方向来处理。同时增加了一些参数可以进行一些定制化调整。
|
||||
|
||||
## RimLight
|
||||
贴图:
|
||||
- RimLightMask
|
||||
- RimLightWidth
|
||||
|
||||

|
||||

|
||||
使用描边计算的方式最后计算出Rim的范围。
|
||||
|
||||
## 轮廓
|
||||
蓝色协议的轮廓由3部分组成:
|
||||
- **预先绘制**
|
||||
- **Mesh挤出**
|
||||
- **后处理描边**
|
||||
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/BlueProtocol_Outline.jpg]]
|
||||
|
||||
### 预先绘制
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/BlueProtocol_DrawOutlineTexture.png]]
|
||||
|
||||
### Mesh挤出
|
||||
蓝色协议用用模型扩边法绘制**头部、下颚、嘴、耳朵**区域。不使用后处理的原因是:解决使用后处理描边后,鼻子处会被勾到的问题。
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/BlueProtocol_面挤出2.png]]
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/BlueProtocol_面挤出3.jpg]]
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/BlueProtocol_面挤出4.jpg]]
|
||||
|
||||
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/BlueProtocol_面挤出1.png]]
|
||||
>模型扩边用了单独储存共点平均法线的方式来修复分叉,是常见做法。它并没有继续解释,我在这补充一下,这个描边法线想在蒙皮网格上正常使用需要转到切线空间,或者直接储存在切线上。可以参考:【Job/Toon Shading Workflow】自动生成硬表面模型Outline Normal https://zhuanlan.zhihu.com/p/107664564
|
||||
|
||||
如果想做得更好一点,还可以根据法线的角度方差来增强这部分的描线宽度,这样就能把发尖做出来。**也就是制作一个Mesh后处理工具,遍历当前顶点法线与其他附近法线,根据差异度(方差)大小对法线进行伸缩**
|
||||
|
||||
### 后处理描边
|
||||
绘制**非头部**区域,PostOutlinePass位于Lighting与透明物体绘制之间,采用4张贴图进行计算。
|
||||
1. 使用Sobel过滤器进行深度检测描边。只勾数值差别较大的区域。
|
||||
- ![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/BlueProtocol_DepthTestOutline.png]]
|
||||
- 最左边的是顶点色存的深度偏移值,用它偏移中间的深度图之后就可以勾出嘴、口腔内的、脸的轮廓。
|
||||
2. 使用Sobel过滤器进行Id图检测描边。只勾数值差别较大的区域。
|
||||
- ![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/BlueProtocol_IDOutline.jpg]]
|
||||
3. 使用Sobel过滤器进行Normal检测描边(使用点积dot的方式)。对同一个ID区域内深度差异小进行检测,通过获取周围点法线求点乘的方式判断出轮廓。
|
||||
- ![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/蓝色协议_Normal检测描边.png]]
|
||||
- ![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/BlueProtocol_NormalDotTestOutline1.jpg]]
|
||||
- ![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/BlueProtocol_NormalDotTestOutline.png]]
|
||||
4. 叠加预先画好的轮廓(GBuffer)。
|
||||
- ![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/BlueProtocol_DrawOutlineInGBuffer.png]]
|
||||
|
||||
最终合成结果:![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/BlueProtocol_FinalyOutline.png]]
|
||||
|
||||
### 眉毛勾线
|
||||
头发将在BasePass之后通过实现一个ToonHairPass进行绘制。(眉毛依然在BasePass中进行绘制)以此来实现透明头发的效果。
|
||||
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/CEDEC2021_BLUEPROTOCOLにおけるアニメ表現手法(実装編)-60.png]]
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/CEDEC2021_BLUEPROTOCOLにおけるアニメ表現手法(実装編)-61.png]]
|
||||
|
||||
眉毛的轮廓会绘制在Hair上。
|
||||
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/CEDEC2021_BLUEPROTOCOLにおけるアニメ表現手法(実装編)-62.png]]
|
||||
|
||||
### 使用Responsive AA解决Outline融化问题
|
||||
Responsive AA处理:
|
||||
TAA会导致勾线变糊,所以用了UE的Responsive AA,用stencil标记了勾线部分,这部分在做帧间混合时就少混合一些。虽然会导致锯齿更强,但是动态画面里是可以接受的程度。
|
||||
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/CEDEC2021_BLUEPROTOCOLにおけるアニメ表現手法(実装編)-64.png]]
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/CEDEC2021_BLUEPROTOCOLにおけるアニメ表現手法(実装編)-63.png]]
|
||||
![[08-Assets/Images/ImageBag/UrealEngineNPR/BlueProtocol/CEDEC2021_BLUEPROTOCOLにおけるアニメ表現手法(実装編)-65.png]]
|
||||
|
||||
## 额头发与衣服阴影
|
||||
使用接触阴影这种深度偏移后处理阴影来实现。
|
||||
|
||||
## GBuffer
|
||||
```c#
|
||||
GBufferB:Metallic/Specular/Roughness=>ToonHairMask OffsetShadowMask/SpcularMask/SpecularValue
|
||||
OutGBufferD = CustomData.xyzw=》ShaderColor.rgb/NoL
|
||||
OutGBufferE = GBuffer.PrecomputedShadowFactors.xyzw=》 /RimLightMask/DiffuseOffset/RimLightWidth
|
||||
OutGBufferF = velocity => OutlineWidth/OutlineID/OutlinePaint/OutlineZShift
|
||||
```
|
||||
关闭角色的预计算阴影。
|
||||
|
||||
## 管线
|
||||

|
69
03-UnrealEngine/卡通渲染相关资料/演讲笔记/赛马娘.md
Normal file
69
03-UnrealEngine/卡通渲染相关资料/演讲笔记/赛马娘.md
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: 赛马娘
|
||||
date: 2022-10-22 12:11:21
|
||||
excerpt: 摘要
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
- https://zhuanlan.zhihu.com/p/441159789
|
||||
- https://zhuanlan.zhihu.com/p/441560356
|
||||
- 原文:https://game.watch.impress.co.jp/docs/kikaku/1366154.html
|
||||
|
||||
|
||||
为了获得到“干净”的描边,我们需要调整法线,但是受法线影响的还有其他要素。如果我们直接调整模型的法线,阴影和边缘会受到干扰。如果我们用模型原法线来做扩边,则会出现描出的边断裂。为了解决这个问题,他们把用于扩边的法线调整后存在一个UV里,扩边时用UV中的法线计算。这样,阴影和描边的计算就可以兼容了。
|
||||
|
||||

|
||||
|
||||
> 左:用OutLine的法线进行全部着色的话,边缘处和阴影就都乱了。
|
||||
> 右:用原来计算阴影的法线进行全部着色的话,阴影虽然很对但描的边会出现断裂。
|
||||
|
||||

|
||||
|
||||
> 对策:用两种法线分别解决
|
||||
> 上面是阴影和边缘等用的基本模型法线,下面做描边时用的处理过的法线。右边是我们存放处理过的法线的新增UV图的预览。
|
||||
|
||||
实际的调整方法就是直接手工调整原模型的法线。其中最有必要调整头发束之类的三角形面变化剧烈的部分。
|
||||
|
||||

|
||||
|
||||
> 左侧调整前,我们可以看到一些接缝处或法线变化剧烈的地方描的线出现断裂,而右侧调整后的法线则效果较好。
|
||||
|
||||
为了和动画风格相近,赛马娘会根据场景与演出来调整描边的颜色。
|
||||
|
||||
## 眼睛
|
||||
此外,马娘的眼睛是使角色具有吸引力的重要元素,但赛马娘游戏特别注重眼睛中的高光(high light)。除了制作漫反射纹理外,也加了张高光专用的纹理。以此组合出多彩的瞳孔表现。
|
||||
|
||||
高光可以改变自身的强度,包括垂直和水平的旋转和平移。在马娘们情绪高涨到快哭时,使用更大的高光和更多的高光粒子等。
|
||||
|
||||

|
||||
|
||||
> 上方为基础高光,下方为补充的粒子高光。
|
||||
|
||||

|
||||
|
||||
> 改变高光强度:通常、强高光,无高光。
|
||||
|
||||
此外,还有单独的高光动画。马娘快要哭出来的时候,高光会小范围内颤动。在兴致勃勃时,高光会闪闪发光并绕眼睛转一圈。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
「寄」になった.jpg
|
||||
|
||||
> 醒目飞鹰发动技能时眼睛的小高光颤动特效
|
||||
|
||||

|
||||
|
||||
> 米浴在演唱会时的眼睛中也有小高光颤动特效
|
||||
|
||||

|
||||
|
||||
> 胜利奖券在赢下比赛时眼中同时使用了两种高光。
|
||||
|
||||
赛马娘通过结合面部表情的情感表达和眼睛高光的情感表达。更加自然的表现出了马娘们的情感,突出了马娘们的个性,让用户更加轻易的入坑。
|
||||
|
||||

|
||||
|
||||
> 总结:表情部分总结,表情系统是让马娘们能更加自然表达出情感的系统。
|
||||
> 利用表情和眼睛来达到各种效果。
|
BIN
03-UnrealEngine/卡通渲染相关资料/米哈游演讲/米哈游技术总监贺甲:如何实现次世代卡通渲染效果.pdf
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/米哈游演讲/米哈游技术总监贺甲:如何实现次世代卡通渲染效果.pdf
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/米哈游演讲/米哈游高质量动态渲染技术.pdf
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/米哈游演讲/米哈游高质量动态渲染技术.pdf
Normal file
Binary file not shown.
Reference in New Issue
Block a user