This commit is contained in:
2023-06-29 11:55:02 +08:00
commit 36e95249b1
1236 changed files with 464197 additions and 0 deletions

1
02-Note/.keep Normal file
View File

@@ -0,0 +1 @@
this file is created for keeping the folder after git.

11
02-Note/02-Note.md Normal file
View File

@@ -0,0 +1,11 @@
# 02-Note Overview
```ccard
type: folder_brief_live
noteOnly: true
style: card
```
### 网址收藏
- [[模型资源]]
- [[资源笔记]]
- [[UE知识库]]

View File

@@ -0,0 +1,638 @@
## DMX
渲染模型&材质:
- SM_Strobe_Lens M_MI_Lens_Strobe不透明材质指令数254
- SM_Beam_RM M_Beam_Master
- BP_StaticMatrix M_MatrixBeam_Master
### Beam材质
变量:
- NSliceNearPlane 交点世界坐标
- NDepth
- FSliceFarPlane 交点世界坐标
- FDepth
MF_WSIntersection如果没有相交RelIntersectPos为IntersectDistance为。
### 优化方案
1. DMX模型改成StaticMeshInstanceGun的模型是Masked建议改成不透明。
2. 关闭DMX灯光中的照明效果因为不会对着模型照明。
1. 场景中的组件勾选**Disable Lights**
3. StaticMeshLens 组件可以按照距离进行关闭。BP_StaticStrobe_C、BP_MovingHead_C
1. 场景中的组件中的StaticMeshLens去掉**Visible**。
2. Sequence中手动设置**Visible**。
4. Beam材质
1. 给BeamRayMarch的DMX Max Light Distance 乘上一个变量倍率之后适当减少DMX Max Light Distance大小建议的倍率为1.2~1.5长度为1/1.2~1/1.5 给BeamRayMarch的DMX Zoom 乘上一个变量倍率Zoom 5=>4.5~3并且将倍率调整成合适数值。
2. 调整Zoom Quilty 1=>1.5~2并且适当调整DMX Lens Radius的数值减少断点问题。
5.
## 优化目标
XR 2k 2048*1080 50帧 => 3440 * 1440 50帧0
## 优化场景
E:\Project\yanjingdan\BengHuai\Ture\Content\UnWorld\BengHuai_True\Maps
E:\Project\yanjingdan\BengHuai\L_Dacapo\content\UnWorld\BengHuai_Dacapo\Maps\L_Dacapo5.umap
E:\Project\yanjingdan\BengHuai\BengHuai\Content\UnWorld\BengHuai_Tiantai\Maps\L_BengHuai_Tiantai.umap
步骤:
1. 场景静态模型设置成Nanite以下模型需要额外考虑是否开启Nanite
1. 带有世界位移与Masked的模型比如花 草 模型。
2. 将一些不会动的模型都设置成Static。
3. 告知拍摄镜头需求,是全景还是需要360°都有拍摄要求。
4. 确定场景之后不会进行改动之后使用MergeActor将模型中的相同材质进行合并将场景中的静态模型设置成Instance。
### L_BengHuai_Tiantai
问题:
~~1. 模型问题
1. 目前比较大的问题在于模型较多、面数较高且场景静态模型没有设置成Nanite。使得剔除与BasePass渲染延迟过高。~~
#### Model
~~1. 场景中大量巨大无异议的球名字为Sphere~SphereX~~
~~2. 图元较多的关卡
1. L_Main_AllCity_Chengbao_2~~
- dd_polySurface
- BookOpen
- Box001~Box003
-
#### Light&Shadow
1. SpotLightDingGuang
1. 去掉Cast Shadow只影响台阶的阴影)
2. 勾选Distance Field Shadows。
2. RectLight 81~93
1. 去掉Cast Shadow。
2. 将其替换成SourceLength 匹配的PointLight。
3. 将其他面光源替换成点光源或者聚光灯可以减少0.1~0.2ms,优先级低)。
#### Translucent
1. 修改EasyFog的材质(大纲搜索EasyFog从Translucent=>Additive。并且重新调整亮度。
2. 大纲搜索EnvEmitter调整大小并使用Cutoff工具对齐进行裁剪。覆盖范围过大。修改材质MI_Env_Inst~MI_Env_Inst22从Translucent=>Additive。并且重新调整亮度。
#### 2023.6.5优化建议
1. 舞台观众、荧光棒去掉CastShadow同时考虑使用Nanaite
2. 浮游炮材质重Masked=>不透明
3. 烟花材质重Translucent=>Additive
4. Sequence进入舞台镜头时对看不见的建筑物进行隐藏操作。
5. 主舞台所有模型进行Merge并且设置成Nanite。
6. 主舞台DMX灯光开启DisableLight。
7. 调整Lumen渲染参数或者后处理盒子参数
- Lumen Scene Lighting Quality 2 =>0.25
- Lumen Scene Detail4=>1
- Lumen Scene View Distance20000=>1000~3000
- Max Trace Distance20000=>1000~3000
### L_Dacapo5
问题:
1. 模型问题
1. 模型较多、面数较高且场景静态模型没有设置成Nanite。使得剔除与BasePass渲染延迟过高。
2. 花草不是Instance
3. 花卉、草的的材质有优化空间。
2. 阴影
1. 有2个方向光投射阴影关闭其中一个。并且使用距离场阴影。
3. Lumen
1. 设置Lumen参数去除草地对GI的计算。
4. 天空上的透明体积雾效果
BasePass渲染延迟较高的模型
HUABAN_toon hua 123 instances 19501-19515 4775.936
grass0 hua 123 instances 19481-19489 4387.84
MI_grass_01_Inst_gbl SM_grass_01_gbl_lv 31 instances 14016-14025 2996.224
HUABAN_PINK_toon1 hua_pink 35 instances 15286-15291 1172.416
grass0 hua_pink 35 instances 19491-19499 1107.968
HUAXIN_toon_toon1 hua 123 instances 15266-15274 572.416
MM_Grass HUA002 2 instances 14405-14415 440.32
M_plants SM_sf_flower_05 13548-13551 193.536
M_plants SM_sf_flower_05 13739-13742 191.488
M_plants SM_sf_flower_05 13734-13737 167.936
HUAXIN_toon_toon1 hua_pink 35 instances 15276-15284 164.832
M_plants SM_sf_flower_05 13669-13672 128.00
M_plants SM_sf_flower_05 3 instances 13664-13667 124.928
### L_True
1. 模型问题
1. Nanite与Instance主要是电视机
2. VolumetricSpotlightEngine 是拿来干什么的?
3. 灯光
1. 关闭投射阴影(尤其是点光源,包括天光)
1. 天光
2. SpotLight11
3. SpotLight12
4. SpotLight2
5. DirectionalLight
6. PointLight1~6
2. 调整灯光投射范围(有一些太远了)
4. 13_Mat_DianShiJi14 贴花看不出有什么作用。
5. 黑色遮罩的几个Plane是干什么的
6. 中间的几个透明片删掉
7. 中间数据库的材质重Tranlucent => Additive
8. 几个透明的粒子效果将材质从Tranlucent => Additive 鬼影问题很大)
BasePass渲染延迟较高的模型
monitorE_col_High_A_Mat2 monitorE_High_Geo_Grp 90 instances 12336-12351 2444.288
TTest monitorE_High_Geo_Grp 28 instances 12006-12014 761.856
monitorE_col_High_A_Mat2 monitorE_High_Geo_Grp 12 instances 12353-12357 337.92
monitorE_col_High_A_Mat monitorE_High_Geo_Grp 11 instances 12837-12852 308.224
monitorF_col_high_A_Mat monitorF_High_Geo_Grp 107 instances 12870-12884 197.632
TTest monitorE_High_Geo_Grp 6 instances 11961-11969 183.296
monitorG_col_High_A_Mat3 monitorG_High_Geo_Grp 66 instances 11542-11552 159.7
monitorA_col_High_Mat3 monitorA_High_Geo_Grp 168 instances 12576-12590 153.536
monitorD_col_High_A_Mat monitorD_High_Geo_Grp 27 instances 12283-12297 135.168
monitorG_col_High_A_Mat1 monitorG_High_Geo_Grp 47 instances 11448-11480 115.648
## L_XBLA xiubolian休伯利安
1. 移除没有明显投影效果的灯光。
2. 重写DMX插件CPU GPU 负载压力较大。
3. Separate Translucency去除勾选。
### 模型优化
1. 将场景中的静态模型都设置成Nanite。
2. 将BP_FeiChuan模型进行合并。
3. 如果飞船模型如果需要CastShadow则需要在模型里勾选Generate Distance Field。
#### xbla
开启Nanite以及勾选Generate Distance Field。
MininumResidency2MB
Fallback Relative Error1.0=> 0.1~0.0
#### 关闭FeiChuang蓝图中StaticMesh的Shadow
### Light&Shadow
- [ ] 远处的几个小飞船 去掉CastShadow选项。
- DirectionalLight
- Num Dynamic Shadow Cascades5=>3
- Dynamic Shadow Distance MovableLight20000=>5000
- DistanceField Shadow Distance51200=>5000
- Shadow Resolution Scale1=>0.1~0.5
- SpotLight3
- 关闭CastShadow(3与4至少关一个)
- Attenuation Radius11052.831055=> 2040
- Shadow Resolution Scale1=>0.5
- 第二次修改
- Outer Cone Angle 44=>40
- Shadow Resolution Scale0.5=>0.3
- SpotLight4
- 关闭CastShadow(3与4至少关一个)
- Attenuation Radius12052.831055=>4000.0
- 点光源
- 关闭阴影
- 降低Attenuation Radius到合适范围。
- 删除机翼处重新的点光源pointLight81
### Lumen
调整Lumen渲染参数或者后处理盒子参数
Lumen Scene Lighting Quality 2 =>1
Lumen Scene Detail4=>1
Lumen Scene View Distance20000=>5000
Max Trace Distance20000=>5000
### Translucent
#### Nebula
以下几个建议删除:
BP_NebulaProceduralGenerator
BP_NebulaProceduralGenerator2
BP_NebulaProceduralGenerator3N
BP_NebulaProceduralGeneratorMove
BP_NebulaProceduralGeneratorMove2
- BP_NebulaProceduralGenerator5
- Volumetric Sphere Amount10=>1~5推荐1
- 效果会大变
- Min Sphere Radius (Km)0.25=> 0.1~0.2
- Max Sphere Radius (Km)0.75=> 0.6~0.7
- BP_NebulaProceduralGenerator6
- Volumetric Sphere Amount10=>1~3推荐1
- QualityHight => Low~Medium
- 效果会大变
- Min Sphere Radius (Km)0.25=> 0.1~0.2
- Max Sphere Radius (Km)0.75=> 0.6~0.7
远景星云考虑使用SceneCapture渲染出来再贴到一个片上。
#### StarCluster
建议使用Niagara重做一个透明区域太多了。
## Domineer_Showcase
SequenceSeq_Seq_Domin_XR
鬼影问题:
1. 修改材质从Masked=> Translucent并且开启材质的Responsive AA。
## NightGlow
### ~~CustomDepth~~
- MI_Icelandic_Rock_Cliff_ucdmcawdy_4K_Inst2_Normal
没有特殊需要把PostProcessing-Custom Depth-Stencil Pass改成Disable。
### Shadow
- ~~SpotLight ~~
- ~~SpotLight2 ~~
- ~~SpotLight4
- ~~geogrp29下面的灯~~
- stone_Anim2
CasShadow关了。
### ~~Lumen~~
- Lumen Scene Lighting Quality 1=>0.25
- Lumen Scene View Distance20000=>1000
- Max Trace Distance20000=>1000
或者考虑换成烘焙阴影(不太合适)。
### Nanite
特效Mesh
~~- NS_Storns
- SM_Strom_piece_1884
- SM_Strom_piece_1875
- SM_Strom_piece_1900~~
### 场景模型控制 & WuTai破碎
- 在舞台模型出现后使用Sequence将wutai破碎下面的破碎模型都隐藏了。
- 将外部六棱柱与内部六棱柱分开导出并将内部六棱柱转换成Instance Static Mesh。
- 使用StaticMesh替换Landscape。
### 透明物体
- EasyFog材质从Translucent=>Additive
- SM_Cloud_Single的材质从Translucent=>Additive
- geogrp的NewMaterial1_Inst从Translucent=>Additive
### 2023.6.11
- GameThread 13.4ms
- ~~WorldTick 4.3ms =>3)~~
- ~~进地形改成StaticMesh选中地形File => Selected (1.3ms)~~
- ~~减少NiagaraActor数量。~~
- RenderThread 43.1ms
- InitViews 9.8ms
- GatherDynamicMeshElements 7.4 ms
#### 理论上WuTai优化极限
- 关闭光线追踪
- 体积云参数调节
-
删除模型测试:
- 原始状态延迟29~32
- 删除WuTai FbxScene_zhuzi 21~22
- 删除FbxScene_zhuzi2~8 28~30
- WuTai 全部删除 23~25
- Wutai1 几乎没有变化
- Wutai2 26~28
- Wutai3 26~28
替换模型之后19~21ms
##### 可以Instance的WuTai网格
删除以下网格并且使用 替换
- piece_0
- piece_1
- piece_2**
- piece_4**
- piece_6**
- piece_7
- piece_8
- piece_9
- piece_10**
- piece_13**
- piece_14
- piece_15**
- piece_18**
- piece_21**
- piece_23**
- piece_24
- piece_25
- piece_26**
- piece_28**
- piece_29
- piece_30
- piece_31
- piece_32**
- piece_35**
- piece_36**
- piece_38**
- piece_40**
- piece_41
- piece_42**
- piece_45**
- piece_46
- piece_47
- piece_48**
- piece_51**
- piece_53**
- piece_54**
- piece_56**
- piece_57
- piece_58
- piece_59
- piece_60
- piece_61
- piece_62**
- piece_64**
- piece_65
- piece_66
- piece_67
- piece_68
- piece_69
- piece_70
- piece_71
- piece_72
- piece_73
- piece_74
- piece_75
- piece_76
- piece_77
- piece_78**
- piece_81**
- piece_82**
- piece_84**
- piece_85
- piece_86
- piece_87
- piece_88
- piece_89
- piece_90**
- piece_93**
- piece_94**
- piece_97**
- piece_98
- piece_99
- piece_100
- piece_101
- piece_102
- piece_103**
- piece_105**
- piece_107**
- piece_108
- piece_109**
- piece_112**
- piece_115**
- piece_117**
- piece_118
- piece_119
- piece_120
- piece_121
- piece_122**
- piece_124**
- piece_125
- piece_126**
- piece_104
- piece_12
- piece_44
- piece_92
- piece_123
- piece_63
- piece_5
- piece_11
- piece_33
- piece_111
- piece_79
- piece_17
- piece_22
- piece_37
- piece_43
- piece_114
- piece_27
- piece_34
- piece_106
- piece_116
- piece_110
- piece_16
- piece_19
- piece_83
- piece_95
- piece_20
- piece_49
- piece_50
- piece_91
- piece_113
## L_Dacapo6
### CPU
- CPU粒子删除看不到的Fog
- NewBlueprint
- P_Wind
- BP_AddCommand
- BP_LightControl
### GPU
#### 植被
镜头:
- 1612 GPU 37ms
- 2568 GPU 38ms
- 3233 GPU 38ms
1. 删除数量为0的植被
2. 将所有植被模型设置为Nanite。~~地形启用Nanite。~~
1. 转Nanite之前需要修改材质函数MF_Grow将里面的ObjectPosition节点改成float3(0,0,0) => TransformPosition(Instance&ParticleSpace)这2个节点
3. r.Nanite.AllowWPODistanceDisable
4. r.Lumen.DiffuseIndirect.MeshSDF.RadiusThreshold 100
5. 隐藏额外的灯光006~013并且将非卡通角色照明灯光的ToonIntensity设置为0
6. 将DMX灯光相关的模型用Sequence控制隐藏一下。
7. 将建筑物都使用默认材质进行替换。不要使用卡通材质。
8. r.Shadow.Virtual.Cache.MaxMaterialPositionInvalidationRange 1000
9. SM_Beam_RM 系列用Sequence设置可见性。
### 删除额外的东西
- 删除额外Landscape2使用StaticMesh代替
### Sequence 场景可见性优化
Squence设置物体可见性
- NewBlueprint
### Lumen
PostProcessVolumn => Lumen
- Lumen Scene Lighting Quality 2 => 1
- Lumen Scene Detail2 => 1
- Final Gather Quality3 => 1
- Lumen Scene View Distance20000 => 5000
- Max Trace Distance20000 => 5000
### 2023.6.10
- 删除场景中的垃圾
- NewBlueprint系列
- 需确认用途
- SM_Clouds_346_3
- SM_Clouds_346_01
- 替换成StaticMeshInstance 并且转成Nanite
- ZhuDingHongEXP~ZhuDingHongEXP1659
- stone目录下的模型 SM_ruins_20~SM_ruins_87
- 新建文件夹1目录下的Cube~Cube18
- JuHua1244
- SHIKUAI_对象001
- SM_cliff_stone
- SM_GrassClumpFonce
- ZhuDingHongEXP2026
- 优化粒子 Fx_WaterFall
- 减少粒子数量以及生命。
- 删除所有镜头都看不到的粒子。大致可以删掉一半一些只有单个镜头可以看到的可以用Sequence手动K一下可见性。
- 替换粒子改成GPU或者面片或者直接删除
- P_Wind
- GameThread
- 删除额外的2个地形并使用一个地形进行还原。
- RenderThread
- InitViews
- 几个角色脸部表情骨骼体 CastShadow 去掉
-
## L_Rubia
- PlanearReflection 删除,因为已经使用了屏幕空间反射。
- 因为场景都是透明物体切没有Nanite会导致VSM无法缓存所以需要使用ShadowMap。
## L_LiangZiZhiHai
### 模型转Nanite
- zhuwutai_Cube25
- wutai003
- SM_BENHUAI_BOX_1
- SM_BENHUAI_BOX_2
- SM_BENHUAI_BOX_6
- SM_BENHUAI_BOX_5_DJ
- SM_BOX_HEZI_HEKUAI
- SM_BOX_HEZI_HEKUAI_GanCui
- SM_GaoSuiBox
- SM_KongZhonXuanFU
- SM_LiaBianBox
- SM_ShuiJing_box_ID3
### 灯光&Shadow
- 关闭点光源阴影
### 透明材质Translucent => Additive
- M_StaticMeshSkinParticle_Inst
- MI_Env_Inst2
- M_XuanWo_Inst1
- Stage_SeaOfQuanta_ErodedLand_UI06_Mat_Inst
- M_BP_EasyFog_2
- M_SkyWhiteJianBian_Inst1
### Lumen
PostProcessVolumn => Lumen
- Lumen Scene Lighting Quality 1 => 0.25
- Lumen Scene View Distance20000 => 1000
- Max Trace Distance20000 => 1000
### 粒子(严重影响性能!考虑删除或者优化)
- EnvEmitter_System02
- BP_EasyFog_2
### 合并模型
将场景的中的圆环状模型、Box模型合并一下。
## L_JianZhong
### Shadow
- Ultra_Dynamic_Sky中的方向光
- Dynamic Shadow Distance MovableLight20000 => 10000
- Num Dynamic Shadow Cascades5 => 1
- Distance Field ShadowsTrue => False
- 关闭阴影
- SpotLight17
- SpotLight18
- SpotLight
### 模型优化
- 将场景中的石头、后景石头、剑以及剑下面的石头都转成Instance
- 场景中的模型转成Nanite。
- 相机背后看不到的模型都删除了。
- 删除场景中的CrystalFlw模型
- 将场景中的重复模型使用单个模型重新摆放最后设置成Instance。
### Shadow
- 关闭阴影
- PointLight
- PointLight4
- PointLight5
- PointLight6
- SpotLight2
- SkyLight
- VolumetricSpotlightEngine4的SpotLight
- 删除灯光
- PointLight2
- PointLight3
- DirectionalLight
- Dynamic Shadow Distance MovableLight20000 =>4000
- Num Dynamic Shadow Cascades5 => 1
- DistanceField Shadow Distance4000
- DistanceField Trace Distance25000 =>2000~4000
- Shadow Resolution Scale1 => 0.1
### Lumen
PostProcessVolumn => Lumen
- Lumen Scene Lighting Quality 2 => 0.25
- Lumen Scene Detail2 => 1
- Final Gather Quality3 => 1
- Lumen Scene View Distance20000 => 1000
- Max Trace Distance20000 => 1000
ProjectSettings - Lumen 以下设置仅提高1~2帧
- Use Hardware Ray Tracing when available
- High Quality Translucency Reflections
- Software Ray Tracing ModeDetailTracing => GlobalTracing
- r.Lumen.TraceMeshSDFs.Allow = 0
#### 添加DefaultScalability.ini
将Hight级别的Lumen设置覆盖到Epic级别设置。
### Fog
- ExponentialHeightFog1关闭体积雾。
### 粒子 转换成GPU粒子或者尽量减少CPU粒子数量
- NS_TextRain_Copy (字符雨生成器)
- NS_Test
- NS_Test_25
- NS_STM_SkinParticle
- M_StaticMeshSkinParticle_Inst
### 模型
- SkyBox (删除)
- 透明片Plane22~Plane41使用的材质的透明度是0考虑删除。
- 隐藏电路板Plane~138
- 黑色遮罩Plane4~Plane19
- **Nanite & 转成Instance**
- box1
- monitorG_High_Geo_Grp_2
- **转Nanite & 合并 || 删除看不到**
- monitorG_High_Geo_Grp_2
- SM_DIANSHIJI_HeCheng100
### Sequence中控制隐藏粒子与看不到的模型电视机
- monitorF_High_Geo_Grp12~1516
### 材质Translucent=>Additive
- M_MovingLineTest_Inst1_TRUE
- M_MovingLineTest_Inst1_TRUE1
- M_MovingLineTest_Copy
- BengHuai_True/Material
- M_BeiJing
- M_BeiJing_Inst
- M_OP~M_OP3
### 2023.6.13
1. 电视机模型转Instance。
2. 减少字符雨数量
3. 关闭光线追踪
4. ExponentialHeightFog1关闭体积雾。
#### ~~电视机贴图有拖影问题~~
## DLSS & TSR & FSR 测试
测试场景L_True分辨率4K 100%笔记本3070镜头sequence 604 的1840帧
- TSR ScreenPercentage66 GPU帧数27~29
- TAA ScreenPercentage100 GPU帧数36~37
- FSR+TAA ScreenPercentage66 GPU帧数26~28
- DLSS+TAA ScreenPercentage66 GPU帧数26~28
可以在DefaultEngine.ini里面添加渲染分辨率
[/Script/Engine.RendererSettings]
r.screenpercentage=66
r.screenpercentage=50
## L_BengHuai_Oaths_01
### 灯光
## L_BengHuai_Oaths_Close
### 灯光
# Lumen
r.Lumen.ScreenProbeGather.AdaptiveProbeAllocationFraction =0.5 =>0.1

18
02-Note/DAWA/DAWA.md Normal file
View File

@@ -0,0 +1,18 @@
# DAWA Overview
```ccard
type: folder_brief_live
imagePrefix: '08-Assets/Images/BingWallpaper'
noteOnly: false
style: card
```
[[柔体模拟开发计划]]
[[UE卡通渲染改进]]
# 流程架设计划
1. 共享DDC
2. 推广Lyra的UEditorValidator Asset有效性检测插件。
3. 联机编译Shader
4. DDNS 与 远程
5. NAS 网盘
6. 版本管理系统 PlasticSCM 与 Perforce
7. UnrealGameSync 以及 CI/CL系统

View File

@@ -0,0 +1,135 @@
## 光猫
057152243127
550743
telecomadmin
15675507
## 路由器
## Netgear
- admin
- dawa
- 上网dawa2022
## FreeNAS账号密码
- root
- dawa
## 服务
- SVN
- http://192.168.0.236:7777/svnadmin
- http://192.168.0.236:7777/svn/DAWA_SVN
- portainerhttp://192.168.0.236:9000
## FreeNAS搭建服务流程
玩转Freenas系统06——freenas下使用docker配置RancherOS/Portainer https://www.bilibili.com/video/av77422499/?vd_source=d47c0bb42f9c72fd7d74562185cee290
Docker虚拟机账号:Rancher
密码Docker
Root模式`sudo -i`
### 固定IP命令
bxe1为网卡口。所以创建虚拟机的时候需要设置成bxe1。之后在虚拟机里固定IP还是看虚拟机的局域网IP接口是啥
```c++
sudo ros config set rancher.network.interfaces.eth0.address 192.168.0.236/24
sudo ros config set rancher.network.interfaces.eth0.gateway 192.168.0.252
sudo ros config set rancher.network.interfaces.eth0.mtu 1500
sudo ros config set rancher.network.interfaces.eth0.dhcp false
sudo ros config set rancher.network.interfaces.dns.nameservers "['192.168.0.252']"
```
### 挂载宿主NFS共享文件夹
在FreeNAS里开启服务`NFS`。进入目录
```bash
cd /var/lib/rancher/conf/cloud-config.d
```
下载脚本
```bash
wget https://gist.githubusercontent.com/superseb/15aada95f6f2fbacc1af7a442c767cce/raw/158c8d0161ec6416aa558d3dad4d1e2afb026d89/cloud-config.yml
```
脚本如下:
```yml
#cloud-config
write_files:
- path: /etc/rc.local
permissions: "0755"
owner: root
content: |
#!/bin/bash
mkdir -p /mnt/nfs-1
mkdir -p /mnt/nfs-2
cloud-init-execute
rancher:
services:
nfs:
image: d3fk/nfs-client
labels:
io.rancher.os.after: console, preload-user-images
io.rancher.os.scope: system
net: host
privileged: true
tty: true
restart: always
volumes:
- /usr/bin/iptables:/sbin/iptables:ro
mounts:
- ["nfs_server_ip:/", "/mnt/nfs-1", "nfs4", ""]
- ["nfs_server_ip:/", "/mnt/nfs-2", "nfs4", ""]
```
将`- ["nfs_server_ip:/", "/mnt/nfs-1", "nfs4", ""] `修改成NAS服务器ip后面加上NFS共享目录路径:
```yml
- ["192.168.0.235:/mnt/zpool_main/DockerHost", "/mnt/nfs-1", "nfs4", ""]
```
## 服务部署
### portainer
```bash
cd /mnt/nfs-1
mkdir -vp portainer
```
```bash
docker run -d -p 9000:9000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /mnt/nfs-1/portainer:/data \
--restart always --name portainer portainer/portainer
```
### SVN
```c++
cd /mnt/nfs-1
mkdir -vp svn
chmod -R 777 /mnt/nfs-1/svn
```
```bash
docker run -d --name svn-server --restart=always \
-v /mnt/nfs-1/svn:/home/svn \
-p 7777:80 \
-p 3690:3690 \
elleflorio/svn-server
```
![[NAS与服务部署#SVN]]
## cloudreve
```c++
cd /mnt/zpool_main/DockerHost/cloudreve/
mkdir -vp {uploads,avatar} \
&& touch conf.ini \
&& touch cloudreve.db
```
```c++
docker run -d --restart=always \
-p 5212:5212 \
--mount type=bind,source=/mnt/nfs-1/cloudreve/conf.ini,target=/cloudreve/conf.ini \
--mount type=bind,source=/mnt/nfs-1/cloudreve/cloudreve.db,target=/cloudreve/cloudreve.db \
-v /mnt/nfs-1/cloudreve/uploads:/cloudreve/uploads \
-v /mnt/nfs-1/cloudreve/avatar:/cloudreve/avatar \
cloudreve/cloudreve:latest
```
## 北京p4v密码
- bluerose
- 123456

View File

@@ -0,0 +1,8 @@
## 参数检查
### 灯光
- [ ] 开启RayTracing之后检查所有Map的SpotLight、RectLight、SkyLight、DirectionalLight(Angle较大时)的采样值。
## 参数预设与参数文档
- [ ] MovieRenderQueue的预设以及相关参数文档
- [ ] TAA相关的参数文档

View File

@@ -0,0 +1,92 @@
## MUTools Maya部分笔记
1. AddMesh()
2. SendCommand()
### 导入Maya文件命令Mel
>file -f -o "C:/Users/admin/Desktop/test.mb";
### Maya插件获取场景数据
DBU_ScenePrimitiveCollector::AssembleData具有2个分支UpdateOutline与CollectFeedback同时负责序列化
1. AdditionalMessage
2. 写入文件头虽然这个写入步骤在UpdateOutline之后但在写Outline数据时偏移了sizeof(UPDATEMSG)
3. UpdateOutline
1. OutlineEntityList
1. NameStream中每个节点名称字符串的偏移
2. ChildNode数目
2. UPDATEMSG MSGHEAD 文件头信息。
3. NameStream 存储每个节点名称的流
4. CollectFeedback
1. UpdateEntityList
2. MeshStream->AddrList
3. MeshStream->ByteStream
UE中的反序列化根据文件头信息的HasOutline与HasFeedbackData有以下两个分支
1. 读取文件头信息
2. HasOutline
1. OutlineRootData
2. OutlineData
3. NamesData 节点名称流
3. HasFeedbackData
1. NamesData
2. UpdateEntities
3. AddrList
### 更新大纲Mesh数据
UpdateOutline只有Maya大纲的名称与当前节点的子节点数目数据。其他模型信息集中在CollectFeedback阶段。
DBU_AddMesh来管理列表监视每个Mesh状态如果entity的meshDirty=true则将其加入`vector<MeshAndID> MeshList`。最后使用
```c++
MeshStream = new AssembleMeshStream(MeshList);
```
来生成这个MeshStream。来进行顶点、UV、法线、顶点处理
```c++
bool DBU_ScenePrimitiveCollector::AssembleData(MString& Result,vector<char> &ByteStream, bool CollectFeedback,bool UpdateOutline, MString AdditionalMessage)
{
MStatus status;
//if (DBU_AddMesh::First == nullptr)
// CollectFeedback = false;
if (CollectFeedback)
{
std::chrono::steady_clock::time_point _start_time = std::chrono::steady_clock::now();
DBU_AddMesh::LastSentUpdateList.clear();
DBU_AddMesh* curr = DBU_AddMesh::First;
while (curr)
{
curr->CollectEntity();
auto Next = curr->Next;
curr = Next;
}
```
DBU_AddMesh::CollectEntity()
DBU_AddMesh节点在连接时候将srcPlug的DAG路径加入NodeRegisteredDAGPaths。
MPlug & srcPlug->MObject FromWhat->MFnDagNode FromNode->MDagPath dagPath
### MUTools UE
通过MeshList的Index进行同步。
点击Outline Import后的逻辑 为发送遍历Entiry 并且发送Mel
$indices = { XXXX };
{
int $i;
for($i=0;$i<size($indices);$i++)
{
DBU_AddSynchronize(DBU_GetSentOutlinerEntityPath $indices[$i]);
}
}
print `lsType "DBU_AddMesh"`;
调用DBU_AddSynchronize(DBU_GetSentOutlinerEntityPath)节点

View File

@@ -0,0 +1,200 @@
---
title: UE5制片流程相关想法
date: 2022-08-25 13:55:15
tags: Film
rating: ⭐️
---
## 渲染相关
1. 场景检查工具
1. 灯光
2. 贴图
2. 预渲染相关
1. GPULightMass。
2. 模型/场景距离场生成问题与查看。
3. Lumen
1. 相关参数以及命令行参数。
2. 物理灯光流程。
4. 虚拟阴影贴图
5. 性能优化建议
1. 尽可能的使用MaterialInstance而非直接复制Material。
2. 尽可能使用面光源而非灯阵。
6. 修改引擎
1. 让指定角色不接受VLM的影响
2. 定制一种灯光只对某个指定的SkeletalMesh起作用不对环境模型起作用同时不产生球谐效果
7. MassAI群集系统
8. 机器学习变形器UE5.1出)
9. 其他想法
## 地编相关
1. 虚拟贴图
2. Nanite
3. USD工作流
4. 摆放工具与其他工具链
## 其他建议
1. 中午休息时将电脑改成睡眠模式:对自己的身体、电费、空调费、硬件寿命都好。
## 具体
### 动画组
1. 学习相关基础编辑器使用。
2. **如何导入与重定向商城与动作库资源**
1. 导入与重定向HumanIK骨骼、Biped、其他动作库资产。
3. **重点学习ControlRig与Sequence**
1. 对角色、生物进行绑定。
2. 使用ControlRig与Sequence制作动画。
3. 使用Sequence的Animation轨道以类似动画层的方式来融合动画效果来制作动画。
4. 使用Sequence以录制动画的方式来制作动画资产。
4. 测试Unreal Live Link for Autodesk Maya以其他LiveLink流程
1. 测试编写文档以方便一起讨论实现新的工作流。
#### 快捷键
快捷键是可以导出与导入预设的。
- F聚焦
- Shift+F所有View聚焦
- Alt+鼠标左右键效果与Maya一样。
- Alt+鼠标中键效果与Maya相反。但可以在EditorPreferences-LevelEditor-Viewports-Control中勾选`Invert Middle Mouse Pan`
#### 基础
1. Skeletal、曲线、AnimationSequence资产编辑器。
2. Sequencd
3. 动作库使用
#### ControlRig
- ControlRig https://docs.unrealengine.com/5.0/en-US/control-rig-in-unreal-engine/
- 入门教程合集https://www.youtube.com/watch?v=RDYWbA01k60&list=PLSbIBa4Ejxt_A55gIHQs3wmj5G7VeYESv
- 案例教程https://www.youtube.com/watch?v=r1fHOS4XaeE
- IK Rig类似Maya的HumanIK的重定向系统:https://docs.unrealengine.com/5.0/en-US/unreal-engine-ik-rig/
可以在ContentExample中找到相关的教学用的资产。
##### 类似动画层的功能
https://www.youtube.com/watch?v=4ULgKXTf1jE
#### 动画蓝图
动画蓝图学习:https://docs.unrealengine.com/5.0/zh-CN/animation-blueprints-in-unreal-engine/
#### LiveLink
主要是看Unreal Live Link for Autodesk Maya插件可能地编也能用。
- EPIC Maya LiveLink https://www.unrealengine.com/marketplace/zh-CN/product/maya-livelink
- Unreal Live Link for Autodesk Maya https://www.unrealengine.com/marketplace/zh-CN/product/f2e37c4b943f4337b352e1639fa4ebe3
#### ~~MassAI~~(次要 需要编程)
https://www.youtube.com/watch?v=f9q8A-9DvPo
#### ML变形器 - Maya数据生成插件
https://docs.unrealengine.com/5.0/en-US/using-the-machine-learning-deformer-in-unreal-engine/
https://www.unrealengine.com/marketplace/zh-CN/product/ml-deformer
### 特效
1. 学习Niagara功能。可以大致看懂ContentExample等其他项目中的特效并能移植到番剧中使用。
2. 学习fluid-flux、riverology插件使用。
3. 学习Niagara的流体功能。
#### Niagara效果
学习官方案例关卡中Niagara的所有效果。并且将可复用效果分离出来并且进行资产标签分类。
https://www.bilibili.com/video/BV1Xy4y1m7QS?spm_id_from=333.337.search-card.all.click
#### FluidNinja插件
https://www.youtube.com/c/AndrasKetzer/videos
#### 其他插件(白嫖或者看一下朱总账户有没有)
https://www.unrealengine.com/marketplace/zh-CN/product/uiws-unified-interactive-water-system
https://www.unrealengine.com/marketplace/zh-CN/product/fluid-flux
https://www.unrealengine.com/marketplace/zh-CN/product/riverology
#### 流体
https://www.youtube.com/watch?v=k7WLE2kM4po&t
#### 程序化生成城市
https://www.youtube.com/watch?v=usJrcwN6T4I
### 角色
1. 解析UE5 黑客帝国Demo中角色所用用到的技术与流程。并且寻找番剧中可以使用的技术。
- 黑客帝国Demo Character制作流程https://www.youtube.com/watch?v=h_dJtk3BCyg
### 资产
1. UE5 的一些技巧比较杂什么方面的都有https://www.youtube.com/watch?v=QXuHzH0IyRE
2. 学习USD工作流如果评估为有价值则最后组织同事进行测试。
3. 学习Multi-User Editing的使用如果评估为有价值则最后组织同事进行测试。
4. 测试Unreal Live Link for Autodesk Maya插件。
- USD 工作流https://www.youtube.com/watch?v=Ddu7TAICAXw&t
- Unreal Live Link for Autodesk Maya https://www.unrealengine.com/marketplace/zh-CN/product/f2e37c4b943f4337b352e1639fa4ebe3 https://www.youtube.com/watch?v=5iTbnk5Zmak
### 地编
1. 熟悉常用快捷键以提高效率https://www.unrealengine.com/zh-CN/tech-blog/designer-s-guide-to-unreal-engine-keyboard-shortcuts
2. 熟悉UE的相关系统体积雾、昼夜系统、Cloud System 、Water System。
3. 测试虚拟贴图在地形系统中的应用。(搞明白相关参数对性能的影响)
4. **测试Nanite**搞明白相关参数对性能、Lumen与其他显示效果的影响
5. 使用UE的建模功能与雕刻工具。
6. 寻找商城里相关的快速摆放物品插件。
#### 商城插件寻找
寻找一下商城相关的快速摆放物品插件(朱总账号可以白嫖插件)
#### 虚拟贴图(需要固态硬盘支持)
https://docs.unrealengine.com/5.0/zh-CN/virtual-texturing-in-unreal-engine/
#### Nanite需要固态硬盘支持
https://www.youtube.com/watch?v=xUUSsXswyZM
https://docs.unrealengine.com/5.0/zh-CN/nanite-virtualized-geometry-in-unreal-engine/
### 渲染
1. UE5.0 几种渲染方案测试实时以及MoveRenderQueue。包括Lumen、RayTracing、PathTracing。已知的存在问题
1. Lumen的综合测试。
1. 使用自带的比色卡模型+室内与室外场景进行测试。
2. 了解材质参数、Lumen参数、后期盒子参数对反射、GI的影响。
3. 测试相关性能,以得出相关
2. 已知的Lumen的问题
- Lumen不支持双面与半透明物体5.1会支持半透明)
- 目前Lumen的反射还有一些问题反射精度
- Raytracing反射目前无法反射GI效果反射结果没有GI
- PathTracing不支持某些效果
1. 虚拟阴影贴图测试
2. 物理灯光流程测试。即使用真实环境的曝光度进行打光。选择清晨、正午、下午、黄昏、夜晚。晴天、阴天
3. 室内家装效果图流程测试。
1. GPULightMass
2. 虚拟光照贴图(需要固态硬盘)
#### LumenGI与反射
- Lumen功能与作用介绍https://www.youtube.com/watch?v=Dc1PPYl2uxA
- Lumen基础教学https://dev.epicgames.com/community/learning/courses/6Oz/unreal-engine-lumen/vyV2/lumen
- Lumen Explained - IMPORTANT Tips for UE5非官方https://www.youtube.com/watch?v=1e6oOiKh91U
- Lumen Tiphttps://www.youtube.com/watch?time_continue=267&v=wTYM9TfckOQ&feature=emb_logo
- 参考视频中调节的参数https://www.bilibili.com/video/BV1YV4y1J7Zv?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290
##### 使用软件支持跑Lumen
Lumen分硬件支持与软件支持软件支持需要用到距离场所以如果出现一些渲染错误需要检查一下距离场。大概率还会用到体积光照贴图这个可以使用GPULightMass生成。
1. 体积光照贴图。尝试使用GPULightMass生成。
2. 模型/场景距离场。生成问题与查看。
#### 局部曝光
#### MRQ的路径追踪
#### 性能优化建议
1. 尽可能的使用MaterialInstance而非直接复制Material。
2. 尽可能使用面光源而非灯阵。
#### 番剧中灯光效果在UE5中的实践与应用室内吴陆佳
尽量使用面光源、
一组UE5场景
#### 番剧中灯光效果在UE5中的实践与应用室外陈飞宇、唐登强
## 本人工作
一些自动化任务在[[远程办公与自动化逻辑]]
1. 制作In-House插件。
1. 项目缓存、引擎缓存位置检测与设定。
2. 渲染模板与参数调节面板
2. 移植Lyra的AssetValid检测插件。
3. NAS搭建。
4. DDC共享缓存与联机构建Shader。
5. 版本管理。
6. 将可复用资产归类之后封装成插件。放在服务器上。UE目前有一个功能可以远程自动下载插件。
7. 修改引擎
1. 让指定角色不接受VLM的影响
2. 定制一种灯光只对某个指定的SkeletalMesh起作用不对环境模型起作用同时不产生球谐效果
8. MRQ使用MRQ的切片渲染功能、或者联机部署

View File

@@ -0,0 +1,180 @@
# 大致步骤
使用UnrealEditor-Cmd执行资源导入、处理、输出功能。
1. 导入资产 (这段可以参考FBX)
1. 角色
2. 动画 FBX
2. 处理资产
1. IKRig
2. RetargetRig
3. ControlRig?
3. 输出资产
1. FBX
2. 使用MovieRenderQueue 渲染 MP4
## 短期需求
1. AI 动捕、或者GPT生成 BVH、FBX。
2. 特定骨骼结构。
3. UE骨骼确定。
4. 可能需要高并发(后面再考虑)
5. 输出视频。
6. 工期1~2周。
## 需要解决的问题是
- [ ] 支持多种格式的文件
- [ ] `CustomCommandFunction`编写以及生成**IKRig**、**IKRetarget**资产
- [ ] 输出FBX或者MP4
## 输出文件
- *.BVH
-
## 生产环境
使用Docker或者其他容器工具制作处理用的镜像之后使用群集系统控制运行
## CustomCommandFunction
关检测 UCommandlet
# 测试方法
```bash
D:\UnrealEngine\UE_5.1\Engine\Binaries\Win64\UnrealEditor-Cmd.exe D:\UnrealEngine\Project\AIAutomationTools\AIAutomationTools.uproject -run=AIAnimationAutomationCommandlet
```
如果带有配置文件
```bash
D:\UnrealEngine\UE_5.1\Engine\Binaries\Win64\UnrealEditor-Cmd.exe D:\UnrealEngine\Project\AIAutomationTools\AIAutomationTools.uproject -run=AIAnimationAutomationCommandlet -importsettings=C:\\Users\\BlueRose\\Desktop\\ImportJson.json
```
```json
{
"ImportGroups": [
{
"GroupName": "Group11",
//导入文件名
"Filenames": [
"C:\\Users\\BlueRose\\Desktop\\untitled2_Anim.FBX"
],
//生成资产的文件夹
"DestinationPath": "Animation",
//使用的Factory类名
"FactoryName": "FbxFactory",
"bReplaceExisting": 1,
"bSkipReadOnly": 0,
//导入动画资产的设置
"ImportSettings": {
"OriginalImportType": 2,
"MeshTypeToImport": 2,
"Skeleton": "/Game/1/untitled2_Skeleton.untitled2_Skeleton"
}
}
]
}
```
# 其他资料
## IKRig & IKRetarget=
`Engine\Plugins\Animation\IKRig`
- FAssetTypeActions_AnimationAssetRetarget
- FAssetTypeActions_IKRigDefinition:UIKRigDefinition
- FAssetTypeActions_IKRetargeter:UIKRetargeter
## Retarger
```c++
/** The runtime processor that converts an input pose from a source skeleton into an output pose on a target skeleton.
* To use:
* 1. Initialize a processor with a Source/Target skeletal mesh and a UIKRetargeter asset.
* 2. Call RunRetargeter and pass in a source pose as an array of global-space transforms
* 3. RunRetargeter returns an array of global space transforms for the target skeleton.
/
```
## UEditorUtilityLibrary
##
Python参考
`Plugins\MovieScene\MovieRenderPipeline\Content\Python`
## 渲染影片(旧版)
MovieRenderPipelineCommandLine
```bash
"D:\Program Files\UE_4.24\Engine\Binaries\Win64\UE4Editor.exe"
D:\Projects\UnrealProjects\renderMovieTest\renderMovieTest.uproject
/Game/maps/shot0010
-MovieSceneCaptureType="/Script/MovieSceneCapture.AutomatedLevelSequenceCapture"
-LevelSequence="/Game/Sequences/Shot0010"
-MovieFolder="D:\Projects\UnrealProjects\renderMovieTest\outputs"
-NoLoadingScreen -game
```
void FMovieRenderPipelineCoreModule::StartupModule() 处理CommandLine变量
```c++
// Look to see if they supplied arguments on the command line indicating they wish to render a movie.
if (IsTryingToRenderMovieFromCommandLine(SequenceAssetValue, SettingsAssetValue, MoviePipelineLocalExecutorClassType, MoviePipelineClassType))
{
UE_LOG(LogMovieRenderPipeline, Log, TEXT("Detected that the user intends to render a movie. Waiting until engine loop init is complete to ensure "));
// Register a hook to wait until the engine has finished loading to increase the likelihood that the desired classes are loaded.
FCoreUObjectDelegates::PostLoadMapWithWorld.AddRaw(this, &FMovieRenderPipelineCoreModule::OnMapLoadFinished);
}
```
通过委托调用渲染最终到void FMovieRenderPipelineCoreModule::InitializeCommandLineMovieRender()
```c++
void FMovieRenderPipelineCoreModule::InitializeCommandLineMovieRender()
{
#if WITH_EDITOR
//const bool bIsGameMode = !GEditor;
//if (!bIsGameMode)
//{
// UE_LOG(LogMovieRenderPipeline, Fatal, TEXT("Command Line Renders must be performed in -game mode, otherwise use the editor ui/python and PIE. Add -game to your command line arguments."));
// FPlatformMisc::RequestExitWithStatus(false, MoviePipelineErrorCodes::Critical);
// return;
//}
#endif
// Attempt to convert their command line arguments into the required objects.
UMoviePipelineExecutorBase* ExecutorBase = nullptr;
UMoviePipelineQueue* Queue = nullptr;
uint8 ReturnCode = ParseMovieRenderData(SequenceAssetValue, SettingsAssetValue, MoviePipelineLocalExecutorClassType, MoviePipelineClassType,
/*Out*/ Queue, /*Out*/ ExecutorBase);
if (!ensureMsgf(ExecutorBase, TEXT("There was a failure parsing the command line and a movie render cannot be started. Check the log for more details.")))
{
// Take the failure return code from the detection of our command line arguments.
FPlatformMisc::RequestExitWithStatus(/*Force*/ false, /*ReturnCode*/ ReturnCode);
return;
}
else
{
UE_LOG(LogMovieRenderPipeline, Log, TEXT("Successfully detected and loaded required movie arguments. Rendering will begin once the map is loaded."));
if (Queue)
{
UE_LOG(LogMovieRenderPipeline, Log, TEXT("NumJobs: %d ExecutorClass: %s"), Queue->GetJobs().Num(), *ExecutorBase->GetClass()->GetName());
}
else
{
UE_LOG(LogMovieRenderPipeline, Log, TEXT("ExecutorClass: %s"), *ExecutorBase->GetClass()->GetName());
}
}
// We add the Executor to the root set. It will own all of the configuration data so this keeps it nicely in memory until finished,
// and means we only have to add/remove one thing from the root set, everything else uses normal outer ownership.
ExecutorBase->AddToRoot();
ExecutorBase->OnExecutorFinished().AddRaw(this, &FMovieRenderPipelineCoreModule::OnCommandLineMovieRenderCompleted);
ExecutorBase->OnExecutorErrored().AddRaw(this, &FMovieRenderPipelineCoreModule::OnCommandLineMovieRenderErrored);
ExecutorBase->Execute(Queue);
}
```
# 多线程
https://blog.csdn.net/j756915370/article/details/122752719
Runtime\Core\Public\Async\
- FFutureState
- FTaskGraphImplementation
### 2.6 TaskGraph 系统中的 Wait
在 TaskGraph 中,无论是 Event->Wait() 还是 FTaskGraphInterface::Get().WaitUntilTaskCompletes() ,最终都是调用到 FTaskGraphImplementation::WaitUntilTasksComplete 中的。

View File

@@ -0,0 +1,15 @@
---
title: 厚涂卡通渲染效果计划
date: 2022-08-25 13:55:15
tags:
rating: ⭐️
---
感觉还需要一个厚涂原画师的协助:
第一阶段:
- 开发
- 绑定
  1. 使用UE4/UE5标准绑定
- 原画
  1. 寻找合适的插画

View File

@@ -0,0 +1,23 @@
# 学习
## Game101系列
胡渊明 201
## UE编辑器扩展
FSceneViewExtensionBase
## Houdini
1. Sop(几何体、其他图元)
2. Dop
3. Cop
4. Chop
5. VS模块
## CUDE Optial
# 工作
- [x] 升级MUTool =>UE5
2. GPU合成工具 IS 扩展节点。扩展RayTracing Shader节点。 实现GI。参考结果 Color Grading
3. Houdini的功能植入UE5。面向实时交互的Houdini Engine
4. 渲染类型扩充。OpenVDB。
## 工作内容

View File

@@ -0,0 +1,25 @@
## 使用的模拟算法
FEM + 肌肉(变量):
PBDPosition Based Dynamics
MPM
## 技术路线
1. NVIDIA flex移植。
- 优点有完备的UE组件实现提供编辑器等工具
- 缺点没有UE5版本UE4版本也是民间人士实现的。
3. Niagara PBD解算。
- 优点在ContentExample中有实现PBD解算器。
- 缺点:没有其他组件的实现。
5. UE5 Chaos。
- 优点:与其他组件可以数据交互。
- 缺点没有具体的PBD柔体实现。
7. ZENO可以查看节点源码
8. Houdini xPBD Vellum学习节点的解算器代码主要是OPENCL
9. ~~AMD FEMFX库。~~
10. 保底路线:骨骼模拟。
## 开发执行计划
因为不管上述的哪个方案都无法直接制作出想要的柔体模拟效果,所以:
1. 先学习Houdini搭建可行的离线模拟方案FEM、PBD保证技术路线方向的可行性正确性。
2. 学习PBD、FEM的算法。
3. 在UE中复现出现。

View File

@@ -0,0 +1,135 @@
## 优化记录
1. 灯光优化
   1. 主场景(城楼、栈桥与城门部分)
      1. 主光源
         1. 将DynamicShadowDistanceMoveableLight调整到合适范围近景细节阴影40000 => 4000~15000范围
         2. 调整DistanceFieldShadowDistance远景的软阴影效果20000 => 4000~15000如果远景阴影效果有问题再调整DistanceFieldTraceDistance100000.0 => 100000.0~40000000.0
         3. 调整Num Dynamic Shadow Cascades4=> 1~3保证阴影效果变化不大的情况下
      2. 其他辅助光源
         1. 保证灯光的半径与角度刚好覆盖想要影响的物体。
         2. 补光灯可以尝试低亮度不投射阴影或者高亮度+贴花,尽可能避免高亮度加投射阴影。
         3. 如果需要投射阴影请适当调整阴影分辨率1=> 0.05~0.8
   2. 不太容易看的场景(坑底与中间浮岛下面部分)
      1. 尽量保证只有灯光效果关闭阴影效果。如果需要阴影就使用SpotLight不投射阴影可以用点光源。
      2. 尽可能减少灯光数量。即坑底部分的照明可以选择使用贴花Fake
   3. 远景山脉阴影使用Fake贴花。避免使用方向光来投射阴影。
   1. 如果需要阴影可以将Num Dynamic Shadow Cascades 4=>0并且调整调整DistanceFieldShadowDistance。直接使用距离场阴影。
2. 场景优化
   1. 粒子可以调节Cut Off的裁剪系数Spawn TimeSpawn Rate等等可以在保证效果的同时来降低粒子数量的参数
   2. 模型
   1. ~~制作LOD或者HLOD。~~ 使用Nanite
   2. 考虑使用HSIM或者 UE5出的Light Weight Instances https://www.youtube.com/watch?v=QGEV8YjBGHQ
   1. 启用Light Weight Instances插件。
   2. 创建一个蓝图类LightWeightInstanceStaticMeshManager类并且拖入场景中。
   3. Manager设置RepresentedClass指定新建的Actor这里假定名字为AA里面只有StaticMeshComponent
   4. 设置InstancedStaticMeshComponentStaticMesh与Materials。
   5. 往场景里放入多个AA并且选中之后在菜单栏的Actor-Convert Actors to LightWeightinstances
   3. 远景资产
   1. 删除场景外的看不见的模型
   2. 删除多余的地形
3. Nanaite
1. 尽可能得将模型转化成
2. 使用Nanite Tools检查哪些模型的材质有问题并解决。
### 优化记录
#### 方向光
##### 2022.9.20
修改远景4个DirectionalLight 动态ShadowMap阴影可以为距离场阴影或者静态阴影。
- DirectionalLight
  - 级联4=》2
  - DynamicShadowDistanceMoveableLight 40000=》5000
  - DistanceFieldShadowDistance40000=》10000
- DirectionalLight2
  - 级联4=》2
  - DynamicShadowDistanceMoveableLight 40000=》25000
  - DistanceFieldShadowDistance10000=》1000000.0
  - DistanceFieldTraceDistance  =>2500000.0
- DirectionalLight4
  - 级联5=》0
  - DynamicShadowDistanceMoveableLight 40000000.0=》4000.
  - DistanceFieldShadowDistance1000000.0 =》1000000.0
  - DistanceFieldTraceDistance  100000.0=》40000000.0
##### 2022.9.21
- [x] DirectionalLight2
  - 删除
- [ ] PointLight3
- 亮度150=》50
- 关闭CastShadow
- [ ] SpotLight11
- ShadowResolutionScale1=》0.1
- 或者考虑降低角度,只对着城楼。
- [ ] RectLight7
- 建议调整范围 =》降低至能刚好包裹城门的区域。
- 亮度 500=>100
- 关闭阴影
- 或者删了
- [ ] SpotLight25
- 降低半径2000 角度 60
- 阴影分辨率1=>0.5
- [ ] SpotLight26
- 降低半径到2500角度降低到45亮度提高=》10000 Shadow分辨率 1=》0.5
- [ ] SpotLight27
- 降低半径到2300角度降低到45亮度提高=》12000Shadow分辨率 8=》0.5
- [ ] PointLight56
- 关闭CastShadow
- 降低半径到1600
- [ ] PointLight57
- 关闭CastShadow 或者降低阴影分辨率 0.1=》0.01
- 降低半径到700
- [ ] PointLight74
- 关闭CastShadow
- 或者换成SpotLight
- [ ] RectLight14
- 降低半径到2860
- 关闭CastShadow
- 或者换成SpotLight
### 后处理
- AmbientCubeMapIntensity 1=》0
- Ambient OcclusionIntensity 0.7=>0
### 粒子优化
- Niagara Cutout
- https://www.youtube.com/watch?v=_T-BTiMF7XA
- https://docs.unrealengine.com/4.26/en-US/RenderingAndGraphics/Niagara/EmitterReference/RenderModules/
- 4层 雾气=> 3层
- 粒子系统的 LifeTime 25=>14
### 场景优化
- 删除场景外看不到的模型
- 删除多出的Landscape2
- 给树木等物体制作Impostors
## 优先级顺序
- [x] Stat数据查看
- 查看Stat Unit 数值确定是GPU还是CPU瓶颈
- 查看Stat SceneRendering
- ctrl+shift+ r.showmaterialdrawevents -1 r.rhicmdbypass 1 r.rhithread.enable 0
- [x] 动态灯光阴影尝试增加级联数与其他参数。使用低分辨率贴图来渲染阴影使用RenderDoc查看分辨率
- [x] 远景模型距离场检查。使用DFAO关闭SSAO
- [x] 尝试干掉点光源制作Fake灯光 贴花来代替各种灯光与阴影效果
- [x] 检查材质复杂度
- 烘焙材质,将复杂材质烘焙结果到贴图
- [x] ~~检查模型面数以及LOD碰撞盒~~ 改用Nanite来解决
- [x] 草地使用HISM或者UE5出的Light Weight Instances
- https://www.youtube.com/watch?v=QGEV8YjBGHQ
- [x] 粒子优化工具:剪裁粒子面积。
- Unreal4引擎对粒子系统半透明渲染的Overdraw做了优化。在ProjectSetting->Rendering > Optimization中有一个渲染Enable Particle Cutouts by Default选项
- [x] MergeActor工具合并DrawCall
- ~~开启DLSS插件~~
- ~~设置Dynamic分辨率~~
## 制作流程改变
对于性能要求的项目。
1. 制作前先让TA知道目标硬件帧数。
2. 美术摆放场景后检查**距离场**。
3. 美术手动合并材质。
4. 吃透Nanite以及LOD属性设置
5. 搭建VPN、版本管理等方便远程协作的服务。
## 优化笔记
![[UE5优化方法与实践笔记]]

View File

@@ -0,0 +1,134 @@
# 同步
git同步教程
https://zhuanlan.zhihu.com/p/531516583
https://cloud.tencent.com/developer/article/1877515
## 手机同步方案
目前Obsidian 除了自带的同步外,仅支持加载手机本地目录(安卓以及苹果),或苹果的 iCloud 服务,再次特别推荐官方的带有详细文件历史同步的同步功能。不过既然 Obsidian 手机版支持加载手机本地目录,对于安卓手机,可以采用 Folder Sync 来同步坚果云、OneDrive以及其它 WebDav 网盘的文件到自己的安卓手机
# 推荐模板
- https://github.com/cumany/Blue-topaz-examples ![](https://user-images.githubusercontent.com/42957010/184872220-f9b52079-8695-46a7-b517-0588426aa7b5.jpg)
- 视频教程https://space.bilibili.com/1970226/channel/series
- https://github.com/sheldonxxd/obsidian_vault_template_for_researcher
- 介绍视频https://www.bilibili.com/video/BV1XR4y1w7jp/?spm_id_from=333.1007.top_right_bar_window_default_collection.content.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
# 显示编辑
格式编辑器插件:
obsidian-editing-toolbar插件https://github.com/cumany/obsidian-editing-toolbar
1. 安装 [BRAT Plugin](https://obsidian.md/plugins?id=obsidian42-brat)去安装。之后安装 `cumany/obsidian-editing-toolbar`
2. 手动下载与安装https://github.com/obsidian-canzi/Enhanced-editing
3. 在obsidian-editing-toolbar菜单中添加操作
# 插件
- Obsidian-outliner
- 日历
- Obsidian Big Calendar
- Calendar
- 同步
- Obsidian Git
- 看板
- Kanban
- rollover daily todos0
- 表格
- Advanced Tables
- mind map
- Obsidian Memos
- templater
- Media Extended
- 代码高亮
- Editor Syntax Highlight
- Highlightr
- Dataview
- Advanced Tables
- Collapse All
- Customizable Sidebar
- MOC
- Zoottelkeeper **Plugin**
- 手绘流程图
- Excalidraw
- 笔记拆分将多个2级标题进行拆分Note Refactor
### 其他人分享的插件列表
https://www.zhihu.com/zvideo/1465842922323636224
https://zhuanlan.zhihu.com/p/410202700
- cm-editor-syntax-highlight-obsidian
- note-refactor-obsidian
- workbench-obsidian
- markdown-prettifier
- obsidian-shortcuts-for-starred-files
- obsidian-journey-plugin
- tag-wrangler
- obsidian-footnotes
- nldates-obsidian
- pane-relief
- url-into-selection
- calendar
- obsidian-advanced-uri
- remember-cursor-position
- mrj-text-expand
- obsidian-hotkeys-for-templates
- recent-files-obsidian
- obsidian-charts
- obsidian-auto-link-title
- obsidian-leaflet-plugin
- obsidian-admonition
- obsidian_focus_mode
- text-snippets-obsidian
- buttons
- obsidian-hider
- obsidian-markdown-furigana
- obsidian-pangu
- obsidian-pandoc-plugin
- obsidian-powerthesaurus
- obsidian-outliner
- obsidian-tracker
- supercharged-links-obsidian
- media-extended
- privacy-glasses
- obsidian-style-settings
- obsidian-sortable
- maximise-active-pane-obsidian
- dataview
- better-fn
- obsidian-electron-window-tweaker
- obsidian-tasks-plugin
- mrj-jump-to-link
- quickadd
- oz-image-plugin
- metaedit
- obsidian-pandoc
- obsidian-map-view
- smart-random-note
- obsidian-dice-roller
- obsidian-excalidraw-plugin
- mx-bili-plugin
- hotkey-helper
- obsidian-hotkeys-for-specific-files
- graph-view-blink
- sliding-panes-obsidian
- alx-folder-note
- longform
- obsidian-wordnet-plugin
- obsidian-copy-block-link
- emoji-shortcodes
- obsidian-jump-to-date-plugin
- templater-obsidian
- obsidian-markmind
- obsidian-relative-find
- obsidian-code-copy
- obsidian-banners
- calendar-beta
- file-explorer-note-count
- obsidian-drag-and-drop-blocks
- obsidian-hackernews
- quick-explorer
- readwise-official
- obsidian-annotator
- obsidian-kanban
- customizable-sidebar
- folder-note-core
## 甘特图案例
![[Obsidian甘特图笔记]]

View File

@@ -0,0 +1,25 @@
`
```mermaid
gantt
title 本周项目事务甘特图
dateFormat YYYY-MM-DD HH:mm:ss
axisFormat %m-%d
section obsidian
这是一个测试 :t1, 2022-03-28 14:32:45, 2d 10min
section 测试
做饭 :t2, 2022-03-29 14:32:45, 3d 30min
洗碗 :t3, 2022-03-30 16:32:45, 1d 30min
```
`
```mermaid
gantt
title 本周项目事务甘特图
dateFormat YYYY-MM-DD HH:mm:ss
axisFormat %m-%d
section obsidian
这是一个测试 :t1, 2022-03-28 14:32:45, 2d 10min
section 测试
做饭 :t2, 2022-03-29 14:32:45, 3d 30min
洗碗 :t3, 2022-03-30 16:32:45, 1d 30min
```

View File

@@ -0,0 +1,72 @@
---
title: UE5你可能不知道的35个新功能
date: 2022-11-10 11:28:08
excerpt:
tags:
rating: ⭐
---
## 前言
[35 UE5 Features You Probably Don't Know About](https://www.youtube.com/watch?v=k2IP5DYQ0-0)
## ConsoleVariable
### ConsoleVariablesEditor
一种用于快速切换不同数值ConsoleVariables的工具。方便TA/图程调节参数。
首先需要在启用插件`ConsoleVariablesEditor`
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/ConsoleVariablesEditor_1.png)
之后就可以在Window-ConsoleVariables打开这个工具了。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/ConsoleVariablesEditor_5.png)
主要的操作方法就是点击左上角的按钮添加ConsoleVariables。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/ConsoleVariablesEditor_2.png)
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/ConsoleVariablesEditor_3.png)
之后还可以将其保存成预设,方便进行切换。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/ConsoleVariablesEditor_4.png)
### ABTest
一种方便比较2种ConsoleVariables效果差别的工具开启后会以一定频率不断进行切换参数以此方便开发者进行效果比较。
- abtest (Console Command) (value1) (value2)
- abtest stop
比如:
> abtest r.VolumetricFog 0 1
## Asset
### Asset Reload
可以在资产上右键-AssetAction-Reload直接加载本地磁盘或者当前版本管理版本的资产而无需重启整个编辑器。
### Asset高级搜索语法
比如:
>(NaniteEnable = False) AND Triangles > 5000)
可以查询的信息主要为鼠标放在资产上显示的信息。
### Asset List显示
在ContentBrower右边的Settings中点击List即可显示成列表模式。
## UE建模工具
- CubeGrid使用Cube画刷进行原型关卡的搭建。
- FixPivotTool调整Pivot位置。
### GeometryScript
通过蓝图脚本进行一些建模操作,比如对场景中的物品进行布尔运算啥的。
### TextureBaking
位于UE建模工具的Baking中。有BakeTX、BakeVtx与BakeAll。
## Niagara
Baker可以将Niagara制作的特效优化成Flip效果。
## Chaos
ChaosCacheManager组件可以将ChaosCache设置成Pose之后通过设置StartTime来制作一些时间逆转的效果。
## Sequence
Sequence摄像机与玩家摄像机混合过渡效果。在摄像机轨道上右键点击Can Blend。之后进入某个区域播放Sequence就有这种下够哦。
## 其他
- 开启屏幕空间旋转编辑器设置中勾选Enable Screen Rotation
- Procedural Foliage Volumn
- 可以通过ShadowPassSwitch材质节点控制某一些区域的模型不投射阴影。
- 在Simulation之后选中对应的物体之后按K就可以保留物理模拟后的结果适场景美术。

View File

@@ -0,0 +1,74 @@
# 灼华互娱
## 头发
他们的解决方案是:
1、Nvidia的HairWorks
2、在DCC中解算面片头发动画之后导出ABC缓存。
HairWorks是基于dx11的使用了HairWorks就无法使用dx12的Rtx。不过4.24新出的Hair Rendering功能可以代替之前HairWorks。
## ABC缓存使用
主要使用ABC缓存结算头发与布料但ABC缓存很可能因为过大而导致导入崩溃。所以灼华互娱对DCC的ABC导出插件进行魔改从而减少ABC缓存大小。但个人认为最佳的解决方案还是重新实现导出缓存格式不过这需要实现DCC导出插件与Unreal4导入插件引擎没有必要使用高精度数据工作相对来说会大一些。
## Niagara与Houdini结合
灼华互娱采用的方案是:
使用Niagara制作普通粒子特效。
对于需要解算巨量粒子则使用Houdini生成粒子缓存导入Ue4中。在Ue4中生成模型Instance作为粒子。
## 资产管理
灼华互娱使用USD(通用场景描述) 皮克斯公司开发的开源工具,来制作整个资产管理系统。从而在流程上实现了很多自动化处理。
比如各种资产同步比如Maya或者houdini工程进行修改就会自动导入Ue4。之后自动导入服务器。
# ASher的演讲 Ue4中地形及环境特效的制作与表现
主要介绍了Volumetric Fog/Cloud、LansmassBrush、Erosion、Niagara、VolumeMaterial。
个人感觉4.24的地形侵蚀工具相当不错可以通过模拟流体来创建河流侵蚀河岸效果。也可以增加地形的细节以增加可信度。而且可以导出高度图、侵蚀图、泥沙流动图等你可以用这些贴图制作别的物体Mask。甚至是导入niagara制作特效。
另外你也可以使用这个系统与niagara制作海水拍打岸边的岩石所造成的的水花。
## 体积插件可以从github下载编译
配合4.24出的物理大气系统可以轻松渲染出一个很不错的天空。它在dev-rendering分支中位于/Engine/Plugins/Experimental/Volumetrics。
## 体积雾
### 体积雾材质
材质使用的是案例关卡中的雾气材质。具体的材质节点可以参考Shaderbits.com。
至于雾气会绕开Mesh雾气这个应该就是根据距离场再使用类似FlowMap的方式来解决的。
对于天空中的云,通过输出地形高度图来影响。
体积雾阴影需要trick通过体积雾的高度图来计算normal之后通过方向光与法线的dot来计算自阴影。
### 在Sequencer里控制时间
use manual time控制时间。
### 使用命令行调整体积雾质量
r.volumetricFog.TemporalReprojection 0
r.volumetricFog.GridSizeZ 1024
### Temp SuperAA相关
![image](D:/youdaonote-pull-master/youdaonote/youdaonote-images/13917C7C77354910BD652768B792F3D3.octet-stream)
![image](D:/youdaonote-pull-master/youdaonote/youdaonote-images/B85557DC9821473FB8B98FB9B1B2FD06.octet-stream)
使用这个方法可以解决物体与地形的边界过于明显的问题。另一种解决方法是:
![image](D:/youdaonote-pull-master/youdaonote/youdaonote-images/DEC240EAC32940248376B36141EDDDA9.octet-stream)
Lerp的Alpha还可以使用HeightLerp、相对高度甚至是距离场来设置。
![image](D:/youdaonote-pull-master/youdaonote/youdaonote-images/6EFC0D2DAAC8454797B8E4118A433B56.octet-stream)
地形的融合注意使用Material LOD
http://riteofilk.com/2017/07/29/blurring-the-edge-blending-meshes-with-terrain-in/
Dither TAA节点可以通过以下视频了解
https://www.youtube.com/watch?v=PKX_v4RDkPc
Dither TAA的另一个技巧就是让物体产生半透明的效果将DitherTAA节点连接到Mask上
### Quiexl 扫描库贴图设置技巧
使用以下设置会让画面变得锐利一些:
r.ToneMapper.Sharpen 3
r.ScreenPercentage 120
贴图设置Mipmap设置为Sharpen5。

View File

@@ -0,0 +1,100 @@
---
title: 杭州UnrealCircle2022笔记
date: 2022-08-09 13:55:15
tags:
rating: ⭐️⭐️
excerpt:
---
# 杭州UnrealCircle
## ChaosCloth
1. 与引擎其他模块协作,比如**动画系统**
2. 基于PositionBase稳定的表现效果。应该是基于粒子的。
3. 允许使用AnimDriveMask 动画蓝图节点以及参数设置。
4. 支持蓝图设置参数以及对应的Debug工具。
5. 支持老算法NvCloth的结算结果。
6. Chaos没有碰撞体数量限制NvCloth<32新增只会影响Cloth的TaperedCapsules
- 对角色的Clothing菜单里的勾选与环境进行碰撞即可实现与环境碰撞collision with environment
- MeshSkinning->Mesh Skinning选项对与高面Cloth的优化方案
- CVARs Debug命令与PIE参数可视化
- p.ChaosClothEditor.Debugxxx
- p.ClothPhysics.WaitForParallelClothTask 1
- p.ChaosDebugView.DebugRaw;
- Lo-Hi值与Mask值相关。
## 博采传媒 基于Unreal的虚拟制片
剧本->资产制作->剧本可视化 制作预演短片。
Previz-》Techviz-》虚拟制景-》LED环幕拍摄
博采制作了一个半圆柱形的LED棚来解决工业光魔 棚的缺陷。杭州750平米5米高;安吉900平米8米高。
一个月制作 0特效 科幻短片LED棚实拍。
优点:可以节约实景道具,同时获得结果接近的光影效果。
难点如何将LED屏幕的光打到角色身上。比如拍摄夜景时控制DMX灯光与LED屏场景同步来对角色进行补光。
道具研发:比如模拟棚内汽车颠簸的承载系统。
#### 需要大量拍摄技巧
为了保证演员脸上的间接照明演员必须里LED屏幕非常接近(2m,夹角150°)。
个人想法:是否可以使用无人机矩阵进行补光处理。
- 小场景丁达尔光
- 小置景大场景:需要小场景与背景结合经验。
- 丁达尔光:可以直接做出丁达尔光的场景。
- 摄像机仰拍:部分镜头因为圆顶屏幕的限制需要使用仰拍视角来规避。
- 虚拟拍摄与DMX拍摄
## 虚幻引擎在短视频中的应用
可以购买LED电视代替LED屏低配版
## 虚幻录像系统
视频地址https://www.bilibili.com/video/BV1Z34y1n72n?share_source=copy_web
#### 概念
- ReplaySubsystem一个全局的回放子系统封装核心接口并且暴露给上层使用。
- DemoNetdriver继承自NetDriver宏观控制回放的录制与播放。
- DemoNetConnection继承自NetConnection自定义实现回放数据的发送位置。
- ReplayHelper防撞一些放回处理数据的接口将回放逻辑与DemoNetDriver进行解耦。
- XXXNetworkReplayStreamer序列化数据的存储类。
#### Streamer流送有5种方式
1. Null
2. Http
3. 内存
4. 文件
5. 还有一个忘记了
#### 实现快进
1. CheckPoint存档点即一个完整的世界快照每隔一段时间记录一个。
2. Stream一段连续时间的数据流存储着从上一个Checkpoint到当前的所有录制数据。
3. Event记录一些特殊的自定义事件。
#### 录像系统的Streamer使用方式
- UGameInstance* GameInstance=GetWorld()->GetGameInstrance();
- GameInstance->StarrtRecordingRecord()
- GameInstance->PlayReplay()
- 在CS模块文件中增加**NetworkReplayStreaming**与**LocalFileNetworkReplayStreaming**模块按照需求还可以增加InMemoryNetworkReplayStreaming与HttpNetworkReplayStreaming。
#### 版本兼容
- SendProperties_BackwardsCompatible_r/ReciveProperties_BackwardsBackwardsCompatible_r
- 跨版本兼容将本版蹦属性的名称等信息一并写入NetFieldExports结构体再进行序列化
- SendProperties_r/RecevieProperties_r
- 不考虑兼容只序列化某个属性的Index以及内容不记录属性名称。
#### 关卡设计思路
- 游戏中的死亡回放要使用内存Streamer。
- 在进行比赛的同时播放死亡录像采用DuplicatedLevelCollection方案实现。
- 对Level进行划分DynamicSourceLevel原始世界、StaticLevel存放静态Actor、DynamicDuplicatedLevels存放回放世界。
- 使用ActorGUID来区分是服务器Actor还是本地Actor。
#### 观战系统
FHttpNetworkReplayStreamer通过服务器发送一个拷贝到观战服务器中。
#### 优化
- 尽量避免RPC而多使用属性同步
- 使用PlayerState不适用Controller来存储同步数据
- 服务器与客户端都可以录制回放
- 建立对象池来避免加载、Spawn、Destroy、Actor GC的卡顿问题
- 回放世界与真实世界是同一个物理场景,需要避免碰撞
- 避免在回放世界打开物理
- 通过设置PxFilterFlags FCollisionFilterData并修改引擎的碰撞规则处理

View File

@@ -0,0 +1,118 @@
---
title: 究极风暴渲染技术
date: 2022-08-09 13:55:15
tags:
rating: ⭐️
excerpt:
---
此文为转载  转载地址此文中已注明,本地址转载请注明 http://blog.sina.com.cn/diorsolo 
重视画面效果的DOF        图A是应用DOF前的状态。在这个基础上由于适用了从ZBuffer中抽出的(unfocusmask)虽然再现出了DOF但是因为在角色上有轮廓线Outline模糊的部分和不模糊的部分的差距鲜明的显出了轮廓若隐若现图C。因此像图D那样在unfocusmask本体上加上Blur来抑制上述的不合适图E。实际仔细看来本来的焦点应该符合背景而显得模糊但考虑图像的融合还是采用了这种方法。顺便说一句Blur使用了高斯filter来处理可以很轻松的完成。【千里马肝注这的确是一个让unfocus部分与focus部分更柔和融合的小trick。】
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/C936EB7747674CEEAE7990DA20272D9B.jpeg)
### 火焰的特效
      图A里是佐助的[豪火球忍术]图B是实际的模型构成。这个特效可以由中心的球体模型图C平面的正面化模型图DE对应移动的生成平面多边形的轨迹模型图F这三个部分构成把这些合成后可以实现拥有2D的样子并在3D空间中没有不协调感的特效。还有适用于Flare或轨迹的动画用贴图如图所示各自八张的循环,每个一种但是担任特效的下田星儿先生表示最初是做成了平均性形状的多边形在实际组合时让多边形变形即使用一个种类的循环动画也能够表现出形态丰富的火焰。顺便说一句这个特效为了再现动画版的时间虽然是一秒内8到15个画面但游戏本身是按30fps活动。试验性的尝试过60fps和15fps但在60fps中不能流畅的做出活动的动画一旦用15fps做出动画作为游戏的反应就变差了。
      
http://www.opengpu.org/forum.php?mod=viewthread&tid=6609&extra=page=1 转载原文地址尊重作者劳动
## 为了让动画片和游戏没有界限
      本作是在06年初时开发的当时Xbox360刚在日本发售PS3的发售还要有些时候这时不论是谁都在思考着今后的次时代游戏的开发战略。在这样的状况中负责开发CyberConnect2公司的董事社长松山洋先生决心把[伴随着次世代机的图形性能飞跃的提高,各种开发经费也高涨]这个问题,通过以全世界的市场作为目标进行开发而得到突破。
      为了实现这个坚固的理念,深思熟虑过我们自己对于世界的优势,导出的方针就是[把日本独有的动画和漫画的表现贯穿始终],那就成为了所谓的[把动画和游戏的界限消除]的具体目标。为了这个目标首先编成了10人的研究开发组作为先头部队这期间有7名设计师进入开发组内从事制作被称为[超动画]的[贯穿始终]的视觉表现的预想影像经过了半年时间终于在3dsMax上做了出来。
      
      为了达成目标如动画般的画面表现,本作的动画全部用手工完成,一方面是松山先生回想起那些庞大的工作量就说[总之是动作组MotionTeam一直很努力才顺利前进],另一方面动作指导的石桥洋平先生也回答说[确实太辛苦了,但是有研究开发时适当策略可供参考,所以可以冷静的应对]。确实是可靠的言词。
      还有本作是有动画版权的游戏所以要由原版权公司检查。具体上和动画设定的出入对于游戏中存在的IF假象剧情原创忍术的[奥义]的表现进行检查。这些都给予了细心的注意,动画师不时地一边参照动画影像一边进行工作,还要根据每个负责人的文字描述做出概念,之后由导演进行核对,整个工程全部的阶段都要在内部核对,要做到尽可能不返工的程度,用这样精炼的制作来接受原版权公司的监督。职员们这样的努力,已经出色的成为本作的一个理念[兼顾画面的观赏性和作为游戏的游戏性这样的两个目标]。事实上在本作的预告片公开时,也能看到用户和游戏业界对于过高的游戏品质比起动画业界发出了更多的赞赏声。
      最后,通过采访最有印象的是,开发职员要面对把原作动画作为初始的动画本身,从言词的细微传来了敬意和热爱。从下页开始要介绍这种丝毫不妥协的热爱,平时挑战各种各样限制本作构思和技术平衡的真髓。
### 让人感觉不到CG噪音的简单角色表现
动画作画的关键就是对信息量的控制。用少量的信息表现出强烈的个性,怎么才能做出活灵活现的角色呢?
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/704A99E2037A4B2BBA320465980DA40E.jpeg)
### 角色模型的LOD
      角色的建模使用3dsMax。图里是主人公漩涡鸣人的身体和头的建模从左开始是高、中、低模。多边形数量各自为高模16000头部2400、中模8000头部1400、低模4000头部700基本上由从镜头的距离算起5米以内、5到7米、8米以上来区分以此进行LOD处理。梅田公一先生表示这是为了在减轻描画处理负荷的同时表现出动画特有的[远处的东西被画出线条是经过整理的形态]。还有,有时有必要让人清楚的看到表演性质的脸,还会发生由于大量的角色登场引起的负荷上升那样局面的情况,要根据情况强制性的切换高模或是低模。还有,脸上使用到了很多的多边形,本作中使用法术时结印的手指也分配到了相当精细的多边形。
【千里马肝注:临、兵、斗、者、皆、阵、烈、在、前。这九个字源于东晋葛洪的《抱朴子*登涉篇》,(葛洪者,乃东晋时结合儒家思想改造道教,宣扬采药炼丹、长生不老的人,至此,道教变成为封建统治服务的宗教)“临兵斗者,皆数组前行,常当视之,无所不辟。”  意思是说,常念这九个字,就可以辟除一些邪恶。在抄录这九个字时,把“数、组、前、行”误抄成“阵、列、在、前”而沿用至今。这九个字分别的意思是:临,代表身心稳定。兵,代表能量。斗,代表宇宙共鸣。者,代表复原。皆,代表危机感应。阵,代表心电感应或隐身。列,代表时空控制。在,代表对五元素的控制。(五元素:就是我们熟知的金、木、水、火、土。)前,代表光明。】
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/8000D42FA9A44E93B15A6AE23283AAA2.jpeg)
### 重视画面效果的生动配色阴影
      公司内部以前使用过的乘法CelShader绘制出皮肤的阴影部分的褪色效果是个难点见上图A。在本作中为了解决那个问题模仿了Photoshop的softlight图层效果开发出了CelShader见上图B。用这个方法虽然确实在皮肤的影子上能得到良好的结果但同时衣服等东西原本的色彩很高的部分影子也变的明亮结果作为影子就会出现破绽。于是开发出了新的彩度高的像素上使用乘算彩度低的像素上用softlight的把影子颜色在直线上混合描绘的shader实现了取得平衡的阴影见上图C。另外项目主管的西川裕贵先生表示因为这个shader保持相当的的彩度根据动画上经常用的色调也可以适用在阴影的表现上例如说把夜里的阴影做成青色时也很有用。
【千里马肝注:动画在绘制之前,会由一位经验丰富的美术绘制一套被称作“色彩指定”,简称“色指”的图片,其中对于角色的肤色、头发色、眼睛、服饰等的颜色进行规定,包括该部分在阴影下的颜色、高光的颜色等等。这样严格要求的结果是,当动画关键帧被绘制完成后,流水线上的美术必须严格按照色指进行上色,于是出来的动画效果就如同一个人画出来的一样。】
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/323A8D58845A421495538CCD55D2C04C.png)
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/DCB6EECE80484ACBA8C6CC0C0F14A8EF.jpeg)
### 整理出动画风格的阴影
      虽然图A把高模做出了GlowShading但是如果使用这个光源设定进行CelShading光照会像图B那样忠实的表现出阴影来导致表情变的非常僵硬。这样就达不到动画片的效果所以需要像图C和D那样让模型膨胀再整理法线作成调整过的模型取得了法线信息后把那些适用在原模型上就可以再现出图E那样动画风格的柔软的表情。还有在实际主机上的再现是开发了专用的插件对应完成的。这个Shading技术可以说是初期研究开发阶段很大的一个成果。
【千里马肝注这种手法之前在《偶像大师1、2》中也出现过具体实现还是第一次见到。需要试验一下这种膨胀的方式当光源位置变化时会是什么效果
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/2DE1377209674C0F933535642A1953D8.jpeg)
### 必要的轮廓线(Outline)抽出法
      作为轮廓线的生成法从PS2的时候就是被经常使用的方法把模型按法线的方向推出很大的一圈再把多边形的法线反向作为轮廓的绘制手法图A。用这个方法因为对于全部的多边形都适用一样的处理头发等的轮廓也忠实的绘制出来表现出期望中的动画风格的质感。还有一种生成法一次性把模型渲染到2Dbuffer中进行post处理是一种抽出模型的轮廓silhouette的绘制手法图B。用这个手法虽然使用silhouette生成了简单的的轮廓线Outline但不能在silhouette的内侧生成。所以本作的Celshading上附加了轮廓线Outline选择的选项由于设计师会对应状况选择绘制方法实现了这两个手法的并用。
      
【千里马肝注图A的方法不用多说图B的方法可以将normal或depth渲染到rendertarget中然后进行Sobelfilter处理。作为卡通渲染有时的确不希望过多的细节被表现出来这里本作使用2种可选项是个好办法。更多的信息请见RenderMonkey中的NPR。】
### 动画中加入了其所特有的夸张
动画的深奥妙趣,就是充分利用省略和强调张驰。
那么如何通过CG来表现这些并灵活的运用呢
这里来披露一些吧。
![《火影忍者:究级风暴》渲染技术究极解析](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/66E0601B773C4C0897688A5B49D19040.jpeg)
### 索具(Rigging)的准备配置(Setup)
       角色的索具装配使用了Biped。骨骼的构成被称为全角色共通的基础的躯干部分有52个各种调整用的自由骨骼是22个面部是67个服装和头发等摇动的物体用的延伸骨骼是30到80个全部大概每个角色可以算出是170到220个。还有做成的动画数漩涡鸣人的情况在自由战斗时是125个极限任务模式里是90个。
![《火影忍者:究级风暴》渲染技术究极解析](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/173B3E3372CF466CBAF62D2B94B0A042.jpeg)
### 动画拖影AnimeBlur的实现
      作为动画片固有的速度表现速度过快的会有轮廓被拖长的情况。在本作中把三角锤的模型重叠图A,提前加入角色的末端部分图B,把骨骼拉出来图C实现了这个效果图D。在骨架上加入了弹簧控制根据动作的速度把拉出的长度自动的变化虽然做出的动作也有些和表演意图不同但为了取得平衡全部的人都加入了。
【千里马肝注: 关于弹簧控制,请搜索:虎克定律。】
![《火影忍者:究级风暴》渲染技术究极解析](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/D0BA456FAE4A4D8E8BD494880593C096.jpeg)
### 面部配置Setup的基础
       面部正如前述那样由67个骨骼控制图A。作为本作特有的设定下唇的下骨骼占到脸的三分只一。这是为了可以表现出即使是动画独特的张嘴下巴的位置也不会改变或是表现出图B那样脸有很大的扭曲。还有一看就明白的真实系的面部配置中大多被设定的脸颊或鼻根的骨骼极少。这样着色在平面上难以反映变化所以并不是要设定最低限度的骨骼而代替那些皱纹表现的要用专用的3D模型图C来实现图D。顺便说一句图C的两个白色圆板是眼睛的高光这样可以把皱纹以及高光从shader上分离容易按意图做出表情
![《火影忍者:究级风暴》渲染技术究极解析](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/0D143BF89E814B7DA84E3BFFE8BA9F94.jpeg)
### 动画独特的不合常态的关节
       图A作为人的动作当然相当的正确但是按动画来看就太过死板缺乏趣感。正是像图B那样柔软有弹性的形状变化才符合动画片。为了实现那些要用基础的骨骼图A制作动作后再把子阶层模型顶点网格通过自由骨骼对权重进行控制图C的红色部分这样就可以制作动画独特的柔软性。
![《火影忍者:究级风暴》渲染技术究极解析](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/2822B5D78276433D8CDAEF4A086D4395.jpeg)
### 大胆夸张的动画风格构图
       在实际的镜头中一旦让被摄影对象的人完全的进入画面怎样努力也如图A那样有透视的限制。所以为了得到如图B那样动画型的夸张的构图要通过Biped的骨架的辅助动画中加算上Scale让其在物理性上巨大化。
![《火影忍者:究级风暴》渲染技术究极解析](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/9B85F54DB0CB464690D1E2C433A5BDAF.jpeg)
### 面部的作业环境
       使用了以前的目标变形MorphTarget是因为要在本作中变更骨骼如图那样准备了选择器工作中可以迅速的对应新的手法图A。还有由于这个变更表情的数据库管理化就成了问题但是可以把表情的骨骼和标准的姿态的骨骼的差值做成文本档案化解决了输出脚本的制作问题。由于这样通过目标变形能够混合两个表情的同时还可以转移到拥有同一个骨架构造的其他角色上做出类似的表情对工作的效率化还有风格的统一等有很大的作用。
![《火影忍者:究级风暴》渲染技术究极解析](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/DA0CEEBCE9C2478DA77C99B1741522C5.jpeg)
### 背景的概要
      上图是战斗舞台的其中一个角色的移动可能范围按实际尺寸是直径50米最远景是500米左右大约用了5万多边形做成。担当背景制作的四所铁矢先生表示开发当初好像有40万到50万多边形一边要维持品质一边要把多边形消减到能够实时绘制处理的水平很辛苦呢
![《火影忍者:究级风暴》渲染技术究极解析](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/10CA18F8717F457CA88A232FB38E5A58.jpeg)
### 丰富的河流表现
       让水面上反射出美丽阳光的河流图A,如何能用一张平面的多边形作出来图B。使用的贴图有color图Cheight图Dnormal图Eshadow图F环境图G5个种类水的颜色在shader参数里设定在颜色上也可以增加高度的信息。环境贴图拥有alpha通道,加入了模拟的HDR信息图H绘制太阳一类的效果。像素描绘的结果会显出很强的GlareFilter效果花了很多工夫。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/9EFCA4B955C84964A830832998D90916.jpeg)
### 背景的贴图
      在贴图上使用了Albedo贴图图A和阴影贴图图B。根据适用的地方在2048X2048到256X256像素的范围内选择尺寸使用DXTC格式总计30MB的程度。还有用Photoshop的插件做成Mipmap来使用图C。虽然由于这些贴图的容量会增加33%但和没使用时比起来GPU的处理负荷减轻了接近一半那个效果是非常大的。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/212029DE080A4C709ACC71E7DC81E798.jpeg)
### 背景的作业环境
      公司内部在构筑开发环境上也倾注了全力在背景制作中使用了30台以上的wacom的21寸液晶绘图板。更有应该注意的是在负责人的头上有个防止面对监视器时映入灯光的特别定作的圆盘形的遮光罩。这是日产汽车和slik共同开发的产品由于这个抑制了照明实现了没有那么明亮的作业环境。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/NARUTO/04F65D2BA78243F0BE323125FB99F78F.jpeg)

View File

@@ -0,0 +1,27 @@
## 参考案例
https://www.bilibili.com/video/BV1tp4y1r7A7
Engine/Source/Runtime/LinvLinkInterface
## 添加数据类型
>在Roles文件夹中得到XXXType.h与XXXRole.h中增加自定义数据类型与Roles规则。
- FLiveLinkBaseStaticData静态变量
- FLiveLinkBaseFrameData每帧变量
- FLiveLinkBaseBlueprintData:蓝图数据,用于在蓝图中访问对应的数据。
## 添加规则
- GetDisplayName():定义自己的LiveLink名称。必须定义且唯一。
- GetXXXDataStruct():返回XXXType.h中定义的结构体。
- InitializeBlueprintData():初始化数据并且赋值到FLiveLinkBaseBlueprintData中设置的变量里。
## 编写LiveLinkProvider(数据发送端)
>在Provider中编写传递数据过程
## 编写LiveLinkController
IsRoleSupportedd():判断Role是否为指定Role,即判断数据为什么类型Role的数据。
Tick():编写每帧处理逻辑。
## 使用
在LiveLink管理器中连接Provider
在Actor中挂载LiveLinkController组件

View File

@@ -0,0 +1,26 @@
### 虚幻引擎4全平台热更新方案
https://www.bilibili.com/video/BV1ir4y1c76g
### 虚幻引擎中基于物理的动画
https://www.bilibili.com/video/BV1UV411a7Jq
主要讲解使用的计算功能与思路。
### 深入解读Niagara智能粒子效果
https://www.bilibili.com/video/BV1At4y1v7EQ
### 虚幻引擎的Niagara系统与实时VFX的演进Niagara功能节点介绍
https://www.bilibili.com/video/BV13541157SU
### 纯虚幻引擎流程的卡通烟雾特效
https://www.bilibili.com/video/BV1fV411a7Se
一句话是要用Niagara模拟粒子之后使用CaptureSceneRender获知速度、透明度、Alpha等信息的flip之后再制作粒子效果。
### 用虚幻示意图形UMGUI设计器优化并构建3A级UI
https://www.bilibili.com/video/BV1EK4y1j7VB
UMG的优化
### 用Unreal Insights收集、分析及可视化你的数据
https://www.bilibili.com/video/BV1Ay4y1q7Kj
### 利用虚幻引擎自动化框架进行性能测试
https://www.bilibili.com/video/BV1Ca4y1p7Bq

View File

@@ -0,0 +1,18 @@
## 视频地址
https://www.bilibili.com/video/BV19p4y167gh
## 为什么需要CL
- 多操作系统开发需要
- 构建与发布产品(不同环境)
- 构建DLL并且上传到哨兵程序中
- 构建自定义引擎
- 运行自动测试程序
- 检测代码与分支的有效性
- 为每个设置构建对应的版本
## CL工具
- TeamCity
- Jenkins
- Bamboo
## CD

View File

@@ -0,0 +1,40 @@
## 视频地址
https://www.bilibili.com/video/BV1ey4y1q7s4
## ComputerShadder
### 支持原子操作
InterLockedAdd(),Min()/Max(),Exchange()
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/ComputeShaderAtomic.png)
### 线程同步
GroupMemoryBarrierWithGroupSync()![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/ComputeShaderSync.png)
### Shader传入参数
- GroupThreadID(SV_GroupThreadID)
- GroupID(SV_GroupID)
- DispatchThreadID(SV_DispatchThreadID)
- GroupIndex(SV_GroupIndex)
可以在微软的文档中找到具体解释。
### ComputeShader对于PixelShader的优势
PixelShader只能处理当前ShaderComputeShader是任意位置可写。可以用于编写屏幕空间反射等需要将效果写入任意位置的效果。
可以更好地利用显卡的并线单元。使用二分法循环计算:
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/ComputShaderExposureCalculate.png)
共享内存:举个例子模糊、等需要多次采样各个像素的算法,使用共享内存就可以减少采样次数与消耗。
#### 工作组
工作组会影响共享内存大小,从而影响多次采样像素的效率。但也会影响线程同步,同步速度变慢,也会影响原子操作次数。
### PixelShader对于ComputeShader的优势
- PixelShader可以预加载贴图并且缓存UV使得读取贴图的效率会非常高。而ComputeShader需要计算出UV所以做不到这点。
- PixelShader支持FrameBuffer压缩减少带宽压力。
- PixelShader支持更多的贴图格式。
## ComputeShader优化技巧
- Optimizing Compute Shaders For L2 Locality Using Thread-Group ID Swizzling
- DirectCompute Programming Guild
- DirectCompute Optizations And Best Practices

View File

@@ -0,0 +1,42 @@
## 前言
视频地址https://www.bilibili.com/video/BV1Ge411N7Bm/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290
开启Strata的方式
- ProjectSettings- Enable Strata Materials
为了提高编译效率可以给ConsoleVariables.ini添加`r.D3D.ForceDXC=1`
有关材质函数的帮助可以参考 `Engine/Content/Functions/Strata`
## Strata
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221231182809.png)
Fuzz用于模拟天鹅绒这种边缘会泛光的材质。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221231212317.png)
这里的MFP指的是Mean Free Path平均自由路径下面这4个引脚控制SSS效果。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221231211236.png)
使用下图节点实现透射率转化为MFP这主要是方便美术理解也因为MFP不是线性的关系所致。该节点通过计算BaseColor与TransmittanceColor来取得MFP与Thickness。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221231212513.png)
## 混合方式
- Horizontal Mixing水平混合模式。使用Mask贴图混合也可以做到。
- Vertical Layering垂直混合模式。(使用Strata可以保证物理正确)
- ![500](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221231214110.png)
- Coverage Weight权重混合
- Thin Film
- ![500](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221231214554.png)
## Strata Tree
可以通过查看Strata材质组成构造。
其中好处就是在跨平台移植时可以方便得关掉一些材质功能,在保证大体效果接近的情况下进行优化。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221231214901.png)
## 数据存储方式
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221231215557.png)
## debug与命令
勾选 `Show - Visualize - StrataMaterial`就可以查看占用数据。
r.Strata.Debug.VisualizeMode 1/2/3 可以切换不同的debug视图。

View File

@@ -0,0 +1,32 @@
## 前言
视频地址:
https://www.bilibili.com/video/BV1d841187Pt/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290
## 基础包拆分
- PrimaryAssetLabel
- ChunkIDs
- ObbFilter(Android)
UE默认拆分基础包的缺点
- 只拆分UAsset
- MetaData不会拆分仍在Chunk0
- Chunk中无法配置Non-Asset文件
- UE不支持IOS IPA包内PAK的过滤
## UnrealFileSystem
UE的热更新是基于Pak的热更新。
## Pak
在Cook完之后使用UnrealPak进行打包。
```cmd
UnrealPak.exe SAVE_PAK.pak -create=RESPONSE_FILE.txt -compress
```
### Mount
1. Pak需要挂载到游戏中才能使用。
2. Mount时可以设置Pak的优先级。
自动挂载目录:
1. Engine/Content/Paks
2. GAME_DIR/Content/Paks
3. GAME_DIR/Saved/Paks

View File

@@ -0,0 +1,52 @@
---
title: UE的多线程渲染
date: 2022-11-23 16:55:20
excerpt:
tags: Rendering
rating: ⭐⭐
---
视频https://www.bilibili.com/video/BV1sP4y117Dg/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290
# 并行渲染
因为一些平台的RHI指令需要等待驱动层返回才能继续执行所以会使用分离RHI线程的方式来解决。
![800](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221123173341.png)
渲染线程会每帧的帧头处等待RHI线程执行结束。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221123173030.png)
## RHI
同时渲染线程会将这些RHI命令都打包在一起以链表的方式发送到RHI线程进行处理。其中FRHICommandListImmediate单例只存在一个只存放立即执行的RHI命令比如LockTextre2D子类的。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221123174116.png)
## 多线程翻译RHI指令
- IRHIComputeContext只有一些ComputeShader相关逻辑。
- IRHICommandContext继承自IRHIComputeContext其他更多GPU指令。
Command会以链表的方式被构建成CommandBuffer之后加入驱动层的Queue中。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221124105422.png)
## 线程同步
- 前端使用了TaskGraph System来同步主要通过设置Task与Task之间的条件来实现的。
- 后端使用Barrier、Fence、Semaphore等方法。
## MeshDrawCommand的并行过程
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221124112824.png)
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221124113024.png)
## RDG并行渲染
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221124113241.png)
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221124113657.png)
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221124123846.png)
## 并行渲染Debug
- ByPass
- r.RHICmdBypass
- - forcerhibypass
- GRHISupportsRHIThread
- console variables =>r.metal.IOSRHIThread r.OpenGL.AllowRHIThread r.vulkan.RHIThread
- -rhithread -norhithread
- GRHISupportsParallelRHIExecute
- r.Vulkan.RHIThread >1

View File

@@ -0,0 +1,22 @@
## 时间与天气
首先得确定时间与天气(大气),可以制作一个多维度的表格作为参考,之后就可以在创作时选择接近环境氛围快速出效果了。
这里李文磊介绍了自己做一个渲染参数数轴,对比度、亮度、颜色=>渲染效果。
![800](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/%E5%AF%B9%E6%AF%94%E5%BA%A6%E4%BA%AE%E5%BA%A6%E5%9D%90%E6%A0%87%E7%B3%BB.png)
![800](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/%E5%AF%B9%E6%AF%94%E5%BA%A6%E4%BA%AE%E5%BA%A6%E9%A2%9C%E8%89%B2.png)
![800](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/%E5%AF%B9%E6%AF%94%E5%BA%A6%E4%BA%AE%E5%BA%A6%E9%A2%9C%E8%89%B2%E4%B8%8E%E5%AF%B9%E5%BA%94%E6%B8%B2%E6%9F%93%E7%BB%93%E6%9E%9C.png)
每个突然结果上标注曝光度、主光角度、色温、饱和度以及天气。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221230212221.png)
甚至可以配上其他渲染参考
![800](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/%E5%AF%B9%E6%AF%94%E5%BA%A6%E4%BA%AE%E5%BA%A6%E9%A2%9C%E8%89%B2%E4%B8%8E%E5%AF%B9%E5%BA%94%E6%B8%B2%E6%9F%93%E5%8F%82%E8%80%83.png)
## 曝光
曝光是确定时间之后第二确定的。但艺术创作需求很可能会是过曝或者欠爆的,所以不能直接依赖与自动曝光。
1、可以通过切换到BaseColorOnly模式下固有色亮度与灯管都会影响曝光测算暗部Ev与亮度Ev值之后再来调整自动曝光的范围。
2、如果真实情况得到想要的效果就可以使用曝光补偿与曝光补偿曲线。

View File

@@ -0,0 +1,46 @@
## 值得看的视频
- [UOD2022]UE5新材质系统-Strata
### 渲染
- [[多线程渲染]]
- [Rendering Dependency Graph解析]( https://www.bilibili.com/video/BV18K411Z7Jg/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- RDG底层原理以及调试工具介绍。
- [Deformer Graph](https://www.bilibili.com/video/BV18g411W7yh/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- [[UE5新材质系统-Strata]]
- 移动端
- [UE 遮挡剔除技术改进及Nanite的Culling管线浅析](https://www.bilibili.com/video/BV1rR4y1Z7QW/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- 分析了UE4、UE5Nanite、与移动的剔除实现与改进方法。
- [GAUSSF: 移动端高性能及UE4插件方案](https://www.bilibili.com/video/BV1H14y1H7e5/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- 模仿FSR算法实现的移动端超采样算法。
- [极速光影-探讨《Racing Master》中的光影技术](https://www.bilibili.com/video/BV1sP4y1R7TV/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290)
### Gameplay
- [物理载具的网络同步](https://www.bilibili.com/video/BV1Ye4y1s7tT/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- [不Mass怎么Meta](https://www.bilibili.com/video/BV13D4y1v7xx/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- [从行为树到状态树](https://www.bilibili.com/video/BV1ed4y1b7Zk/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- 主要的作用是一个通用的状态机来适配MassAI因为行为树作为一个ActorComponent只能作用于Pawn。它可以在其他各个类中使用。也可以用来代替行为树。
- 不使用其他方案的原因
- 蓝图:复杂
- Ai行为树切换条件不明显
- 动画状态机:不能总览,需要深入每个层级才能了解细节。且该功能转为动画设计。
- ![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20221128203900.png)
- [[UE热更新的原理与实现]]
### 关卡
[[气氛和调子-UE影调设计和实战解析]]
[UE5 Geometry Script的探索与应用](https://www.bilibili.com/video/BV1rR4y1o7Db/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
### 动画
[IKRig的应用价值](https://www.bilibili.com/video/BV17P411g7Qp/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
动画重定向、IK动画重定向以及相关应用场景介绍。
### 特效
[Niagara中的物理模拟](https://www.bilibili.com/video/BV1324y1m7nA/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
### 其他
- [主机游戏的跨平台开发与优化-《暗影火炬城》的主机开发经验](https://www.bilibili.com/video/BV1WD4y1e7N6/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- 开发主机游戏的相关经验。
- [低成本虚拟制片 Vol 3](https://www.bilibili.com/video/BV1UG411F79p/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- RealityCapture
- [初探RealityCapture摄影测量常用流程](https://www.bilibili.com/video/BV1k841187dX/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- [从RealityCapture到虚幻引擎5(官方字幕)](https://www.bilibili.com/video/BV1sG4y137qi/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290)

View File

@@ -0,0 +1,351 @@
地平线的世界设计
1、开方世界——玩家有很大的探索区域
2、昼夜循环
3、动态天气
4、史诗般的场景
5、天空是地形的一部分
•地平线是一个巨大的开放世界,你可以去任何你看到的地方,包括山顶。(我表示呵呵,这个已经被吐槽,真正做到的只有荒野之息了)
•由于这是一个活生生的现实世界,我们模拟地球的自转周期。
•天气是环境的一部分,因此它也会发生变化和演变。
•有很多史诗般的风景:高山,森林,平原和湖泊。
•天空是地平线的一个重要组成部分。他们占了屏幕的一半。天空也是故事和世界建筑的重要组成部分。
云的实现目标
1、美术方面的可操控性
2、真实性需要实现多种云的类型
3、与天气继承
4、可移动的
5、EPIC叫我干什么
早期的模拟方案:
1、使用流体解算器MAYA、houdini效果很好。但是工作室模拟云彩的经验比较少……说到底就是没钱请不来影视特效大佬
2、使用简单的几何体配合体素然后运行流体解算器直到他们像云。
3、然后我们开发了一种照明模型用于预先计算主和次散射。在Houdini完成解算需要10S时间
4、把云转换层多边形烘培了光照等数据。再使用Billboards————缺点无法轻易生成云阴影本人表示严重质疑
5、我们试着把我们所有的voxel云作为一个云来生产它也可以混入大气层。类型的工作。在这一点上我们后退一步来评估什么行不通。没有一个解决方案使云随着时间的推移而进化。没有一种好的方法可以使云层通过头顶。并且对所有方法都有很高的内存使用和透支。所以也许传统的基于资产的方法是不可取的。
6、于是开始考虑体素云了
7、所以我们进入了Houdini并从模拟的云形状中生成了一些3d纹理。使用Houdini的GL扩展我们构建了一个原型GL着色来开发云系统和照明模型。
8、这让我们看到了地平线的云系统。为了更好地解释我把它分成了四个部分:建模、照明、渲染和优化。在了解我们对云的建模之前,我们应该对云是什么以及它们如何演变成不同的形状有一个基本的了解。
9、There are many examples of real-time volume clouds on the internet. The usual approach involves drawing them in a height zone above the camera using something called fBm, Fractal Brownian Motion. This is done by layering Perlinnoises of different frequencies until you get something detailed.
•This noise is then usually combined somehow with a gradient to define a change in cloud density over height.
10、由于光噪声本身不能切割我们开发了自己的分层噪音。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/32C9E5FA800B471DAA04DDFC66AE5542.png)
Worley噪音是由Steven Worley在1996年引入的经常用于因果关系和水的影响。如果它是倒立的就像你在这里看到的:
它的形状非常紧密。
我们把它像标准的perlinfbm方法一样分层
然后我们用它来作为对dilate perlin噪声的偏移量。这使我们能够保持perlin噪声的连接但是增加了一些billowy的形状。
我们把这叫做“佩林-沃利”的噪音
11、在游戏中把声音存储为tiling 3d纹理通常是最好的。
•你想保持纹理读取到最低…
•尽可能小的保持决心。
•在我们的例子中我们已经压缩的声音…
•两个3 d纹理…
•和1 2 d纹理。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/C055723666034AE096C4B945D18D726B.png)
第一个3 d纹理…
•4个通道
•这是128 ^ 3决议…
第一个频道是我刚才描述的perlin- worley噪声。
另外3个是在频率增加的Worley噪声。和标准方法一样这个3d纹理用来定义云的基本形状。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/B89809EC8BAA4624B0F4ADDEDD88DB1D.png)
我们的第二个3 d纹理…
•有3个频道…
•它是32 ^ 3决议…
•使用Worley噪音来增加频率。这个纹理被用来向第一个3d噪音定义的基本云形状添加细节。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/7D9FC6AB9E1B4C679B6C79627DCAFE64.png)
•我们的2 d纹理…
•有3个频道…
•这是128 ^ 2分辨率…
•并使用curl噪音。它不是发散的是用来假装流体运动的。我们使用这种噪音来扭曲我们的云形状并增加气流的感觉。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/C6719605F51941139F16BAD78E97E0C4.png)
回想一下,标准解决方案要求高度梯度来改变高度的噪音信号。相反,我们使用…
•3数学预设,代表了主要的低空…
•当我们在样本位置混合时,云的类型。
•我们也有一个值告诉我们希望在样本位置上有多少云覆盖。这是0和1之间的值。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/1AD13B896C2544DBB2DAC66417C158A8.png)
我们在屏幕右边看到的是一个大约30度的视图。我们将会在相机上方的一个区域中按标准方法绘制云。
首先我们建立一个基本的云形状通过采样我们的第一个3d纹理并将它乘以我们的高度信号。
•下一步是将结果乘上覆盖范围,并减少云层底部的密度。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/229424181EA1428C901DB709DF5DBFE6.png)
使用2D燥波增加基础云的细节
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/B5B4D5E3DD0B4C5496A51E591CABDDBD.png)
•通过在云边缘减去第二个3d纹理来侵蚀基本的云形状。小提示如果你在云的基础上颠倒了Worley的噪音你会得到一些很好的声音形状。
•我们也扭曲第二旋度由2 d纹理噪声假纠结的扭曲的大气湍流可以看到…
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/41DD539373F943D28BBDC56D69B5F70F.png)
在这个图像中你可以看到一个小地图在左下角。这代表了在我们的世界地图上驱动云层的天气设置。您所看到的粉红色白色图案是来自我们的天气系统的输出。红色是覆盖层,绿色是降水,蓝色是云类型。天气系统通过模拟在游戏中进行的模拟来调节这些通道。这里的图像有积雨云直接在头顶(白色)和普通积云在远处。我们有控制偏见的方法来保持事物在一般意义上是直接的。
其实就是通过贴图来控制雨云的分布
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/57857EB5097C4544A78B8A4384CE4E4A.png)
我们还利用我们的天气系统来确保云是地平线,这是很有趣的。
•我们画的cloudscape withina球员…周围半径35000米。
•和从15000米的距离…
•我们开始向积云过渡到50%的覆盖率。
总结
所以总结我们的建模方法…
我们遵循标准的ray - march / sampler框架
但是我们用两个层次的细节来构建云
低频率的云基形状
高频细节和失真
我们的噪音是由Perlin、Worley和Curl杂音制成的
我们为每个云类型使用一组预先设置来控制在高度和云覆盖上的密度
这些都是由我们的天气模拟或定制纹理来驱动的,用于使用剪切场景
它在给定的风向中都是动态的。
云的定向散射或发光质量…
•银衬里当你看向太阳通过云…
当你从太阳中移开时,云上的暗边可以看到。
前两个有标准的解决方案,但第三个是我们必须解决的问题。
在光线进入云再到结束,要么被吸收、要么色散
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/45AD92FC56E94A48A02E4CFF452C11DE.png)
Beer's law说我们可以根据所经过的介质的光学厚度来确定光的到达点。根据比尔斯定律我们有一种基本的方法来描述云中的特定点的光量。
•如果我们用能量来替代云中的透光率,并把它画出来,你就可以看到能量指数的下降。这是我们照明模型的基础。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/CECBE3176ED0405185377E0DA67CB4C0.png)
在云层中,有更高的光散射的可能性。这被称为各向异性散射。
在1941年henyey - greenstein模型被开发用来帮助天文学家在星系尺度上进行光计算但今天它被用于在云照明中可靠地复制各向异性。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/DCCE3D5B67DB4F45AF5098B742371AD1.png)
每次我们采样光能量时我们用henyey - greenstein相函数乘以它。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/CDF9EA6FEA9347CE9D8AD1ADD7ACBE88.png)
在这里你可以看到结果。左边的只是我们照明模型的Beers law部分。在右边我们应用了henyey - greenstein相函数。请注意在右边的太阳周围的云层更明亮。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/671F3D41178E45C5BE7ED6753DA19EE6.png)
当我们在云上越走越深时,我们在散射上的潜力就会增加,更多的信息将会到达我们的眼睛。
•如果你把这两个函数得到描述这样的东西…
•效应以及传统方法。
我还在ACM数字图书馆寻找啤酒粉的近似方法但我还没有发现任何与这个名字有关的东西。
将Power公式与Beer's law相结合得到了Beer's power
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/996CDD462EAE44A0B81AF08E2F1E61BB.png)
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/3C34E5D14A164FB1A2A5EC0EAC226033.png)
但我们必须记住这是一个依赖于视图的效果。我们只看到我们的视图矢量接近光矢量所以Power函数也应该解释这个梯度。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/4BA5F7C597C3427D9B88C1997A362875.png)
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/0F8D74F9099449409DA027F8E8433F64.png)
So, in review our model has 3 components:
•Beers Law
•Henyen-Greenstein
•our powder sugar effect
•And Absorption increasing for rain clouds
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/0D33BE22800A49659E2B3F1846C0CCC3.png)
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/693A2257D0094575A428827A016248A2.png)
低海拔1500~4000用体素云。超过4000就用2D ray march云
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/C33B04DE3E5D4F42ABE11A792C015432.png)
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/EE882FE8787B4926B7FC23B50E3E843E.png)
回想一下,采样器有一种低细节的噪声,它能形成基本的云形状
•高细节的噪音,增加我们所需要的现实细节。
高细节的噪声总是应用于从基云形状边缘的侵蚀。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/5682BFB95ED249878E6C9AD2825B4A67.png)
这意味着我们只需要做高细节的噪声和所有相关的指令,其中低细节的样本返回一个非零的结果。
•这就产生了在我们的云所处的区域周围产生一个等表面的效应。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/E1A53D8D6463443A88F411BB0EBA42F1.png)
因为射线长度增加我们看向地平线,我们开始…
•一个初始potential64样本和结束…
•潜在的128位。我之所以说潜力是因为这些优化可能导致3月份提前退出。我们真的希望他们能做到。
这就是我们如何利用样本来建立我们图像的alpha通道。为了计算光强度我们需要取更多的样品。
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/B558F80C8F9A466995AB61AF820A9363.png)
![](D:/youdaonote-pull-master/youdaonote/youdaonote-images/4D891FCBA47D4146BE72624E3B2BC16D.png)
In our approach, we sample 6 times in a cone toward the sun. This smooth's the banding we would normally get with 6 simples and weights our lighting function with neighboring density values, which creates a nice ambient effect. The last sample is placed far away from the rest …
•in order to capture shadows cast by distant clouds.
自阴影算法…………

View File

@@ -0,0 +1,494 @@
## 1.1.1 auto笔记
### auto与引用作用则不会抛弃const
```
int x=0;
const auto& g=x; //g->const int&
auto& h =g; //h->const int&
```
### 什么时候用auto
比如在使用迭代器遍历的时候,对类型及获取并不关心的时候。
### auto推断模板函数对应函数返回值
```
struct Foo {
static int get(void) { return 0; }
};
struct Bar {
static const char* get(void) { return "0"; };
};
struct Bar2 {
};
template <class A>
void func(void)
{
auto val = A::get();
}
int main()
{
func<Foo>();
func<Bar>();
func<Bar2>();//Bar2类中不包含get方法此时编译器会报错
system("pause");
return 0;
}
```
### c++14 增加了函数返回值推导
在C++11下如果你想要打印出一个数的平方可能需要这样
```
auto square_int = [](int x) { return x * x; };
auto square_double = [](double x) { return x * x; };
std::cout<<square_int(10)<<std::endl;
std::cout<<square_int(10.1)<<std::endl;
```
为了保持函数的局部性我们才选择的lambda但C++11的lambda却导致多个类型时代码膨胀且重复此时我们需要回过头来借助全局的模板了。
但C++ 14可以完美的解决上面的问题因为C++14中lambda的参数可以用auto代替具体的类型
```
auto square = [](auto x) { return x * x; };
std::cout<<square_int(10)<<std::endl;
std::cout<<square_int(10.1)<<std::endl;
```
## 1.1.2 decltype
### decltype推导规则
decltype(exp)
1. exp是标识符、类访问表达式decltype(type)和exp类型一样。
2. exp是函数调用decltype(exp)和返回值的类型一致。
3. 其他情况若exp是一个左值则decltype(exp)是exp类型的左值引用否则和exp类型一致。
```
class Foo{
static const int Number=0;
int x;
};
int n=0;
volatile const int &x=n;
decltype(n) a=n; //a->int
decltype(x) b=n; //b->const volatile int &
decltype(Foo::Number) c=0; //c->const int
Foo foo;
decltype(foo.x) d=0; //d->int
```
```
int& func_int_r(void);
int&& func_int_rr(void);
int func_int(void);
const int& func_cint_r(void);
const int&& func_cint_rr(void);
const int func_cint(void);
const Foo func_cfoo(void);
int x=0;
decltype(func_int_r()) a1=x; //a1->int &
decltype(func_int_rr()) b1=x; //b1->int &&
decltype(func_int()) c1=x; //c1->int
decltype(func_cint_r()) a2=x; //a2->const int &
decltype(func_cint_r()) b2=x; //b2->const int &&
decltype(func_cint()) c2=x; //c2->int & 因为是纯右值所以舍弃掉cv符只有类类型可以携带
decltype(func_cfoo()) ff=foo(); //ff->const foo
```
```
struct Foo{int x;};
const Foo foo=Foo();
decltype(foo.x) a=0; //a->int
decltype((foo.x)) b=a; //b->const int &
int n=0,m=0;
decltype(n+m) c=; c=0; //c->int
decltype(n+=m) d=c; d=c; //d->int &
```
a和b的结果仅仅多加了一对括号他们得到的类型却是不同的。具体解释请见P12
++++
### decltype实际应用
具体解释请见P12
## 1.1.3 返回类型后置语法——auto和decltype结合使用
如何实现类似:
```
template <typename R,typename T,typename U>
R add(T t,U u)
{
return t+u;
}
int a=1;floatb=2.0;
auto c=add<decltype(a+b)>(a+b);
```
错误,定义返回值时参数变量还不存在
```
template <typename T,typename U>
decltype(t+u) add(T t,U u)
{
return t+u;
}
```
正确,但是如果T和U是无参构造函数的类就不行了
```
template <typename T,typename U>
decltype(T()+U()) add(T t,U u)
{
return t+u;
}
```
使用c++返回类型后置语法
```
template <typename T,typename U>
auto add(T t,U u)->decltype(t+u)
{
return t+u;
}
```
另一个例子,根据使用的重载函数推导返回类型
```
int& foo(int& i);
float foo(float& f);
template<typename T>
auto func(T& val)-> decltype(foo(val)
{
return foo(val);
}
```
### 1.2.2 模板的别名
using和typedef都可以定义一个新的类型别名但是using可以直接定义一个模板别名而不像typedef需要一个struct 外敷类来实现。而且using的可读性更高。
```
template <typename T>
struct func_t{
typedef void (*type)(t,t);
};
func_t<int>::type xx_1;
template<typename T>
using func_t=void(*)(T,T);
func_t<int> xx_2;
```
另外using可以定义任意类型的模板表达方式
```
template<typename T>
using type_tt = T;
type_tt<int> a;
```
### 1.3.2初始化列表的使用细节
初始化列表可以用于对应类型函数的返回值中。
例如就等于Foo123,321.0假设有Foointfloat这个构造函数
```
Foo function(void)
{
return {123,321.0};
}
```
### 1.3.3初始化列表
自己定义的类并没有类似Vector、Map容器的任意长度的初始化功能如果想要实现可以添加一下构造函数来实现。
```
class Foo
{
public
Foo(std::initializer_list<int> list){
}
};
```
之后就可以执行Foo foo={1,2,3,4,5,6,7}(需要自己实现list对类内部变量的赋值)
同时std::initializer_list可以用于传值
```
void func(std::initializer_list<int> l){
for(auto it=l.begin();it!=l.end();++it)
{std::cout<<*it<<std::endl;}
}
int main()
{
func({});
func({1,2,3})
}
```
PS.std::initializer_list为了提高初始化效率使用的是引用传值所以当传值结束时这些里面的变量也都被销毁了。所以如果需要传值这需要传递它初始化容器的值。
初始化列表同时可以用于检查类型收窄,即因为类型转换而导致的数据丢失。
### 1.4.1基于范围的for循环
当遍历需要修改容器中的值时,则需要使用引用:
```
for(auto& n: arr)
{
std::cout<<n++<<std::endl;
}
```
如果只是希望遍历而不希望修改可以使用const auto& n
### 1.4.2基于范围的for循环使用细节
在:后面可以写带返回遍历对象的函数,而且这个函数只执行一次。
```
#include <iostream>
#include <vector>
std::vector<int> arr={1,2,3,4,5,6};
std::vector<int>& get_range()
{
std::cout<<"get_range->"<<std::endl;
return arr;
}
int main()
{
for(auto val: get_range())
{
std::cout<<val<<std::endl;
}
return 0;
}
```
在使用基于范围的for循环时如果遍历时修改容器大小很可能出现因为迭代器失效而导致的异常情况。
### 1.4.3 让基于范围的for循环支持自定义类型
```
#include <iostream>
namespace detail_range {
template <typename T>
class iterator {
public:
using value_type = T;
using size_type = size_t;
private:
size_type cursor_;
const value_type step_;
value_type value_;
public:
iterator(size_type cur_start, value_type begin_val, value_type step_val)
:cursor_(cur_start), step_(step_val), value_(begin_val)
{
value_ += (step_*cursor_);
}
value_type operator*() const { return value_; }
bool operator!=(const iterator& rhs) const
{
return (cursor_!=rhs.cursor_);
}
iterator& operator++(void)
{
value_ += step_;
++cursor_;
return (*this);
}
};
template <typename T>
class impl {
public:
using value_type = T;
using reference = const value_type&;
using const_reference = const value_type&;
using iterator = const detail_range::iterator<value_type>;
using const_iterator= const detail_range::iterator<value_type>;
using size_type = typename iterator::size_type;//这里的typename是告诉编译器后面跟着的是类型
private:
const value_type begin_;
const value_type end_;
const value_type step_;
const size_type max_count_;
size_type get_adjusted_count(void) const
{
if (step_ > 0 && begin_ >= end_)
throw std::logic_error("End value must be greater than begin value.");
else if (step_ < 0 && begin_ <= end_)
throw std:: logic_error("End value must be less than begin value.");
size_type x = static_cast<size_type>((end_ - begin_) / step_);
if (begin_ + (step_*x) != end_)
++x;//为了防止出现类似{0,12,0.5}之类的迭代要求,而做的升位处理
return x;
}
public:
impl(value_type begin_val, value_type end_val, value_type step_val)
:begin_(begin_val),
end_(end_val),
step_(step_val),
max_count_(get_adjusted_count())
{}
size_type size(void) const
{
return max_count_;
}
const_iterator begin(void) const
{
return{ 0,begin_,step_ };
}
const_iterator end(void) const
{
return{ max_count_,begin_,step_ };
}
};
template <typename T>
detail_range::impl<T> range(T end)
{
return{ {},end,1 };
}
template <typename T>
detail_range::impl<T> range(T begin, T end)
{
return{ begin,end,1 };
}
template <typename T,typename U>
auto range(T begin, T end, U step)->detail_range::impl<decltype(begin + step)>
{
using r_t = detail_range::impl<decltype(begin + step)>;
return r_t(begin, end, step);
}
}
int main()
{
for (int i : detail_range::range(15))
{
std::cout << " " << i;
}
std::cout << std::endl;
for (auto i : detail_range::range(2,8,0.5))
{
std::cout << " " << i;
}
std::cout << std::endl;
system("pause");
return 0;
}
```
### 1.5.2 可调用对象包装器———std::function
用于解决以下可调用对象的统一操作的问题
1. 函数指针
2. 具有operator() 成员函数的类对象(仿函数)
3. 可被转化为函数指针的类对象
4. 类成员(函数)指针
std::function也可以作为函数参数来使用也就是相当于回调函数的作用
### 1.5.3 std::bind绑定器
std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以用std::function进行保存并延迟调用到任何我们需要调用的时候。
作用:
1. 将可调用对象与其他参数一起绑定成一个仿函数
2. 将多元可调用对象转成一元或者(n-1)元可调用对象,即只绑定部分参数。
关于bind的使用
bind&要调用的函数,&对象, 要调用函数的参数1要调用函数的参数2...,_1(bind函数的参数1),_2(bind函数的参数2)...)
如果bind的是一个非静态成员函数第二个参数一定是一个该成员的一个指针后面才是正常的参数。
数this指针作为一个隐含参数传递给非静态成员函数用以指向该成员函数所属类所定义的对象。当不同对象调用同一个类的成员函数代码时编译器会依据该成员函数的this指针所指向的不同对象来确定应该一用哪个对象的数据成员。下面举一个简单例子。
1bind&f)() 假设f是一个全局函数绑定全局函数并调用
2bind (&A::f, A())() 假设A是一个构造函数为空的类这个形式绑定了类的成员函数故第二个参数需要传入一个成员成员静态函数除外
### 1.6 lambda表达式
```
[capture](params)opt->ret{body;};
auto f=[](int a)->int {return a+1;};
```
c++11允许省略lambada表达式的返回值定义
```
auto x1=[](int i) {return i;};
auto x2=[](){return {1,2};); //error:初始化列表无法推断
```
在没有参数列表时,()是可以省略的
```
auto f1=[]{return 1;};
```
```
class A{
public:
int i_=0;
void func(int x,int y)
{
auto x1=[]{return i_;}; //error,没有捕获外部变量
auto x2=[=]{return i_+x+y;}; //ok
auto x3=[&]{return i_+x+y;}; //ok
auto x4=[this]{return i_;}; //ok
auto x5=[this]{return i_+x+y;}; //error,没有捕获x与y
auto x6=[this,x,y]{return i_+x+y;};//Ok
auto x7=[this]{return i_++} //ok
}
};
int a=0,b=1;
auto f1=[]{return a;}; //error没有捕获外部变量
auto f2=[&]{return a++;}; //ok
auto f3=[=]{return a;}; //ok
auto f4=[=]{return a++;}; //error, a是以复制方式捕获的无法修改
auto f5=[a]{return a+b;}; //error,没有捕获b变量
auto f6=[a,&b]{return a+(b++);};//ok
auto f7=[=,&b]{return a+(b++);};//ok
```
需要注意的是默认状态下lambda表达式无法修改通过复制方式捕获的外部变量如果希望修改需啊需要使用引用方式来捕获。
```
int a=0;
auto f=[=]{return a;};
a+=1;
std::cout<<f()<<std::endl;
```
在以上lambda表示式中修改外部变量并不会真正影响到外部我们却仍然无法修改他们。
那么如果希望去修改按值捕获的外部变量就需要显式致命表达式为mutable:
```
int a=0;
auto f1=[=]{return a++;}; //error
auto f2=[=]()mutable{return a++;}; //ok,mutable
```
需要注意的是被mutable修饰的lambda就算没有参数也需要写参数列表。
我们可以使用std::function与std::bind来操作lambda表达式另外对于没有捕获任何变量的lambda表达式还可以被转化为一个普通的函数指针
```
using func_t=int(*)(int);
func_t f=[](int a){return a;};
f(123);
```
lambda表示式可以基本代替std::function(一些老式库不支持lambda)
### 1.7 tupe元组
```
tuple<const char*,int>tp=make_tuple(sendPack,nSendSize);//构建一个tuple
```
使用tuple相当于构建一个对应的结构体<br>
可以使用std::tie来进行解包来获取对应的数值,也可以用来创建元组。
```
int x=1
int y=2;
string s="aa";
auto tp=std::tie(x,s,y);
//tp是实际类型是std::tuple<int&,string&,int&>
const char* data=tp.get<0>();
int len=tp.get<1>();
int x,y;
string a;
std::tie(x,a,y)=tp;
```
你可以使用std::ignore占位符来表示不解某个位置的值。
```
std::tie(std::ignore,std::ignorey)=tp;//只获取了第三个值
```

View File

@@ -0,0 +1,65 @@
### 7.1 委托构造函数和继承构造函数
#### 7.1.1 委托构造函数
委托构造函数允许在同一个类中一个构造函数可以调用另一个构造函数,从而可以在初始化时简化变量的初始化<br>
```
class class_c
{
public:
int max;
int min;
int middle;
class_c(int my_max) { max = my_max > 0 ? my_max : 10; }
class_c(int my_max, int my_min) :class_c(my_max) {
min = my_min > 0 && my_min < max ? my_min : 1;
}
class_c(int my_max, int my_min, int my_middle) :class_c(my_max, my_min) {
middle = my_middle<max&&my_middle>min ? my_middle : 5;
}
};
```
#### 7.1.2 继承构造函数
让派生类直接使用基类的构造函数
```
struct A:B
{
using B::B;
}
```
这个特性对普通函数也有效
### 7.2 原始字面量
```
string str="D:\A\B\test.test";
string str1="D:\\A\\B\\test.test";
string str2=R"()";
//输出
D:AB est.test
D:\A\B\test.test
D:\A\B\test.test
```
可以通过R直接得到原始意义的字符串只有()中的才有效果
### 7.3 final和override
final用来限制某个类不能被继承或者某个虚函数不能被重写
```
struct A{
virtual void foo() final;
}
struct B final:A{
}
```
override确保在派生类中声明的重写函数与基类的虚函数有相同的签名一样的形参防止写错同时也表明将要重写基类的虚函数还可以防止因疏忽把本来想重写积累的虚函数声明承载
### 7.4 内存对齐
#### 7.4.1 内存对齐
主要讲了一些概念以及msvc与gcc的内存对齐机制区别
http://blog.csdn.net/markl22222/article/details/38051483
我们经常会看到内存对齐的应用是在网络收发包中。一般用于发送的结构体都是1字节对齐的目的是统一收发双方可能处于不同平台之间的数据内存布局以及减少不必要的流量消耗。
#### 7.4.2 堆内存的内存对齐
msvc下使用_aligned_malloc使用默认的对齐大小32位是8字节64位是16字节当然也可以自己实现这里提供一个案例。
#### 7.4.3 利用alignas指定内存对齐大小
#### 7.4.4 利用alignof和std::alignment_of获取内存对齐大小
#### 7.4.5 内存对齐的类型std::aligned_storage
### 7.5 c++11新增便利算法

View File

@@ -0,0 +1,508 @@
## 使用c++11消除重复提高代码质量
### 3.1 type_traits———类型萃取
type_traits提供了丰富的编译期计算、查询、判断、转换和选择的帮助类在很多场合都会使用到。
### 3.1.1 基本的type_traits
##### 1.基本的type_traits<br>
无需自己定义static const int或者enum类型只需要从std::integral_constant派生
```
template<typename Type>
struct GetLeftSize : std::integral_constant<int,1>
```
之后用GetLeftSize::value来取值。
##### 2.类型判断的type_traits
这些从std::integral_constant派生的type_traits,可以通过std::is_xxxx来判断T的类型。也可以通过std::is_xxxx::value是否为true来判断模板类型是否为目标类型。
```
#include <iostream>
#include <type_traits>
int main(){
std::cout<<"is_const:"<<std::endl;
std::cout<<"int:"<<std::is_const<int>::value<<std::endl;
std::cout<<"const int:"<<std::is_const<const int>::value<<std::endl;
std::cout<<"const int&:"<<std::is_const<const int&>::value<<std::endl;
std::cout<<"const int*:"<<std::is_const<const int*>::value<<std::endl;
std::cout<<"int* const:"<<std::is_const<int* const>::value<<std::endl;
}
```
##### 3.判断两类型之间的关系traits
```
struct is_same;//判断两类型是否相同
struct is_base_of;//判断一个是否为另一个的基类
struct is_convertible;//判断模板参数类型能否转化为后面的模板参数类型
```
##### 4.类型转换traits
常用的类型的转换traits包括对const的修改————const的移除与添加引用的修改————引用的移除与添加数组的修改和指针的修改。<br>
创建对象时需要去除引用:
```
template<typename T>
typnename std::remove_reference<t>::type* Create()
{
typedef typename std::remove_reference<t>::type U;
return new U();
}
```
模板参数T可能是引用类型而创建对象时需要原始的类型不能用引用类型所以需要将可能的引用引用。同样const也需要考虑进去。<br>
为了简洁可以使用std::decay,移除const与引用。<br>
另一个用处就是用来保存函数指针。
```
{
using FnType=typename std::decay<F>::type;
SimpFunction(F& f):m_fn(f){
}
void run()
{
m_fn();
}
FnType m_fn;
}
```
### 3.1.2根据条件选择traits
```
template<bool B,class T,class F>
struct conditional;
typedef std::conditional<true,int,float>::type A;//int
```
### 3.1.3获取可调用对象返回类型的traits
一些模板函数的返回值不能直接确定虽然可以通过decltype来进行推断但是可读性较差。<br>
可以通过auto -> decltype进行和简化。
```
template<typename F,template Arg>
auto Func(F f,Arg arg)->decltype(f(arg))
{
return f(arg);
}
```
不过在类没有构造函数的情况下就不行了。此时如果希望编译器进行推导就需要使用std::declval。
```
decltype(std::declval<int>(std::declval<int>())) i=4;
```
declval获得的临时值引用不能用于求值因此需要使用decaltype来推断出最终值。<br>
当然可以用更加简洁的方法:
```
std::result_of<A(int)>::type i=4;
```
### 3.1.4 根据条件禁用或者启用某种或者某些类型traits
```
template<class T>
typnename std::enable_if<std::is_arithmetic<T>::value,T>::type foo(T t)
{
return t;
}
auto r=foo(1);
auto r1=foo(1.2);
auto r2=foo("test");//error
```
不但可以作用于返回值,也可以作用于模板定义、类模板特化和入参类型的限定。
## 3.2 可变参数模板
在c++11中允许模板定义中包含0到任意个数模板参数。声明可变参数模板需要在typename或class后面带上省略号
省略号作用有两个:
1. 声明一个参数包这个参数包中可以包含0到任意个模板参数。
2. 在模板定义的右边,可以将参数包展开成一个一个独立参数。
### 3.2.1 可变参数模板函数
```
template<class... T>
void f(T... args)
{
std::cout<<sizeof...(args)<<std::endl;
}
f(); //0
f(1,2); //2
f(1,2.5,""); //3
```
1. 递归函数方式展开参数包
```
void print()
{
std::cout << "empty" << std::endl;
}
template<class T, class... Args>
void print(T head, Args... rest)
{
std::cout << "parameter" << head << std::endl;
print(rest...);
}
int main()
{
print(1,2,3,4,5);
return 0;
}
```
当然递归终止函数也可以写成,或者使用更多参数
```
template<typename T,typename T1>
void print(T t,T1, t1)
{
std::cout<<t<<""<<t1<<std::endl;
}
```
2. 逗号表达式和初始化列表方式展开参数包
逗号运算符:<br>
运算符有左右两个表达式,先计算左侧的,之后丢弃结果再计算右侧。
```
d=(a=b,c);
```
b会先赋值给a接着括号中的逗号表达式将会返回c的值。
```
template <class T>
void printarg(T t)
{
std::cout<<t<<std::endl;
}
template <class ...Args>
void expand(Args... args)
{
int arr[]={(printarg(args),0)...};
}
expand(1,2,3,4);
```
这里使用了c++11的初始化列表特性{(printarg(args),0)...}将会展开成{(printarg(args1),0),(printarg(args2),0),(printarg(args3),0),(printarg(args4),0),}<br>
之后也可以使用std::initializer_list来优化这样就无需定义一个数组了使用lambda的话printarg函数就无需定义了。
### 3.2.2可变参数模板
std::tuple是一个可变参数模型
```
template<class... Type>
class tuple;
```
1. 模板递归和特化方式展开参数包
```
//前向声明
template<typename... Args>
struct Sum;
//基本定义
template<typename First,typne... Rest>
struct Sum<First.Rest...>
{
enum{value=Sum<First>::value+Sum::<Rest...>::value};
};
//递归终止
template<typename Last>
struct Sum<Last>
{
enum{value=sizeof(Last)};
};
```
2. 继承方式展开参数包
有点复杂不做笔记了<br>
除了用继承还可以使用using来实现。
```
template<int N,int... Indexes>
struct MakeIndexes{
using type=MakeIndexes<N-1N-1Indexes...>::type
};
template<int... Indexes>
struct MakeIndexes<0,Indexes...>
{
using type=IndexSeq<Indexes...>;
};
MakeIndexes<3>=>IndexSeq<0,1,2>
```
### 3.2.3可变参数模板
免去手写大量的模板类代码,而让编译器来自动实现。
```
template<typename... Args>
T* Instance(Args... args)
{
return new T(args);
}
A* pa=Instance<A>(1);
B* pb=Instance<B>(1,2);
```
上面代码中的Args是拷贝值使用右值引用与完美转发来消除损耗。
```
template<typename... Args>
T* Instance(Args&&... args)
{
return new T(std::forward<Args>(args)...);
}
```
### 3.3 可变参数模板和type_taits的综合应有
#### 3.3.1 optional的实现
使用placement new的时候容易出现内存对齐的问题可以使用
```
#include <iostream>
#include <type_traits>
struct A
{
int avg;
A(int a,int b):avg((a+b)/2){}
};
typedef std::aligned_storage<sizeof(A),alignof(A)>::type Aligned_A;
int main()
{
Aligned_A a,b;
new (&a) A(10,20);
b=a;
std::cout<<reinterpret_cast<A&>(b).avg<<std::endl;
return0;
}
```
#### 3.3.2 惰性求值类Lazy的实现
```
#ifndef _LAZY_HPP_
#define _LAZY_HPP_
#include <boost/optional.hpp>
template<typename T>
struct Lazy
{
Lazy(){}
template <typename Func, typename... Args>
Lazy(Func& f, Args && ... args)
{
m_func = [&f, &args...]{return f(args...); };
}
T& Value()
{
if (!m_value.is_initialized())
{
m_value = m_func();
}
return *m_value;
}
bool IsValueCreated() const
{
return m_value.is_initialized();
}
private:
std::function<T()> m_func;
boost::optional<T> m_value;
};
template<class Func, typename... Args>
Lazy<typename std::result_of<Func(Args...)>::type>
lazy(Func && fun, Args && ... args)
{
return Lazy<typename std::result_of<Func(Args...)>::type>(
std::forward<Func>(fun), std::forward<Args>(args)...);
}
#endif
```
```
#include "Lazy.hpp"
#include <iostream>
#include <memory>
struct BigObject
{
BigObject()
{
std::cout << "lazy load big object" << std::endl;
}
};
struct MyStruct
{
MyStruct()
{
m_obj = lazy([]{return std::make_shared<BigObject>(); });
}
void Load()
{
m_obj.Value();
}
Lazy<std::shared_ptr<BigObject>> m_obj;
};
int Foo(int x)
{
return x * 2;
}
void TestLazy()
{
//带参数的普通函数
int y = 4;
auto lazyer1 = lazy(Foo, y);
std::cout << lazyer1.Value() << std::endl;
//不带参数的lamda
Lazy<int> lazyer2 = lazy([]{return 12; });
std::cout << lazyer2.Value() << std::endl;
//带参数的fucntion
std::function < int(int) > f = [](int x){return x + 3; };
auto lazyer3 = lazy(f, 3);
std::cout << lazyer3.Value() << std::endl;
//延迟加载大对象
MyStruct t;
t.Load();
}
int main(void)
{
TestLazy();
system("pause");
return 0;
}
```
#### 3.3.3 dll帮助类实现
首先需要封装GetProcAddress函数需要解决
1. 函数的返回值可能是不同类型,如果以一种通用的返回值来消除这种不同返回值导致的差异
2. 函数的入参数目可能任意个数,且类型也不尽相同,如何来消除入参个数和类型的差异
```
#ifndef _DLLPARSER_HPP_
#define _DLLPARSER_HPP_
#include <Windows.h>
#include <string>
#include <map>
#include <functional>
#include <iostream>
class DllParser
{
public:
DllParser()
{
}
~DllParser()
{
UnLoad();
}
bool Load(const std::string& dllPath)
{
m_hMod = LoadLibrary(dllPath.data());
if (nullptr == m_hMod)
{
std::cout << "LoadLibrary failed\n";
return false;
}
return true;
}
template <typename T, typename... Args>
typename std::result_of<std::function<T>(Args...)>::type
ExcecuteFunc(const std::string& funcName, Args&&... args)
{
auto f = GetFunction<T>(funcName);
if (f == nullptr)
{
std::string s = "can not find this function " + funcName;
throw std::exception(s.c_str());
}
return f(std::forward<Args>(args)...);
}
private:
bool UnLoad()
{
if (m_hMod == nullptr)
return true;
auto b = FreeLibrary(m_hMod);
if (!b)
return false;
m_hMod = nullptr;
return true;
}
template <typename T>
T* GetFunction(const std::string& funcName)
{
auto addr = GetProcAddress(m_hMod, funcName.c_str());
return (T*) (addr);
}
private:
HMODULE m_hMod;
std::map<std::string, FARPROC> m_map;
};
#endif //_DLLPARSER_HPP_
```
### 3.3.4 lambda链式调用
```
#include <iostream>
#include <functional>
#include <type_traits>
template<typename T>
class Task;
template<typename R, typename...Args>
class Task<R(Args...)>
{
public:
Task(std::function<R(Args...)>&& f) : m_fn(std::move(f)){}
Task(std::function<R(Args...)>& f) : m_fn(f){}
template<typename... Args>
R Run(Args&&... args)
{
return m_fn(std::forward<Args>(args)...);
}
template<typename F>
auto Then(F& f)->Task<typename std::result_of<F(R)>::type(Args...)>
{
using return_type = typename std::result_of<F(R)>::type;
auto func = std::move(m_fn);
return Task<return_type(Args...)>([func, f](Args&&... args)
{return f(func(std::forward<Args>(args)...)); });
}
private:
std::function<R(Args...)> m_fn;
};
void TestTask()
{
Task<int(int)> task = [](int i){ return i; };
auto result = task.Then([](int i){return i + 1; }).Then([](int i){
return i + 2; }).Then([](int i){return i + 3; }).Run(1);
std::cout << result << std::endl; // 7
}
int main(void)
{
TestTask();
system("pause");
return 0;
}
```
后面若干章节较难,为了节约时间所以跳过了

View File

@@ -0,0 +1,210 @@
### 使用c++11开发一个半同步半异步线程池
```
#pragma once
#include<list>
#include<mutex>
#include<thread>
#include<condition_variable>
#include <iostream>
using namespace std;
template<typename T>
class SyncQueue
{
public:
SyncQueue(int maxSize) :m_maxSize(maxSize), m_needStop(false)
{
}
void Put(const T&x)
{
Add(x);
}
void Put(T&&x)
{
Add(std::forward<T>(x));
}
void Take(std::list<T>& list)
{
std::unique_lock<std::mutex> locker(m_mutex);
m_notEmpty.wait(locker, [this] {return m_needStop || NotEmpty(); });
if (m_needStop)
return;
list = std::move(m_queue);
m_notFull.notify_one();
}
void Take(T& t)
{
std::unique_lock<std::mutex> locker(m_mutex);
m_notEmpty.wait(locker, [this] {return m_needStop || NotEmpty(); });
if (m_needStop)
return;
t = m_queue.front();
m_queue.pop_front();
m_notFull.notify_one();
}
void Stop()
{
{
std::lock_guard<std::mutex> locker(m_mutex);
m_needStop = true;
}
m_notFull.notify_all();
m_notEmpty.notify_all();
}
bool Empty()
{
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.empty();
}
bool Full()
{
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.size() == m_maxSize;
}
size_t Size()
{
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.size();
}
int Count()
{
return m_queue.size();
}
private:
bool NotFull() const
{
bool full = m_queue.size() >= m_maxSize;
if (full)
cout << "full, waitingthread id: " << this_thread::get_id() << endl;
return !full;
}
bool NotEmpty() const
{
bool empty = m_queue.empty();
if (empty)
cout << "empty,waitingthread id: " << this_thread::get_id() << endl;
return !empty;
}
template<typename F>
void Add(F&&x)
{
std::unique_lock< std::mutex> locker(m_mutex);
m_notFull.wait(locker, [this] {return m_needStop || NotFull(); });
if (m_needStop)
return;
m_queue.push_back(std::forward<F>(x));
m_notEmpty.notify_one();
}
private:
std::list<T> m_queue; //缓冲区
std::mutex m_mutex; //互斥量和条件变量结合起来使用
std::condition_variable m_notEmpty;//不为空的条件变量
std::condition_variable m_notFull; //没有满的条件变量
int m_maxSize; //同步队列最大的size
bool m_needStop; //停止的标志
};
```
```
#pragma once
#include<list>
#include<thread>
#include<functional>
#include<memory>
#include <atomic>
#include "SyncQueue.hpp"
const int MaxTaskCount = 100;
class ThreadPool
{
public:
using Task = std::function<void()>;
ThreadPool(int numThreads = std::thread::hardware_concurrency()) : m_queue(MaxTaskCount)
{
Start(numThreads);
}
~ThreadPool(void)
{
//如果没有停止时则主动停止线程池
Stop();
}
void Stop()
{
std::call_once(m_flag, [this]{StopThreadGroup(); }); //保证多线程情况下只调用一次StopThreadGroup
}
void AddTask(Task&&task)
{
m_queue.Put(std::forward<Task>(task));
}
void AddTask(const Task& task)
{
m_queue.Put(task);
}
private:
void Start(int numThreads)
{
m_running = true;
//创建线程组
for (int i = 0; i <numThreads; ++i)
{
m_threadgroup.push_back(std::make_shared<std::thread>(&ThreadPool::RunInThread, this));
}
}
void RunInThread()
{
while (m_running)
{
//取任务分别执行
std::list<Task> list;
m_queue.Take(list);
for (auto& task : list)
{
if (!m_running)
return;
task();
}
}
}
void StopThreadGroup()
{
m_queue.Stop(); //让同步队列中的线程停止
m_running = false; //置为false让内部线程跳出循环并退出
for (auto thread : m_threadgroup) //等待线程结束
{
if (thread)
thread->join();
}
m_threadgroup.clear();
}
std::list<std::shared_ptr<std::thread>> m_threadgroup; //处理任务的线程组
SyncQueue<Task> m_queue; //同步队列
atomic_bool m_running; //是否停止的标志
std::once_flag m_flag;
};
```

View File

@@ -0,0 +1,151 @@
## 使用c++改进出程序性能
### 2.1右值引用
左值:表达式结束后依然存在的持久对象</br>
右值:表达式结束时就不存在的临时对象</br>
一个区分左值与右值的便捷方法:看能不能对表达式取地址,如果能则为左值,否则为右值。所有的具名变量或者对象都未左值,而右值不具名。</br>
右值有两个概念构成,一个是将亡值,另一个是纯右值。
### 2.1.1 &&的特性
右值引用就是对一个右值进行引用的类型。因为右值不具名,所以我们只能通过引用的方式找到它。
```
template<typnename T>
void f(T&& param); //T的类型需要推断,所以是个universal references
template<typename T>
class Test{
Test(Test&& rhs); //已经定义类型,所以&&是个右值
};
void f(Test&& rhs); //同上
template<typnename T>
void f(const T&& params); //&&是个右值因为universal references仅仅在T&&下发生。
```
T&&被左值或者右值初始化被称为引用折叠。<br>
总结:
1. 左值和右值是独立于他们的类型的,右值引用类型可能是左值也可以是右值。
2. auto&&或是函数类型自动推导的T&&是一个未定的引用类型。
3. 所有的右值引用叠加到右值引用上仍然是右值引用其他都是左值引用。当T&&为函数模板时,输入左值,它会变成左值引用,而输入右值时则会变成具名的右值引用。
4. 编译器会将已经命名的右值引用视为左值,而将未命名的右值引用视为右值。
### 2.1.2 右值引用优化性能,避免深拷贝
对于含有堆内存的类,我们需要提供深拷贝的拷贝构造函数,如果使用默认构造函数,会导致堆内存的重复删除:
```
class A{
public:
A():m_ptr(new int(0))
{
}
~A()
{
delete m_ptr;
}
private:
int* m_ptr;
};
A Get(bool flag)
{
A a;
A b;
if(flag)
return a;
else
return b;
}
int main()
{
A a=Get(false); //运行出错
}
```
在上面的代码中默认构造函数是浅拷贝a和b会指向同一个指针m_ptr,在析构时会导致重复删除该指针,正确的方法是提供深拷贝函数。<br>
```
A(const A& a):m_ptr(new int(*a.m_ptr))
{
}
```
但是有时这种拷贝构造函数是没有必要的,使用移动构造函数会更好。<br>
```
A(A&& a):m_ptr(a.m_ptr)
{
a.m_ptr=nullptr;
}
```
这里的A&&用来根据参数是左值还是右值来建立分支,如果是临时值,则会选择移动构造函数。<br>
需要注意的是:一般在提供右值引用的构造函数同时,也会提供左值引用的拷贝构造函数,以保证移动不成还可以用拷贝;提供了移动也同时提供拷贝构造函数。<br>
有关拷贝构造函数相关的一些笔记:<br>
拷贝构造函数是C++独有的,它是一种特殊的构造函数,用基于同一类的一个对象构造和初始化另一个对象。
当没有重载拷贝构造函数时,通过默认拷贝构造函数来创建一个对象
A a;
A b(a);
A b=a; 都是拷贝构造函数来创建对象b
强调这里b对象是不存在的是用a 对象来构造和初始化b的
浅拷贝:如果复制的对象中引用了一个外部内容(例如分配在堆上的数据),那么在复制这个对象的时候,让新旧两个对象指向同一个外部内容,就是浅拷贝。(指针虽然复制了,但所指向的空间内容并没有复制,而是由两个对象共用,两个对象不独立,删除空间存在)<br>
深拷贝:如果在复制这个对象的时候为新对象制作了外部对象的独立复制,就是深拷贝。
拷贝构造函数重载声明如下:
```
A (const A&other)
```
下面为拷贝构造函数的实现:
```
class A
{
int m_i
A(const A& other):m_i(other.m_i)
{
Cout<<”拷贝构造函数”<<endl;
}
}
```
赋值函数<br>
当一个类的对象向该类的另一个对象赋值时,就会用到该类的赋值函数。
当没有重载赋值函数(赋值运算符)时,通过默认赋值函数来进行赋值操作
A a;
A b;
b=a; <br>
强调这里a,b对象是已经存在的是用a 对象来赋值给b的
赋值运算的重载声明如下:
```
A& operator = (const A& other)
```
1. 拷贝构造函数是一个对象初始化一块内存区域,这块内存就是新对象的内存区,而赋值函数是对于一个已经被初始化的对象来进行赋值操作。
```
class A;
A a;
A b=a; //调用拷贝构造函数b不存在
A c(a) ; //调用拷贝构造函数
/****/
class A;
A a;
A b;
b = a ; //调用赋值函数(b存在)</span>
```
2. 一般来说在数据成员包含指针对象的时候,需要考虑两种不同的处理需求:一种是复制指针对象,另一种是引用指针对象。拷贝构造函数大多数情况下是复制,而赋值函数是引用对象
3. 实现不一样。拷贝构造函数首先是一个构造函数,它调用时候是通过参数的对象初始化产生一个对象。赋值函数则是把一个新的对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检察一下两个对象是不是同一个对象,如果是,不做任何操作,直接返回。
### 2.2 move语义
普通左值也可以借助移动语义进行优化<br>
c++11提供std::move方法将左值转化为右值从而方便使用移动语义。即强制将左值转化为右值引用。
```
{
std::list<std::string> tokens;
std::list<std::string> t=tokens;
}
std::list<std::string> tokens;
std::list<std::string> t=std::move(tokens);
```
事实上标准库容器都实现了move语义而一些int之类的基础类型计算使用move但仍然会发生拷贝。
### 2.3 forward和完美转发
右值引用类型独立于值,一个右值引用参数作为函数的形参,在函数内部再转发该参数时它已经变成一个左值了。<br>
所以我们需要“完美转发”是指在函数模板中完全依照模板的参数的类型即保持参数的左值、右值特征讲参数传递给函数模板中调用另一个函数。c++11中提供了std::forward
### 2.4emplace_back减少内存拷贝与移动
emplace_back能通过参数构造对象不需要拷贝或者移动内存相比push_back能够更好得避免内存的拷贝与移动使得容器性能得到进一步提升。大多数情况下应该优先使用emplace_back。<br>
其原理是直接利用构造函数直接构造对象<br>
当然在push_back还不能完全被代替比如在没有提供构造函数的的struct。

View File

@@ -0,0 +1,647 @@
## 使用c++11让线程线程开发变得简单
### 5.1 线程创建
1. jon()
```
#include <thread>
void func(){}
int main()
{
std::thread t(func);
t.join();//join将会阻塞当前线程直到线程执行结束
return 0;
}
```
2. detach
```
#include <thread>
void func(){}
int main()
{
std::thread t(func);
t.detach();//线程与对象分离,让线程作为后台线程去运行,但同时也无法与线程产生联系(控制)了
return 0;
}
```
线程不能被复制,但是可以被移动
```
#include <thread>
void func(){}
int main()
{
std::thread t(func);
std::thread t2(std::move(t));
t2.join();
return 0;
}
```
需要注意线程的生命周期
```
#include <thread>
void func(){}
int main()
{
std::thread t(func);
return 0;
}
```
以上代码会报错因为线程对象先于线程结束了可以通过join()或者detach()来解决,当然也可以放到一个容器中来保存,以保证线程生命周期
```
#include <thread>
std::vector<std::thread> g_list;
std::vector<std::shared_ptr<std::thread>> g_list2;
void func(){}
void CreateThread()
{
std::thread t(func);
g_list.push_back(std::move(t));
g_list2.push_back(std::make_shared<std::thread>(func));
}
int main()
{
void CreateThread();
for(auto& thread : g_list)
{
thread.join();
}
for(auto& thread : g_list2)
{
thread->join();
}
return 0;
}
```
#### 线程的基本用法
1. 获取当前信息
```
void func()
{
}
int main()
{
std::thread t(func);
std::cout << t.get_id() << std::endl;//当前线程id
//获得cpu核心数
std::cout << std::thread::hardware_concurrency() << std::endl;
}
```
2. 线程休眠
```
void f() {
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "time out" << std::endl;
}
int main()
{
std::thread t(f);
t.join();
}
```
### 互斥量
#### 独占互斥锁std::mutex
```
std::mutex g_lock;
void func() {
g_lock.lock();
std::cout << "entered thread" << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "leaving thread" << std::this_thread::get_id() << std::endl;
g_lock.unlock();
}
int main()
{
std::thread t1(func);
std::thread t2(func);
std::thread t3(func);
t1.join();
t2.join();
t3.join();
}
```
使用lock_guard可以简化lock/unlock同时也更加安全。因为是在构造时自动锁定互斥量析构时自动解锁。
```
void func() {
std::lock_guard<std::mutex> locker(g_lock);
std::cout << "entered thread" << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "leaving thread" << std::this_thread::get_id() << std::endl;
}
```
#### 5.2.2 递归的独占互斥量std::recursive_mutex
递归锁允许同一个线程多次获得该锁,可以用来解决同一线程需要多次获取互斥量时死锁的问题。
```
struct Complex
{
std::mutex mutex;
int i;
Complex() :i(0) {}
void mul(int x) {
std::lock_guard<std::mutex> lock(mutex);
i *= x;
}
void div(int x) {
std::lock_guard<std::mutex> lock(mutex);
i *= x;
}
void both(int x,int y) {
std::lock_guard<std::mutex> lock(mutex);
mul(x);
div(y);
}
};
int main()
{
Complex complex;
complex.both(32, 23);
}
```
简单的方法就是使用std::recursive_mutex
```
struct Complex
{
std::recursive_mutex mutex;
int i;
Complex() :i(0) {}
void mul(int x) {
std::lock_guard<std::recursive_mutex> lock(mutex);
i *= x;
}
void div(int x) {
std::lock_guard<std::recursive_mutex> lock(mutex);
i *= x;
}
void both(int x,int y) {
std::lock_guard<std::recursive_mutex> lock(mutex);
mul(x);
div(y);
}
};
int main()
{
Complex complex;
complex.both(32, 23);
}
```
需要注意的是:
1. 需要用到递归锁定的多线程互斥处理往往本身就可以简化,允许递归互斥很容易放纵复杂逻辑的产生
2. 递归锁比起非递归锁效率会低一些
3. 递归锁虽然允许同一个线程多次获得同一个互斥锁可重复获得的最大次数并未具体说明一旦超过一定次数再对lock进行调用就会抛出std::system错误
#### 5.2.3带超时的互斥量std::timed_mutex和std::recursive_timed_mutex
主要用于在获取锁时增加超时等待功能。不至于一直等待
```
std::timed_mutex mutex;
void work()
{
std::chrono::milliseconds timeout(100);
while (true)
{
if (mutex.try_lock_for(timeout))
{
std::cout << std::this_thread::get_id() << ": do work with the mutex" << std::endl;
std::chrono::milliseconds sleepDuration(250);
std::this_thread::sleep_for(sleepDuration);
mutex.unlock();
std::this_thread::sleep_for(sleepDuration);
}
else
{
std::cout << std::this_thread::get_id() << ": do work without the mutex" << std::endl;
std::chrono::milliseconds sleepDuration(100);
std::this_thread::sleep_for(sleepDuration);
}
}
}
int main(void)
{
std::thread t1(work);
std::thread t2(work);
t1.join();
t2.join();
return 0;
}
```
#### 5.3 条件变量
条件变量时c++11提供的另外一种用于等待的同步机制它能柱塞一个或者多个线程直到收到另一个线程发出的通知或者超时才会唤醒当前阻塞的线程。<br>
condition_variable
condition_variable_any
```
#include<list>
#include<mutex>
#include<thread>
#include<condition_variable>
#include <iostream>
template<typename T>
class SyncQueue
{
private:
bool IsFull() const
{
return m_queue.size() == m_maxSize;
}
bool IsEmpty() const
{
return m_queue.empty();
}
public:
SyncQueue(int maxSize) : m_maxSize(maxSize)
{
}
void Put(const T& x)
{
std::lock_guard<std::mutex> locker(m_mutex);
while (IsFull())
{
std::cout << "缓冲区满了,需要等待..." << std::endl;
m_notFull.wait(m_mutex);
}
m_queue.push_back(x);
m_notFull.notify_one();
}
void Take(T& x)
{
std::lock_guard<std::mutex> locker(m_mutex);
while (IsEmpty())
{
std::cout << "缓冲区空了,需要等待..." << std::endl;
m_notEmpty.wait(m_mutex);
}
x = m_queue.front();
m_queue.pop_front();
m_notFull.notify_one();
}
bool Empty()
{
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.empty();
}
bool Full()
{
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.size() == m_maxSize;
}
size_t Size()
{
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.size();
}
int Count()
{
return m_queue.size();
}
private:
std::list<T> m_queue; //缓冲区
std::mutex m_mutex; //互斥量和条件变量结合起来使用
std::condition_variable_any m_notEmpty;//不为空的条件变量
std::condition_variable_any m_notFull; //没有满的条件变量
int m_maxSize; //同步队列最大的size
};
```
更好的方法是使用unique_lockunique可以随时释放锁
```
#include <thread>
#include <condition_variable>
#include <mutex>
#include <list>
#include <iostream>
template<typename T>
class SimpleSyncQueue
{
public:
SimpleSyncQueue(){}
void Put(const T& x)
{
std::lock_guard<std::mutex> locker(m_mutex);
m_queue.push_back(x);
m_notEmpty.notify_one();
}
void Take(T& x)
{
std::unique_lock<std::mutex> locker(m_mutex);
m_notEmpty.wait(locker, [this]{return !m_queue.empty(); });
x = m_queue.front();
m_queue.pop_front();
}
bool Empty()
{
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.empty();
}
size_t Size()
{
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.size();
}
private:
std::list<T> m_queue;
std::mutex m_mutex;
std::condition_variable m_notEmpty;
};
```
#### 5.4 原子变量
使用原子变量就不需要来使用互斥量来保护该变量了<br>
使用std::mutex
```
#include <iostream>
#include <thread>
#include <mutex>
struct Counter
{
int value = 0;
std::mutex mutex;
void increment()
{
std::lock_guard<std::mutex> lock(mutex);
++value;
std::cout << value << std::endl;
}
void decrement()
{
std::lock_guard<std::mutex> lock(mutex);
--value;
std::cout << value << std::endl;
}
int get()
{
return value;
}
};
Counter g_counter;
void Increments()
{
for (int i = 0; i < 10; ++i)
{
g_counter.increment();
}
}
void Decrements()
{
for (int i = 0; i < 5; ++i)
{
g_counter.decrement();
}
}
int main(void)
{
std::thread t1(Increments);
std::thread t2(Decrements);
t1.join();
t2.join();
system("pause");
return 0;
}
```
使用std::atomic<T>
```
#include <iostream>
#include <thread>
#include <atomic>
struct Counter
{
std::atomic<int> value = 0;
void increment()
{
++value;
}
void decrement()
{
--value;
}
int get()
{
return value;
}
};
Counter g_counter;
void Increments()
{
for (int i = 0; i < 10; ++i)
{
g_counter.increment();
std::cout << g_counter.get() << std::endl;
}
}
void Decrements()
{
for (int i = 0; i < 5; ++i)
{
g_counter.decrement();
std::cout << g_counter.get() << std::endl;
}
}
int main(void)
{
std::thread t1(Increments);
std::thread t2(Decrements);
t1.join();
t2.join();
system("pause");
return 0;
}
```
#### 5.5 call_once/once_flag的使用
为了保证多线程环境中某个函数仅被调用一次,比如需要初始化某个对象,而这个对象只能初始化一次。
```
#include <thread>
#include <iostream>
#include <mutex>
std::once_flag flag;
void do_once() {
std::call_once(flag, [] {std::cout << "Called once" << std::endl; });
}
int main(void)
{
std::thread t1(do_once);
std::thread t2(do_once);
std::thread t3(do_once);
t1.join();
t2.join();
t3.join();
system("pause");
return 0;
}
```
#### 5.6 异步操作类
#### 5.6.1 std::future
thread库提供了future用来访问异步操作结果因为异步操作结果是一个未来的期待值所以被称为future。future提供了异步获取操作结果的通道我们可以以同步等待的方式来获取结果可以通过查询future的状态(future_status)来获取异步操作结果。
```
std::future_status status;
do
{
status = future.wait_for(std::chromno::seconds(1));
if (status == std::future_status::deferred) {
}
else if (status == std::future_status::timeout) {
}
else if (status==std::future_status::ready) {
}
} while (status!= std::future_status::ready);
```
获取future有三种方式:
1. get等待异步操作结束并返回结果
2. wait等待异步操作完成
3. wait_for超时等待返回结果
#### 5.6.2 std::promise
std::promise将数据和future绑定起来为获取线程函数中的某个值提供便利在线程函数中为外面传进来的promise赋值在线程函数执行完成之后就可以通过promise的future获取该值。
```
std::promise<int> pr;
std::thread t([](std::promise<int>&p) {
p.set_value_at_thread_exit(9);
}, std::ref(pr));
std::future<int> f = pr.get_future();
auto r = f.get();
```
#### 5.6.3 std::package_task
```
std::packaged_task<int()> task([]() {return 7; });
std::thread t1(std::ref(task));
std::future<int> f1 = task.get_future();
auto r1 = f1.get();
```
#### 5.6.4 以上三者关系
std::future提供了一个访问异步操作结果的机制它和线程是一个级别的属于低层次对象。之上是std::packaged_task和std::promise他们内部都有future以便访问异步操作结果std::packaged_task包装的是一个异步操作std::promise包装的是一个值。那这两者又是什么关系呢可以将一个异步操作的结果放到std::promise中。<br>
future被promise和package_task用来作为异步操作或者异步的结果的连接通道用std::future和std::shared_future来获取异步的调用结果。future是不可拷贝的shared_future是可以拷贝的当需要将future放到容器中则需要shared_future。
```
#include <iostream>
#include <thread>
#include <utility>
#include <future>
#include <vector>
int func(int x) { return x + 2; }
int main(void)
{
std::packaged_task<int(int)> tsk(func);
std::future<int> fut = tsk.get_future(); //获取future
std::thread(std::move(tsk), 2).detach();
int value = fut.get(); //等待task完成并获取返回值
std::cout << "The result is " << value << ".\n";
std::vector<std::shared_future<int>> v;
std::shared_future<int> f = std::async(std::launch::async, [](int a, int b){return a + b; }, 2, 3);
v.push_back(f);
std::cout << "The shared_future result is " << v[0].get() << std::endl;
return 0;
}
```
#### 5.7 线程异步操作函数
std::async可以直接创建异步的task异步任务结果也保存在future中调用furturn.get()获取即可如果不关心结果可以使用furturn.wait()<br>
两种线程创建策略:<br>
1. std::launch::async:在调用async时就开始创建线程。
2. std::launch::deferred:延迟加载方式创建线程。调用async时不创建线程直到调用future的get或者wait时才创建。
```
#include <iostream>
#include <future>
void TestAsync()
{
std::future<int> f1 = std::async(std::launch::async, [](){
return 8;
});
std::cout << f1.get() << std::endl; //output: 8
std::future<void> f2 = std::async(std::launch::async, [](){
std::cout << 8 << std::endl; return;
});
f2.wait(); //output: 8
std::future<int> future = std::async(std::launch::async, [](){
std::this_thread::sleep_for(std::chrono::seconds(3));
return 8;
});
std::cout << "waiting...\n";
std::future_status status;
do {
status = future.wait_for(std::chrono::seconds(1));
if (status == std::future_status::deferred)
{
std::cout << "deferred\n";
}
else if (status == std::future_status::timeout)
{
std::cout << "timeout\n";
}
else if (status == std::future_status::ready)
{
std::cout << "ready!\n";
}
} while (status != std::future_status::ready);
std::cout << "result is " << future.get() << '\n';
}
int main(void)
{
TestAsync();
return 0;
}
```

View File

@@ -0,0 +1,249 @@
### 使用c++11改进我们的模式
### 8.1 改进单例模式
单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。
```
#pragma once
template <typename T>
class Singleton
{
public:
template<typename... Args>
  static T* Instance(Args&&... args)
  {
if(m_pInstance==nullptr)
m_pInstance = new T(std::forward<Args>(args)...);
return m_pInstance;
}
  static T* GetInstance()
  {
    if (m_pInstance == nullptr)
      throw std::logic_error("the instance is not init, please initialize the instance first");
    return m_pInstance;
  }
static void DestroyInstance()
{
delete m_pInstance;
m_pInstance = nullptr;
}
private:
Singleton(void);
virtual ~Singleton(void);
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
private:
static T* m_pInstance;
};
template <class T> T* Singleton<T>::m_pInstance = nullptr;
```
使用了可变参数与完美转发避免了写N个模板函数
单例模式应用的场景一般发现在以下条件下:
1. 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
2. 控制资源的情况下,方便资源之间的互相通信。如线程池等。
3. 只需一个存在就可以的情况
### 8.2 改进观察者模式
观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都能得到通知并被自动更新。
```
#pragma once
class NonCopyable
{
public:
NonCopyable(const NonCopyable&) = delete; // deleted
NonCopyable& operator = (const NonCopyable&) = delete; // deleted
NonCopyable() = default; // available
};
```
```
#include <iostream>
#include <string>
#include <functional>
#include <map>
using namespace std;
#include "NonCopyable.hpp"
template<typename Func>
class Events : NonCopyable
{
public:
//注册观察者,支持右值引用
int Connect(Func&& f)
{
return Assgin(f);
}
//注册观察者
int Connect(const Func& f)
{
return Assgin(f);
}
//移除观察者
void Disconnect(int key)
{
m_connections.erase(key);
}
//通知所有的观察者
template<typename... Args>
void Notify(Args&&... args)
{
for (auto& it: m_connections)
{
it.second(std::forward<Args>(args)...);
}
}
private:
//保存观察者并分配观察者的编号
template<typename F>
int Assgin(F&& f)
{
int k=m_observerId++;
m_connections.emplace(k, std::forward<F>(f));
return k;
}
int m_observerId=0;//观察者对应的编号
std::map<int, Func> m_connections;//观察者列表
};
```
### 8.3 改进访问者模式
访问者模式需要注意的问题:定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重新定义对所有访问者的接口。<br>
通过可变参数模板就可以实现一个稳定的接口层,可以让访问者的接口层访问任意个数的访问者,这样就不需要每增加一个新的被访问者就修改接口层,从而使接口层保证稳定。
```
template<typename... Types>
struct Visitor;
template<typename T, typename... Types>
struct Visitor<T, Types...> : Visitor<Types...>
{
using Visitor<Types...>::Visit;//通过using避免隐藏基类的visit同名方法
virtual void Visit(const T&) = 0;
};
template<typename T>
struct Visitor<T>
{
virtual void Visit(const T&) = 0;
};
```
### 8.4 改进命令模式
命令模式的作用是将请求封装成一个对象,将请求的发起者和执行者解耦,支持对请求排队、撤销和重做。由于将请求都封装成一个一个命令对象了,使得我们可以集中处理或者延迟处理这些命令请求,而且不同客户对象可以共享这些命令,还可以控制请求的优先权、排队、支持请求的撤销和重做。<br>
缺点:越来越多的命令会导致类爆炸,难以管理<br>
接收function、函数对象、lambda和普通函数的包装器
```
template<class F,class... Args,class=typename std::enable_if<!std::is_member_function_pointer<F>::value>::type>
void Wrap(F&& f, Args && ...args)
{
return f(std::forward<Args>(args)...);
}
```
接收成员函数的包装器:
```
template<class R,class C,class... DArgs,class P,class... Args>
void Wrap(R(C::*f)(DArgs...),P&& p,Args&& ... args)
{
return; (*p.*f)(std::forward<Args>(args)...);
}
```
```
#include <functional>
#include <type_traits>
template<typename Ret = void>
struct CommCommand
{
private:
std::function < Ret()> m_f;
public:
//接受可调用对象的函数包装器
template< class F, class... Args, class = typename std::enable_if<!std::is_member_function_pointer<F>::value>::type>
void Wrap(F && f, Args && ... args)
{
m_f = [&]{return f(args...); };
}
//接受常量成员函数的函数包装器
template<class R, class C, class... DArgs, class P, class... Args>
void Wrap(R(C::*f)(DArgs...) const, P && p, Args && ... args)
{
m_f = [&, f]{return (*p.*f)(args...); };
}
//接受非常量成员函数的函数包装器
template<class R, class C, class... DArgs, class P, class... Args>
void Wrap(R(C::*f)(DArgs...), P && p, Args && ... args)
{
m_f = [&, f]{return (*p.*f)(args...); };
}
Ret Excecute()
{
return m_f();
}
};
```
### 8.5 改进对象池
```
#include <string>
#include <functional>
#include <memory>
#include <map>
#include "NonCopyable.hpp"
using namespace std;
const int MaxObjectNum = 10;
template<typename T>
class ObjectPool : NonCopyable
{
template<typename... Args>
using Constructor = std::function<std::shared_ptr<T>(Args...)>;
public:
//默认创建多少个对象
template<typename... Args>
void Init(size_t num, Args&&... args)
{
if (num<= 0 || num> MaxObjectNum)
throw std::logic_error("object num out of range.");
auto constructName = typeid(Constructor<Args...>).name(); //不区分引用
for (size_t i = 0; i <num; i++)
{
m_object_map.emplace(constructName, shared_ptr<T>(new T(std::forward<Args>(args)...), [this, constructName](T* p) //删除器中不直接删除对象,而是回收到对象池中,以供下次使用
{
m_object_map.emplace(std::move(constructName), std::shared_ptr<T>(p));
}));
}
}
//从对象池中获取一个对象
template<typename... Args>
std::shared_ptr<T> Get()
{
string constructName = typeid(Constructor<Args...>).name();
auto range = m_object_map.equal_range(constructName);
for (auto it = range.first; it != range.second; ++it)
{
auto ptr = it->second;
m_object_map.erase(it);
return ptr;
}
return nullptr;
}
private:
multimap<string, std::shared_ptr<T>> m_object_map;
};
```

View File

@@ -0,0 +1,120 @@
### 使用c++11中便利的工具
#### 6.1 处理日期和时间的chrono库
#### 6.1.1 记录时长的duration
```
std::chrono::duration <rep,std::ratio<1,1>> seconds;//表示秒
```
chrono的count(),可以获取时钟周期数<br>
需要注意的是当两个duration时钟周期不同的时候会先统一周期再进行计算。<br>
还可以通过duration_cast<>()进行时钟周期的转换
#### 6.1.2 表示时间点的time point
#### 6.1.3 获得系统时钟的clocks
#### 6.1.4 计时器timer
```
#include<chrono>
class Timer
{
public:
Timer() : m_begin(std::chrono::high_resolution_clock::now()) {}
void reset()
{
m_begin = std::chrono::high_resolution_clock::now();
}
//默认输出毫秒
int64_t elapsed() const
{
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - m_begin).count();
}
//微秒
int64_t elapsed_micro() const
{
return std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now() - m_begin).count();
}
//纳秒
int64_t elapsed_nano() const
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::high_resolution_clock::now() - m_begin).count();
}
//秒
int64_t elapsed_seconds() const
{
return std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::high_resolution_clock::now() - m_begin).count();
}
//分
int64_t elapsed_minutes() const
{
return std::chrono::duration_cast<std::chrono::minutes>(
std::chrono::high_resolution_clock::now() - m_begin).count();
}
//时
int64_t elapsed_hours() const
{
return std::chrono::duration_cast<std::chrono::hours>(
std::chrono::high_resolution_clock::now() - m_begin).count();
}
private:
std::chrono::time_point<std::chrono::high_resolution_clock> m_begin;
};
void fun()
{
std::cout << "hello world" << std::endl;
}
void Test()
{
std::cout << "\nTest()\n";
Timer t; //开始计时
fun();
std::cout << t.elapsed_seconds() << std::endl; //打印fun函数耗时多少秒
std::cout << t.elapsed_nano() << std::endl; //打印纳秒
std::cout << t.elapsed_micro() << std::endl; //打印微秒
std::cout << t.elapsed() << std::endl; //打印毫秒
std::cout << t.elapsed_seconds() << std::endl; //打印秒
std::cout << t.elapsed_minutes() << std::endl; //打印分钟
std::cout << t.elapsed_hours() << std::endl; //打印小时
}
int main(void)
{
Test();
system("pause");
return 0;
}
```
#### 6.2 数值类型和字符类型的互相转换
std::string to_string(type value)
std::string to_wstring(type value)
1. atoi字符串转int
2. atol字符串转long
3. atoll字符串转long long
4. atof字符串转float
#### 6.3 宽窄字符转换
```
std::wstring str=L"中国人";
//宽窄字符转换器
std::codecvt_utf8
std::codecvt_utf16
std::codecvt_utf8_utf16
std::wstring_convert
```

View File

@@ -0,0 +1,121 @@
### 使用c++11开发一个轻量级的Ioc容器
Ioc容器具备两种能力一种是对象工厂不仅可以创建所有的对象还可以根据配置去创建对象另一种能力是可以去创建依赖对象应有不需要直接创建对象由Ioc容器去创建实现控制反转。<br>
实现Ioc容器需要解决三个问题第一个问题是创建所有类型的对象第二个问题是类型擦除第三个问题是如何创建依赖对象。
#### 类型擦除的常用方法
类型擦除就是讲原有类型消除或者隐藏。常用的方法有:
1. 通过多态来擦除类型
2. 通过模板来擦除类型
3. 通过某种类型的模板容器擦除类型(Variant)
4. 通过某种通用类型来擦除类型(any)
5. 通过闭包来擦除类型通过模板函数将数值包入函数中再包入std::function
```
#pragma once
#include<string>
#include<unordered_map>
#include<memory>
#include<functional>
using namespace std;
#include<Any.hpp>
#include <NonCopyable.hpp>
class IocContainer : NonCopyable
{
public:
IocContainer(void){}
~IocContainer(void){}
template<class T, typename Depend, typename... Args>
void RegisterType(const string& strKey)
{
std::function<T* (Args...)> function = [](Args... args){ return new T(new Depend(args...)); };//通过闭包擦除了参数类型
RegisterType(strKey, function);
}
template<class T, typename... Args>
T* Resolve(const string& strKey, Args... args)
{
if (m_creatorMap.find(strKey) == m_creatorMap.end())
returnnullptr;
Any resolver = m_creatorMap[strKey];
std::function<T* (Args...)> function = resolver.AnyCast<std::function<T* (Args...)>>();
return function(args...);
}
template<class T, typename... Args>
std::shared_ptr<T> ResolveShared(const string& strKey, Args... args)
{
T* t = Resolve<T>(strKey, args...);
return std::shared_ptr<T>(t);
}
private:
void RegisterType(const string& strKey, Any constructor)
{
if (m_creatorMap.find(strKey) != m_creatorMap.end())
throw std::invalid_argument("this key has already exist!");
//通过Any擦除了不同类型的构造器
m_creatorMap.emplace(strKey, constructor);
}
private:
unordered_map<string, Any> m_creatorMap;
};
/*test code
struct Base
{
virtual void Func(){}
virtual ~Base(){}
};
struct DerivedB : public Base
{
DerivedB(int a, double b):m_a(a),m_b(b)
{
}
void Func()override
{
cout<<m_a+m_b<<endl;
}
private:
int m_a;
double m_b;
};
struct DerivedC : public Base
{
};
struct A
{
A(Base * ptr) :m_ptr(ptr)
{
}
void Func()
{
m_ptr->Func();
}
~A()
{
if(m_ptr!=nullptr)
{
delete m_ptr;
m_ptr = nullptr;
}
}
private:
Base * m_ptr;
};
void TestIoc()
{
IocContainer ioc;
ioc.RegisterType<A, DerivedC>(“C”); //配置依赖关系
auto c = ioc.ResolveShared<A>(“C”);
ioc.RegisterType<A, DerivedB, int, double>(“C”); //注册时要注意DerivedB的参数int和double
auto b = ioc.ResolveShared<A>(“C”, 1, 2.0); //还要传入参数
b->Func();
}
*/
```

View File

@@ -0,0 +1,235 @@
### 使用c++11封装sqlite库
#### 13.1 sqlite基本用法
对于带参数的sql语句<br>
其中sqlite3_perpare_v2用于解析sql文本并且保存到sqlite_stmt对象中sqlite3_stmt将作为后面一些函数的入参sqlite3_bind_XXX用于绑定sql文本中的参数
```
#include <sqlite3.h>
#include <string>
bool test()
{
sqlite3* dbHandle = nullptr;
int result = sqlite3_open("test.db", &dbHandle);
if (result != SQLITE_OK)
{
sqlite3_close(dbHandle);
return false;
}
const char* sqlcreat = "CREATE TABLE if not exists PersonTable(ID INTEGER NOT NULL, Name Text,Address BLOB);";
result = sqlite3_exec(dbHandle, sqlcreat, nullptr, nullptr, nullptr);
//插入数据
sqlite3_stmt* stmt = NULL;
const char* sqlinsert = "INSERT INTO PersonTable(ID,Name,Adress) VALUE(?,?,?);";
//解析并且保存sql脚本
sqlite3_prepare_v2(dbHandle, sqlinsert, strlen(sqlinsert), &stmt, nullptr);
int id = 2;
const char* name = "peter";
for (int i=0;i<10;++i)
{
sqlite3_bind_int(stmt, 1, id);
sqlite3_bind_text(stmt, 2, name, strlen(name), SQLITE_TRANSIENT);
sqlite3_bind_null(stmt, 3);
if (sqlite3_step(stmt) != SQLITE_DONE)
{
sqlite3_finalize(stmt);
sqlite3_close(dbHandle);
}
//重新初始化stmt对象下次再用
sqlite3_reset(stmt);
}
//使用完需要释放,不然会内存泄露
sqlite3_finalize(stmt);
sqlite3_close(dbHandle);
return result = SQLITE_OK;
}
```
最后通过
sqlite3_colume_xxx(sqlite3_stmt*,int iCol)取得结果
```
int colCount=sqlite3_column_count(stmt);
while(true)
{
int r=sqlite3_step(stmt);
if(r==SQLITE_DONE)
{
break;//数据行都已经获取,跳出循环
}
if(r==SQLITE_ROW)
{
break;//获得某一行数据失败,跳出循环
}
//获得每一列数据
for(int i=0;i<colCount;++i)
{
int coltype=sqlite3_column_type(stmt,i);
if(coltype==SQLITE_INTEGER)
{
int val=sqlite3_column_int(stmt,i);
}else if(coltype==SQLITE_FLOAT)
{
double val=sqlite3_column_double(stmt,i);
}else if(coltype==SQLITE_TEXT)
{
const char* val=(const char*)sqlite3_column_text(stmt,i);
}else if(coltype==SQLITE_NULL)
{
}
}
}
sqlite3_finalize(stmt);
```
事务
```
sqlite3_exec(dbHandle,"BEGIN");
sqlite3_exec(dbHandle,"ROLLBACK");
sqlite3_exec(dbHandle,"COMMIT");
sqlite3_exec(dbHandle,"END");
```
```
/**
* 不带占位符。执行sql不带返回结果, 如insert,update,delete等
* @param[in] query: sql语句, 不带占位符
* @return bool, 成功返回true否则返回false
*/
bool Excecute(const string& sqlStr)
{
m_code = sqlite3_exec(m_dbHandle, sqlStr.data(), nullptr, nullptr, nullptr);
return SQLITE_OK == m_code;
}
/**
* 带占位符。执行sql不带返回结果, 如insert,update,delete等
* @param[in] query: sql语句, 可能带占位符"?"
* @param[in] args: 参数列表,用来填充占位符
* @return bool, 成功返回true否则返回false
*/
template <typename... Args>
bool Excecute(const string& sqlStr, Args && ... args)
{
if (!Prepare(sqlStr))
{
return false;
}
return ExcecuteArgs(std::forward<Args>(args)...);
}
/**
* 批量操作之前准备sql接口必须和ExcecuteBulk一起调用准备批量操作的sql可能带占位符
* @param[in] query: sql语句, 带占位符"?"
* @return bool, 成功返回true否则返回false
*/
bool Prepare(const string& sqlStr)
{
m_code = sqlite3_prepare_v2(m_dbHandle, sqlStr.data(), -1, &m_statement, nullptr);
if (m_code != SQLITE_OK)
{
return false;
}
return true;
}
/**
* 批量操作接口必须先调用Prepare接口
* @param[in] args: 参数列表
* @return bool, 成功返回true否则返回false
*/
template <typename... Args>
bool ExcecuteArgs(Args && ... args)
{
if (SQLITE_OK != detail::BindParams(m_statement, 1, std::forward<Args>(args)...))
{
return false;
}
m_code = sqlite3_step(m_statement);
sqlite3_reset(m_statement);
return m_code == SQLITE_DONE;
}
template<typename Tuple>
bool ExcecuteTuple(const string& sqlStr, Tuple&& t)
{
if (!Prepare(sqlStr))
{
return false;
}
m_code = detail::ExcecuteTuple(m_statement, detail::MakeIndexes<std::tuple_size<Tuple>::value>::type(), std::forward<Tuple>(t));
return m_code == SQLITE_DONE;
}
bool ExcecuteJson(const string& sqlStr, const char* json)
{
rapidjson::Document doc;
doc.Parse<0>(json);
if (doc.HasParseError())
{
cout << doc.GetParseError() << endl;
return false;
}
if (!Prepare(sqlStr))
{
return false;
}
return JsonTransaction(doc);
}
/**
* 执行sql返回函数执行的一个值, 执行简单的汇聚函数如select count(*), select max(*)等
* 返回结果可能有多种类型返回Value类型在外面通过get函数去取
* @param[in] query: sql语句, 可能带占位符"?"
* @param[in] args: 参数列表,用来填充占位符
* @return int: 返回结果值,失败则返回-1
*/
template < typename R = sqlite_int64, typename... Args>
R ExecuteScalar(const string& sqlStr, Args&&... args)
{
if (!Prepare(sqlStr))
return GetErrorVal<R>();
if (SQLITE_OK != detail::BindParams(m_statement, 1, std::forward<Args>(args)...))
{
return GetErrorVal<R>();
}
m_code = sqlite3_step(m_statement);
if (m_code != SQLITE_ROW)
return GetErrorVal<R>();
SqliteValue val = GetValue(m_statement, 0);
R result = val.Get<R>();// get<R>(val);
sqlite3_reset(m_statement);
return result;
}
template <typename... Args>
std::shared_ptr<rapidjson::Document> Query(const string& query, Args&&... args)
{
if (!PrepareStatement(query, std::forward<Args>(args)...))
nullptr;
auto doc = std::make_shared<rapidjson::Document>();
m_buf.Clear();
m_jsonHelper.BuildJsonObject(m_statement);
doc->Parse<0>(m_buf.GetString());
return doc;
}
```
详细代码见
https://github.com/qicosmos/SmartDB1.03/blob/master/SmartDB.hpp

View File

@@ -0,0 +1,2 @@
### 使用c++11开发一个对象的消息总线库
大致了解消息总线的设计思路,但是没有一定的模板基础,代码看了也白看

View File

@@ -0,0 +1,132 @@
### 使用c++11开发一个轻量级的并行task库
#### 15.1 TBB的基本用法
#### 15.1.1 TBB概述
TBB 是inter用标准c++写的一个开源的并行计算库,它的目的是提升数据并行计算的能力。主要功能如下:
1. 并行计算
2. 任务调度
3. 并行容器
4. 同步原语
5. 内存分配器
#### 15.1.2 TBB并行算法
1. parallel_for以并行的方式遍历一个区间
2. parallel_do和parallel_for_each将算法用于一个区间
3. parallel_reduce并行汇聚
4. parallel_pipeline并行的管道过滤器
5. parallel_sort和parallel_invoke并行排序和调和
#### 15.1.3 TBB的任务组
```
tbb::task_group g;
g.run([]{task();});
g.run([]{task();});
g.run([]{task();});
g.wait();
```
#### 15.2 PPL的基本用法
两者差异:
1. parallel_reduce的原型有些不同。
2. PPL中没有parallel_pipeline接口
3. TBB的task没有PPL的task强大PPL的task可以链式连续执行还可以组合任务而TBB的task不行。
#### 15.5 TaskCpp的任务
#### 15.5.1 task的实现
基于task的并行编程模型最基本的执行单元是task一个task就代表了一个要执行的任务。外部只需要简单调用接口就可以创建task并且执行另一个细节就是异步执行。
```
template<typename T>
class Task;
template<typename R, typename...Args>
class Task<R(Args...)>
{
std::function<R(Args...)> m_fn;
public:
typedef R return_type;
template<typename F>
auto Then(F&& f)//->Task<typename std::result_of<F(R)>::type(Args...)>
{
typedef typename std::result_of<F(R)>::type ReturnType;
auto func = std::move(m_fn);
return Task<ReturnType(Args...)>([func, &f](Args&&... args)
{
std::future<R> lastf = std::async(func, std::forward<Args>(args)...);
return std::async(f, lastf.get()).get();
});
}
Task(std::function<R(Args...)>&& f) :m_fn(std::move(f)){}
Task(std::function<R(Args...)>& f) :m_fn(f){}
~Task()
{
}
void Wait()
{
std::async(m_fn).wait();
}
template<typename... Args>
R Get(Args&&... args)
{
return std::async(m_fn, std::forward<Args>(args)...).get();
}
std::shared_future<R> Run()
{
return std::async(m_fn);
}
};
```
#### 15.5.2 task的延续
```
#include <functional>
namespace Cosmos
{
template<typename T>
class Task;
template<typename R, typename...Args>
class Task<R(Args...)>
{
std::function<R(Args...)> m_fn;
public:
typedef R return_type;
template<typename F>
auto Then(F&& f)//->Task<typename std::result_of<F(R)>::type(Args...)>
{
typedef typename std::result_of<F(R)>::type ReturnType;
auto func = std::move(m_fn);
return Task<ReturnType(Args...)>([func, &f](Args&&... args)
{
std::future<R> lastf = std::async(func, std::forward<Args>(args)...);
return std::async(f, lastf.get()).get();
});
}
Task(std::function<R(Args...)>&& f) :m_fn(std::move(f)){}
Task(std::function<R(Args...)>& f) :m_fn(f){}
~Task()
{
}
void Wait()
{
std::async(m_fn).wait();
}
template<typename... Args>
R Get(Args&&... args)
{
return std::async(m_fn, std::forward<Args>(args)...).get();
}
std::shared_future<R> Run()
{
return std::async(m_fn);
}
};
}
```

View File

@@ -0,0 +1 @@
所有类型的容器和数组都抽象为一个Range这个Range由一族迭代器组成然后就可以基于这个抽象的Range实现更为抽象、规范、统一的算法了。Boost库已经实现了Range。

View File

@@ -0,0 +1,109 @@
### 使用c++11开发一个轻量级的AOP库
AOP把软件系统分为两个部分核心关注点和横切关注点。业务处理的主要流程是核心关注点与之关系不大的部分是横切关注点。横切关注点的一个特点是他们经常发生在核心关注点的多处而各处基本相似比如权限认证、日志、事务处理
```
#pragma once
#define HAS_MEMBER(member)\
template<typename T, typename... Args>struct has_member_##member\
{\
private:\
template<typename U> static auto Check(int) -> decltype(std::declval<U>().member(std::declval<Args>()...), std::true_type()); \
template<typename U> static std::false_type Check(...);\
public:\
enum{value = std::is_same<decltype(Check<T>(0)), std::true_type>::value};\
};\
HAS_MEMBER(Foo)
HAS_MEMBER(Before)
HAS_MEMBER(After)
#include <NonCopyable.hpp>
template<typename Func, typename... Args>
struct Aspect : NonCopyable
{
Aspect(Func&& f) : m_func(std::forward<Func>(f))
{
}
template<typename T>
typename std::enable_if<has_member_Before<T, Args...>::value&&has_member_After<T, Args...>::value>::type Invoke(Args&&... args, T&& aspect)
{
aspect.Before(std::forward<Args>(args)...);//核心逻辑之前的切面逻辑
m_func(std::forward<Args>(args)...);//核心逻辑
aspect.After(std::forward<Args>(args)...);//核心逻辑之后的切面逻辑
}
template<typename T>
typename std::enable_if<has_member_Before<T, Args...>::value&&!has_member_After<T, Args...>::value>::type Invoke(Args&&... args, T&& aspect)
{
aspect.Before(std::forward<Args>(args)...);//核心逻辑之前的切面逻辑
m_func(std::forward<Args>(args)...);//核心逻辑
}
template<typename T>
typename std::enable_if<!has_member_Before<T, Args...>::value&&has_member_After<T, Args...>::value>::type Invoke(Args&&... args, T&& aspect)
{
m_func(std::forward<Args>(args)...);//核心逻辑
aspect.After(std::forward<Args>(args)...);//核心逻辑之后的切面逻辑
}
template<typename Head, typename... Tail>
void Invoke(Args&&... args, Head&&headAspect, Tail&&... tailAspect)
{
headAspect.Before(std::forward<Args>(args)...);
Invoke(std::forward<Args>(args)..., std::forward<Tail>(tailAspect)...);
headAspect.After(std::forward<Args>(args)...);
}
private:
Func m_func; //被织入的函数
};
template<typename T> using identity_t = T;
//AOP的辅助函数简化调用
template<typename... AP, typename... Args, typename Func>
void Invoke(Func&&f, Args&&... args)
{
Aspect<Func, Args...> asp(std::forward<Func>(f));
asp.Invoke(std::forward<Args>(args)..., identity_t<AP>()...);
}
/*TEST CODE
struct TimeElapsedAspect
{
void Before(int i)
{
m_lastTime = m_t.elapsed();
}
void After(int i)
{
cout <<"time elapsed: "<< m_t.elapsed() - m_lastTime << endl;
}
private:
double m_lastTime;
Timer m_t;
};
struct LoggingAspect
{
void Before(int i)
{
std::cout <<"entering"<< std::endl;
}
void After(int i)
{
std::cout <<"leaving"<< std::endl;
}
};
void foo(int a)
{
cout <<"real HT function: "<<a<< endl;
}
int main()
{
Invoke<LoggingAspect, TimeElapsedAspect>(&foo, 1); //织入方法
cout <<"-----------------------"<< endl;
Invoke<TimeElapsedAspect, LoggingAspect>(&foo, 1);
return 0;
}
*/
```

View File

@@ -0,0 +1,140 @@
## 使用c++11解决内存泄露的问题
### 4.1 shared_ptr共享的智能指针
1. 初始化<br>
优先通过make_shared来构造智能指针
```
std::shared_ptr<int> p(new int(1));
std::shared_ptr<int> p2=p;
std::shared_ptr<int> ptr;
ptr.reset(new int(1));
```
2. 获取原始指针<br>
```
std::shared_ptr<int> ptr(new int(1));
int *p=ptr.get();
```
3. 指定删除器
```
void DeleteIntPtr(int *p){
delete p;
}
std::shared_ptr<int> p(new int,DeleteIntPtr);
//当然也可以用lambda
std::shared_ptr<int> p(new int,[](int* p){delete p;});
```
#### 4.2.1 使用shared_ptr需要注意的问题
1. 不要用一个原始指针初始化多个shared_ptr
```
int* ptr=new int;
std::shared_ptr<int> p1(ptr);
std::shared_ptr<int> p2(ptr);
```
2. 不要在函数实参中创建shared_ptr
```
function(shared_ptr<int>(new int),g()) //有缺陷,不同编译器的执行顺序不一样
shared_ptr<int> p(new int());
f(p,g());
```
3. 通过shared_from_this()返回的this指针不要将指针作为shared_ptr返回出来因为this本子上是个裸指针。
4. 避免循环引用
```
struct A;
struct B;
struct A{
std::shared_ptr<B> bptr;
};
struct B{
std::shared_ptr<A> bptr;
};
void TestPtr(){
{
std::shared_ptr<A> ap(new A);
std::shared_ptr<B> bp(new B);
ap->bptr=bp;
bp->aptr=ap;
//计数不会归零,无法正确销毁
}
}
```
### 4.2 unique_ptr独占的智能指针
它不允许其他的智能指针共享内部的指针不允许通过复制将unique_ptr赋值给另一个unique_ptr<br>
但是可以通过std::move将拥有权转移
```
std::shared_ptr<T> myPtr(new T);
std::shared_ptr<T> myOtherPtr=std::move(myPtr);
```
### 4.3 weak_ptr弱引用的智能指针
弱引用指针weak_ptr是用来监视shared_ptr的生命周期的不会使引用计数加1.同时他可以解决返回this指针和解决循环引用的问题。
#### 4.3.1 weak_ptr基本用法
1. 通过use_count()方法来获得当前观测资源的引用计数
```
std::shared_ptr<int> sp(new int(10));
std::weak_ptr<int> wp(sp);
std::cout<<wp.use_count()<<std::endl;
```
2. 通过expired()来判断资源是否释放
```
std::shared_ptr<int> sp(new int(10));
std::weak_ptr<int> wp(sp);
if(wp.expired())
{
std::cout<<"weak_ptr无效,资源释放”<<std::endl;
}else{
std::cout<<"weak_ptr有效"<<std::endl;
}
```
3. 通过lock方法获取所监视的shared_ptr
#### 4.3.2 weak_ptr返回this指针
通过派生std::enable_shared_from_this类并通过shader_from_this来返回智能指针
#### 4.3.3 weak_ptr解决循环引用问题
```
struct A;
struct B;
struct A{
std::shared_ptr<B> bptr;
};
struct B{
std::weak_ptr<A> bptr;
//改成weak_ptr就能正常销毁了
};
void TestPtr(){
{
std::shared_ptr<A> ap(new A);
std::shared_ptr<B> bp(new B);
ap->bptr=bp;
bp->aptr=ap;
}
}
```
### 4.4通过智能指针管理第三方库分配的内存
```
void* p=GetHandle()->Create();
std::shared_ptr<void> sp(p,[this](void*p){GetHandle()->Release(p);});
```
包装公共函数
```
std::shared_ptr<void> Guard(void* p)
{
return std::shared_ptr<void> sp(p,[this](void *p){GetHandle()->Release(p);});
}
```
但是不够安全
```
void *p=GetHandle()->Create();
Guard(p);
//Guard(p);是个右值如果没有被赋予给auto指针这句结束就会释放从而导致p提前释放
```
可以使用宏来解决
```
#define GUARD(p) std::shared_ptr<void> p##p(p,
void *p=GetHandle()->Create();
Guard(p);
```

View File

@@ -0,0 +1,28 @@
#### 继承构造函数
```
struct A{
void f(double i){}
A(){}
A(int i){}
A(float f){}
};
struct B:A{
using A:A;
using A:f;
void f(double i){}
}
int main(){
B b;//会调用A的对应构造函数
b.f;//会调用A的函数
}
```
不过使用了继承构造函数,就无法通过构造函数初始化派生类成员了。
#### 用户自定义字面量
可以定义出2.0f或者123_w之类的写法表达瓦特与浮点

View File

@@ -0,0 +1,96 @@
#### 2.1.2 __func__
__func__ 宏:返回函数名称 c++11兼容c99标准
#### 2.1.4 变长参数宏以及__VA_ARGS__
```
#define LOG(...){\
fprintf(stderr,"%s: Line %d:\t",__FINE__,__LINE__);\
fprintf(stderr,__VA_ARGS__);\
fprintf(stderr,"\n");\
}
int main(){
int x=3;
LOG("x=%d",x);//2-1-5.cpp: line 12: x=3
}
```
#### 2.4 宏__cplusplus
可以用于判断是否是c++编译环境同时也可以判断c++版本防止c++11程序在c++98编译器下编译
#### 2.5.1
assert宏定义在cassert头文件中
```
assert(expr);
```
首先对expr求值如果表达式为假assert输出信息并且终止程序运行。为真则什么也不做。
在开头使用需要写在cassert头文件之前因为本质上还是宏判断
```
#define NDEBUG
```
可以跳过检测。在VC++里面release会在全局定义NDEBUG。<br>
既然定义了NDEBUG还可以
```
#ifndef
//do any debug thing
//__FILE__ 文件名字面值
//__LINE__ 当前行号字面值
//__TIME__ 文件编译时间字面值
//__DATE__ 文件编译时间字面值
//__func__ 函数名称 c++11兼容c99标准
//用于打印debug信息
#endif
```
## error用法
```
#ifndef UNIX
#error This software requires the UNIX OS.
#endif
#if !defined(__cplusplus)
#error C++ compiler required.
#endif
```
#### 2.5.2 静态断言与static_assert
编译期断言
static_assert(false,"error string");
#### 2.6 noexcept修饰符和noexcept操作符
```
void excpt_func() noexcept;//虽然这样函数就不会抛出异常,但是如果抛出了异常,程序会直接被终止
void excpt_func() noexcept(expr);//如果为true则不会抛出异常
```
#### 2.9 扩展的friend语法
```
class A{
};
class B{
friend class A;//都通过
};
class C{
friend B;//c++11通过c++98失败
};
```
可以省略class直接声明类的友元这个改动的意义在于类模板,不过使用内置对象时就会实例化成一个没有友元的类
```
class A;
template <class T>
class People{
friend T;
};
```
#### 模板函数的默认模板参数
```
template<typename T=int>
void func(){
}
```
#### 2.12.1 外部模板
在头文件中定义模板类的情况下为了防止编译器给每个包含头文件的cpp文件生成代码可以添加extern关键字。当然至少要有一个是不加extern的。
```
template <typename T> void func(T){
}
extern template void func<int>(int);
```

View File

@@ -0,0 +1,11 @@
#### 5.1.3 强类型枚举
```
enum class Type {a,b,c,d};
```
优势:
1. 强作用域
2. 转换限制(禁止隐式转换)
3. 可以指定底层类型
```
enum class Type : char {a,b,c,d};//指定了类型
```

View File

@@ -0,0 +1,2 @@
# UE知识库
- https://ue5wiki.com/wiki

View File

@@ -0,0 +1,21 @@
# 模型资源
- https://sketchfab.com/
- https://www.turbosquid.com/
- https://www.cgtrader.com/
- https://kitbash3d.com/products
- https://evermotion.org/shop
- (大量写实与建筑资源需购买但taobao)
# 贴图资源
- textures.com
- poliigon.com
- 3dtextures.me (免费)
## HDRI
- HDRI Haven:https://polyhaven.com/hdris
- HDRMaps:https://hdrmaps.com/
- HDRI Haven: https://hdri-haven.com/
# 音效资源
- https://www.purple-planet.com/cinematic
- https://taira-komori.jpn.org/freesoundcn.html