[{"data":1,"prerenderedAt":2110},["ShallowReactive",2],{"page-UE4-5":3,"page-count-UE4":2109},[4,190,1035,1547,1724],{"id":5,"title":6,"body":7,"date":166,"description":13,"extension":167,"meta":168,"navigation":171,"path":183,"seo":184,"stem":185,"tags":186,"__hash__":189},"blogs\u002F_legacy\u002F2018\u002F2018-05-09-ue4-live2d-plugin.md","UE4的Live2D插件",{"type":8,"value":9,"toc":154},"minimark",[10,14,17,20,23,26,31,42,45,48,51,55,58,61,64,67,72,75,88,91,95,104,119,122,131,134,137,140],[11,12,13],"p",{},"Live2D这个技术在公布之后在各种游戏中有使用的痕迹，不过在提供动态的同时还是损失了些许立绘的精细感。",[11,15,16],{},"当前UE4版本为4.19.1。",[11,18,19],{},"Live2D在Unity上是有提供插件的，在近期Cubism的SDK进行结构更新之后，其在其他类型的项目中的接入就变得更加轻松了。",[11,21,22],{},"因此便动了接入UE4插件的念头，不想工作量比想象中的稍微大了一些，再加上最近可用的业余时间变少了，打算将所有的时间集中到一个方面，便只能将这个插件以WIP的形式先放出来了。",[11,24,25],{},"由于短时间内在项目内可能不会用到Live2D，可能更新频率会很低呢~",[27,28,30],"h2",{"id":29},"cubism-sdk","Cubism SDK",[11,32,33,34,41],{},"目前可以在项目中使用的Live2D的SDK可以从[",[35,36,40],"a",{"href":37,"rel":38},"https:\u002F\u002Flive2d.github.io\u002F#native",[39],"nofollow","这里","]下载，由于Cubism最近又出了一款产品，所以其产品主页上的说明有些没有理解。",[11,43,44],{},"由于本身没有进行过Live2D的制作，也没有购买过官方的License，所以在接入测试中使用的是官方测试用的Mark的Live2D模型。",[11,46,47],{},"Live2D的SDK在变更之前，模型动画的渲染和更新是混合在一起的，虽然可以很方便的直接取到渲染结果贴图。",[11,49,50],{},"但是由于渲染的路径是SDK自己控制的，导致在集成到UE4这样的引擎中会有很多心理和时间上的额外成本。",[27,52,54],{"id":53},"ue4的接入","UE4的接入",[11,56,57],{},"变更后的SDK的核心部分只负责对模型进行更新，所以在接入时只需要通过接口函数来进行更新和结果获取就可以了。",[11,59,60],{},"在实现上其实并没有什么太多可以解释的地方，基本上参照官方文档进行一一对接就可以了。",[11,62,63],{},"目前已经实现了Live2D的moc3模型的导入和使用，在使用上模型的渲染Object会有一个RenderTarget，直接将其用于材质或者UI就可以了。",[11,65,66],{},"在渲染时由于SDK那边推过来的时候是大量的Vertex和Index的数据，所以使用了UCanvasRenderTarget。这个类本身是会对Texture进行Batch操作的，但是有一个问题，就是Live2D的Mask类型的渲染方式并不被UCanvaRenderTarget的DrawTriangle所支持，所以必须使用额外的材质来进行Blend的操作。",[68,69,71],"h3",{"id":70},"todo","TODO",[11,73,74],{},"目前剩下的工作主要有三个方面：",[76,77,78,82,85],"ul",{},[79,80,81],"li",{},"没有将模型操作的接口接入，要使用的话必须调用SDK的原生接口",[79,83,84],{},"寻找更高效的渲染方式",[79,86,87],{},"另外就是编辑器的支持，现在打开导入的资源的话，编辑界面是无法使用的，由于需要处理的细节部分比较多而且也没有详细的研究过需要在编辑器中进行控制的项目，所以没有填入内容。不过并不会影响在项目中使用这个资源~",[11,89,90],{},"总体上而言只完成了核心的部分，如果需要在项目中使用的话还需要自己进行一点操作。",[68,92,94],{"id":93},"sample","Sample",[11,96,97,98,103],{},"由于是WIP，所以使用起来会有些繁琐，请直接看[",[35,99,102],{"href":100,"rel":101},"https:\u002F\u002Fpan.baidu.com\u002Fs\u002F1P1DXeIXRmw2czRlpIzt60Q",[39],"Sample项目","(y8a2)]。下面是详细的说明：",[105,106,107,110,113,116],"blockquote",{},[11,108,109],{},"使用上只需要使用UCubismBpLib::CreateCubism就可以了，对于取得之后的Live2D渲染器，必须为其指定贴图。",[11,111,112],{},"由于不是很清楚Live2D实际在进行模型输出时贴图是如何进行规范命名的，所以这里需要进行一次额外的导入。",[11,114,115],{},"如果Texture的命名必然和模型对应的话，之后可以考虑直接在导入时进行引入。",[11,117,118],{},"在蓝图或者代码中完成构建和贴图指定之后，就可以通过Get_RenderTarget来获取渲染的结果的RenderTarget了。",[68,120,121],{"id":121},"链接",[11,123,124,125,130],{},"插件本体的连接在[",[35,126,129],{"href":127,"rel":128},"https:\u002F\u002Fgithub.com\u002FArisego\u002Flivv.git",[39],"GitHub","]上~",[11,132,133],{},"说到ACG相关的插件，有的童鞋可能知道，其实之前有在做MMD的导入插件，但是后面有两个问题：UE4目前不支持在动画序列中原生的IK动画；要在UE4中实现MMD的一些特殊骨骼绑定例如QDEF之类的需要额外的工作量。所以虽然基本的导入和动画功能都实现了，但是这两个问题导致实质上没有办法推进了，目前是停止的状态呢。",[27,135,136],{"id":136},"更新",[11,138,139],{},"由于两次更新尝试之间的时间间隔相差太大，所以对插件进行了整个的重写。现在的渲染效率提高了～",[11,141,142,143,148,149,153],{},"[",[35,144,147],{"href":145,"rel":146},"https:\u002F\u002Fgithub.com\u002FArisego\u002FUnrealLive2D",[39],"插件地址","][",[35,150,152],{"href":151},"\u002F2019-12-30-ue4-live2d-plugin-rewrite\u002F","文章更新","]",{"title":155,"searchDepth":156,"depth":157,"links":158},"",2,3,[159,160,165],{"id":29,"depth":156,"text":30},{"id":53,"depth":156,"text":54,"children":161},[162,163,164],{"id":70,"depth":157,"text":71},{"id":93,"depth":157,"text":94},{"id":121,"depth":157,"text":121},{"id":136,"depth":156,"text":136},"2018-05-09","md",{"layout":169,"status":170,"published":171,"author":172,"author_login":174,"author_email":175,"wordpress_id":176,"wordpress_url":177,"date_gmt":178,"excerpt":179},"post","publish",true,{"display_name":173,"login":174,"email":175,"url":155},"风铃","flinkor","flinkor@foxmail.com",2345,"\u002F?p=2345","2018-05-09 13:51:31 +0000",{"type":8,"value":180},[181],[11,182,13],{},"\u002F2018-05-09-ue4-live2d-plugin",{"title":6,"description":13},"_legacy\u002F2018\u002F2018-05-09-ue4-live2d-plugin",[187,188],"UE4","Live2D","SUN0GOghLNTCDMw9K4ziI14eOBF_JtCPKXpNCmi45Yc",{"id":191,"title":192,"body":193,"date":1020,"description":197,"extension":167,"meta":1021,"navigation":171,"path":1030,"seo":1031,"stem":1032,"tags":1033,"__hash__":1034},"blogs\u002F_legacy\u002F2018\u002F2018-04-08-gdc2018-fortnite-note.md","GDC2018 Fortnite优化演讲笔记",{"type":8,"value":194,"toc":976},[195,198,201,204,218,221,224,231,234,238,242,245,248,251,254,257,260,263,266,271,274,285,288,291,294,304,307,313,316,342,345,349,352,355,358,363,366,369,372,377,380,383,386,389,392,395,398,407,416,419,422,426,429,432,435,438,443,446,451,454,457,461,464,467,470,473,476,479,482,485,489,492,495,498,503,506,509,512,515,518,521,524,527,530,533,539,542,545,548,551,554,560,563,566,569,572,575,578,581,584,587,590,593,596,599,602,606,609,618,623,626,629,634,637,640,644,647,650,653,656,661,664,672,676,679,682,686,689,692,695,698,701,705,708,711,719,722,725,729,732,735,738,741,744,747,750,754,757,760,763,767,770,773,776,779,782,785,788,791,794,798,801,804,807,821,824,827,830,833,836,839,842,845,848,853,856,859,864,867,871,875,878,892,895,899,902,905,908,911,914,917,920,923,927,930,933,936,939,943,946,949,952,955,958,961,964,967,970,973],[11,196,197],{},"Epic官方在GDC2018的Fortnite的性能优化的演讲介绍了在Fortnite中使用的优化技术，感觉受益匪浅，在这里记录下来。",[11,199,200],{},"当前UE4版本为4.19。",[11,202,203],{},"Fortnite是针对所有的平台的，可以让用户在PC、主机、手机上进行游戏并同时联机，这对内容制作和优化都是一个很大的考验。",[11,205,206,207,212,213,217],{},"Fortnite使用[",[35,208,211],{"href":209,"rel":210},"https:\u002F\u002Fdocs.unrealengine.com\u002Fen-us\u002FPlatforms\u002FDeviceProfiles",[39],"Device Profiles","]进行[",[35,214,216],{"href":209,"rel":215},[39],"Scalability","]的调整，对TAAU的分辨率、阴影质量、Foliage、材质进行按目标平台的对应和性能调节。",[11,219,220],{},"而Mobile的优化经验可以被用到低端平台和60FPS模式上。60FPS模式是针对PS4 Pro和XBox1X提供的一个可选模式，除了常规的选项下降外。在60FPS模式下，关掉了DFAO，动态阴影的CSM变成了最低的一级，使用的也是更简化版本的RTDF阴影。",[11,222,223],{},"下面是官方给出的Mobile下的性能表，其中灰色的部分是原本在PC端开启的特性。",[11,225,226],{},[227,228],"img",{"alt":229,"src":230},"clip_image001","\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Fclip_image001_thumb.png",[11,232,233],{},"在进行优化时，以60FPS目标，然后在低平台上降到30FPS可以预留更多的运算空间，防止机器过热。",[27,235,237],{"id":236},"cpu","CPU",[68,239,241],{"id":240},"significance-manager","Significance Manager",[11,243,244],{},"对所有的玩家进行的优先级管理系统，会影响后面的许多可延展性的系统的运行。",[11,246,247],{},"根据其他玩家与当前玩家的位置、可见性、在屏幕上的尺寸来决定他们的优先级，所有的玩家根据高低被分到4个不同的优先等级中。",[11,249,250],{},"平台的不同，各个分组的大小也不同。对于移动平台，由于可使用的资源是最少的，所以只有玩家自身使用最高的优先级进行更新。",[68,252,253],{"id":253},"骨骼动画",[11,255,256],{},"采用模块化的动画策略，每个角色由一个基础骨骼驱动头部、身体、背部装饰、武器四个部分。",[11,258,259],{},"五个模块分别使用不同的动画蓝图进行驱动，动画由基础骨骼拷贝到被驱动的骨骼上去。",[11,261,262],{},"采用角色的模块化的目的是为了提高复用率，减少动画模型等对内存的占用。",[11,264,265],{},"但是这样的方案会带来动画蓝图负担变重的副作用。",[267,268,270],"h4",{"id":269},"异步执行eval","异步执行Eval",[11,272,273],{},"动画系统执行上，大体分为三个部分：",[105,275,276,279,282],{},[11,277,278],{},"Update - 动画蓝图从游戏逻辑中收集状态变量并更新骨骼位置",[11,280,281],{},"Evaluate - 根据骨骼位置对动画进行解压和混合",[11,283,284],{},"Complete - 将运算后的顶点数据推送到渲染现场，更新物体位置和动画通知",[11,286,287],{},"在实际运行上，使用Profiling工具来查看Evaluate步骤是否是在Worker线程上执行的。",[11,289,290],{},"另外Update的过程也是可以异步的，在UAnimInstance::NeedsImmediateUpdate中能看到异步的条件，如果启用了Root Motion的话，就会导致异步的Update无法成立。",[11,292,293],{},"在AnimInstance.cpp中能够看到：",[295,296,301],"pre",{"className":297,"code":299,"language":300},[298],"language-text","if(bNeedsValidRootMotion || NeedsImmediateUpdate(DeltaSeconds))\n{\n  \u002F\u002F cant use parallel update, so just do the work here\n  Proxy.UpdateAnimation();\n  PostUpdateAnimation();\n}\n","text",[302,303,299],"code",{"__ignoreMap":155},[11,305,306],{},"动画的异步更新可以在项目设置中找到选项",[11,308,309],{},[227,310],{"alt":311,"src":312},"image","\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Fimage_thumb.png",[11,314,315],{},"另外，Console中有如下的指令开关：",[105,317,318,321,324,327,330,333,336,339],{},[11,319,320],{},"a.ForceParallelAnimUpdate",[11,322,323],{},"开启时会无视项目设定和动画蓝图设置在WorkerTrhead中进行Update",[11,325,326],{},"a.ParallelAnimEvaluation",[11,328,329],{},"打开时会在WorkerThread中执行Evaluatioin",[11,331,332],{},"a.ParallelAnimUpdate",[11,334,335],{},"打开时animation blend tree, native update, asset players和montages会在WorkerThread中异步执行",[11,337,338],{},"a.ParallelBlendPhysics",[11,340,341],{},"打开后physics blending会在WorkerThread中执行",[11,343,344],{},"由于没有实际使用过，不知道会有什么注意事项。",[267,346,348],{"id":347},"nativization","Nativization",[11,350,351],{},"由于动画蓝图的运算量比较大，在蓝图虚拟机中会有额外的运行成本。所以要尽可能的将复杂的逻辑放到C++中进行。",[11,353,354],{},"执行逻辑转移之后可以观察到动画的时间成本从0.93ms降低到0.19ms。",[11,356,357],{},"同时，在动画蓝图中要尽量的保证Fast Path的使用，但满足条件时就能看到节点上有标志",[11,359,360],{},[227,361],{"alt":311,"src":362},"\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Fimage_thumb-1.png",[11,364,365],{},"需要注意不要再变量连接到节点的过程中在蓝图中执行不被Fast Path支持的变换操作。",[267,367,368],{"id":368},"杂项",[11,370,371],{},"应用优先级管理非常重要，首先",[11,373,374],{},[227,375],{"alt":311,"src":376},"\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Fimage_thumb-2.png",[11,378,379],{},"可以将Skeletal Mesh的更新标志置为只在物体被渲染时才执行Tick和骨骼位置更新。",[11,381,382],{},"同时根据角色的优先级，关闭非关键性的如武器的动画之类的效果。",[11,384,385],{},"Fortnite在移动平台上，除了自身外其他角色的武器动画都是关闭的。",[11,387,388],{},"使用URO可以在需要时对动画蓝图本身的更新频率进行降低，通过与前面的优先级管理系统配合可以有效的降低低优先级角色的动画成本。但是要注意不要关闭插值，插值对动画的效果影响比较大。",[11,390,391],{},"另外RigidBody在使用上可以用于代替AnimDynamic，其拥有更高的效率和更多的功能。",[11,393,394],{},"似乎还有将在地面上的Skeleton Mesh作为Static Mesh渲染的开关，但是在当前版本没有看到呢。",[11,396,397],{},"由于Fortnite的动画有的移动比较大，所以没有使用Use Fixed Bounds。而几个Skeleton Mesh的组件则是使用的Use Parent Bounds来继承基础骨骼的Bounds。",[11,399,400,401,406],{},"动画蓝图中的Trace在Fortinite中也是采用的AsyncTrace以降低GameThread的时间，而动画通知功能也尽量的转移到了C++中，使用[",[35,402,405],{"href":403,"rel":404},"https:\u002F\u002Fdocs.unrealengine.com\u002Fen-us\u002FEngine\u002FAnimation\u002FAnimModifiers",[39],"AimModifier","]以快速的对蓝图中的动画通知进行替换和管理。",[11,408,409,410,415],{},"关于动画系统的优化的更多内容，可以参考官方的[",[35,411,414],{"href":412,"rel":413},"http:\u002F\u002Fapi.unrealengine.com\u002FINT\u002FEngine\u002FAnimation\u002FOptimization\u002Findex.html",[39],"动画优化指南","]。",[68,417,418],{"id":418},"物体管理",[11,420,421],{},"对于要跨平台的需求，物品的管理本身也是需要优化的。",[267,423,425],{"id":424},"spawn","Spawn",[11,427,428],{},"在物品的Spawn上，C++的组件会比蓝图的组件更快些。",[11,430,431],{},"实际进行生成时，先对Component进行配置再进行注册会更高效一些。",[11,433,434],{},"另外，材质实例的参数拷贝过程也被进行过优化。这方面的优化会被提交到引擎的版本中，像是后面会提到的粒子Component的Pooling功能也同样是如此。",[11,436,437],{},"对于粒子和声音组件，可以打开Auto Manage AttachMent，让其只在作用时才Atatch到Actor上。",[11,439,440],{},[227,441],{"alt":311,"src":442},"\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Fimage_thumb-3.png",[11,444,445],{},"对于Overlap，新添加了计数管理的功能：",[11,447,448],{},[227,449],{"alt":311,"src":450},"\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Fimage_thumb-4.png",[11,452,453],{},"防止在没有必要的情况下对物体的遍历查询。",[11,455,456],{},"而Generate Overlaps这个选项是默认打开的，也会在不必要的地方产生性能消耗，可以选择将其关掉。",[267,458,460],{"id":459},"character-movement","Character Movement",[11,462,463],{},"角色移动在联网游戏上会有Predict系统，但是在实际使用过程中，尤其是对于移动平台而言。会支付很高的运算成本。",[11,465,466],{},"因此，对于优先度不高的角色会采用插值同步的方式来进行角色的移动，而不会在本地端进行Predict。",[11,468,469],{},"碰撞上，对于开启了per-poly complex collision的物体，为了降低内存消耗，实质上是开启了LOD的。",[267,471,472],{"id":472},"物体",[11,474,475],{},"对于场景中的物体，应该考虑到尽量的减少面数以及降低材质本身的复杂度。",[11,477,478],{},"要在DrawCall的数量与内存占用之间选择一个好的平衡点。",[11,480,481],{},"可以使用ObjList和ListTextures来对当前场景中各个物体和贴图的内存占用进行追踪。",[11,483,484],{},"对于角色模型就是如此，在去掉不同的Section以及组合必要的Section之后，就变成了4个部分的设计。",[267,486,488],{"id":487},"lod","LOD",[11,490,491],{},"将会有新的用于定义LOD的Asset，对于角色模型，采用的策略是。",[11,493,494],{},"在LOD1上移除了脸部动画，在LOD2上移除了手指和Twist骨骼。",[11,496,497],{},"角色的总体面数，LOD0为30000，LOD1为15000，LOD2为3000，LOD3为300。",[11,499,500],{},[227,501],{"alt":311,"src":502},"\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Fimage_thumb-5.png",[11,504,505],{},"对于长度很长的细节动画进行了优化，添加上一些额外的随机性。",[11,507,508],{},"对于HLOD的Proxy物体，由于会有额外的内存成本，需要在DC和内存之间进行平衡。",[11,510,511],{},"移动平台的LOD是锁定在LOD1的， 所以可以将一些装饰性物品放到LOD上。",[11,513,514],{},"环境物体拥有3~4级的LOD，对于屏幕尺寸很小的LOD可以用相当低的设定。",[11,516,517],{},"将场景内的物体蓝图化以方便进行调节。",[267,519,520],{"id":520},"粒子",[11,522,523],{},"在对粒子进行制作的时候，不能有morph target的mesh，因为他们不支持LOD。",[11,525,526],{},"当资源紧张时应当根据优先级管理将较低的粒子特效关掉。",[11,528,529],{},"在移动环境下，一些没有游戏作用的环境特效可以关闭掉。",[11,531,532],{},"对于使用了Translucent光照的粒子，应当将其光照模型切换到",[11,534,535],{},[227,536],{"alt":537,"src":538},"clip_image0014","\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Fclip_image0014_thumb.png",[11,540,541],{},"或者Volumetric Directional。",[11,543,544],{},"对于功能类似的材质应当进行合并，降低内存消耗。",[267,546,547],{"id":547},"组合",[11,549,550],{},"对于由多个部分组成的物体，必须对其进行必要的合并和精简。",[11,552,553],{},"将可以合并的特效进行合并，并对消耗过大的特效尝试进行更简单的实现。",[11,555,556],{},[227,557],{"alt":558,"src":559},"clip_image0016","\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Fclip_image0016_thumb.png",[11,561,562],{},"例如上面的武器特效，原本是有粒子效果的，后面被合并到了半透明的Grid上，而原本通过物体Duplicate来实现的Outline也被放到了PixelShader中。",[11,564,565],{},"在Mobile上，材质使用了Fully Rough。",[267,567,568],{"id":568},"草",[11,570,571],{},"Fortnite的草经过了不少的优化，由于一开始的草的被踩之类的特效是通过Vertex shader使用Rotation来实现的，而这在数学上的成本非常的高。",[11,573,574],{},"后面被改造成了使用Translation的，在移动平台上则完全关闭了这个Vertex Shader。",[11,576,577],{},"同时，积极的对草使用Density Scale来降低性能消耗。",[267,579,580],{"id":580},"玩家构造物体",[11,582,583],{},"对于玩家在游戏过程中构建的物体，会有采用不同策略的LOD进行控制。",[11,585,586],{},"在建造的过程中使用的是基于Vertex的特效，而在Mobile上则采用了Masked的材质类进行实现。",[11,588,589],{},"在建造完成之后会切换到性能更低的完成的材质。",[267,591,592],{"id":592},"风暴云",[11,594,595],{},"Storm Cloud在PC平台和移动平台上采用的是不一样的设定。",[11,597,598],{},"在PC上使用了Tessellated的平面来进行LOD和Cull。",[11,600,601],{},"在移动平台上则是一个甜甜圈为基础的材质特效。",[267,603,605],{"id":604},"imposter","Imposter",[11,607,608],{},"Fortnite采用了Imposter作为树的低层LOD。",[11,610,611,612,617],{},"这种技术还是非常有效的，官方有很详细的文档对[",[35,613,616],{"href":614,"rel":615},"https:\u002F\u002Fdocs.unrealengine.com\u002Fen-us\u002FEngine\u002FContent\u002FTools\u002FRenderToTextureTools\u002F3",[39],"3d imposter","]进行描述。",[11,619,620],{},[227,621],{"alt":311,"src":622},"\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Fimage_thumb-6.png",[11,624,625],{},"另外，针对玩家在下落阶段会看到的Imposter的阴影，Fortnite采用了blob shadow来进行。",[11,627,628],{},"将4个方向的阴影分别烘焙到贴图的RGBA空间中，",[11,630,631],{},[227,632],{"alt":311,"src":633},"\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Fimage_thumb-7.png",[11,635,636],{},"然后根据日照的方向，对阴影进行偏移和取样，最终实现了相当不错的Fake Shadow。",[27,638,639],{"id":639},"游戏逻辑",[267,641,643],{"id":642},"ui","UI",[11,645,646],{},"关于UMG的使用，官方是有优化指南的。",[11,648,649],{},"例如使用Invalidation来减少CPU成本、使用RetainerBox来减少渲染成本，以及将复杂的逻辑移动到C++中去。",[11,651,652],{},"而动态绑定功能应当尽量避免使用，因为它不仅有可能破坏缓存机制更有可能在不小心的时候造成相当高的性能消耗。",[11,654,655],{},"而对于Image的Batch功能，只有在使用的是同一个Z-Order并打开",[11,657,658],{},[227,659],{"alt":311,"src":660},"\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Fimage_thumb-8.png",[11,662,663],{},"才可以开启Batch。",[11,665,666,667,415],{},"UMG的相关优化可以参照官方的[",[35,668,671],{"href":669,"rel":670},"https:\u002F\u002Fdocs.unrealengine.com\u002Fen-US\u002FEngine\u002FUMG\u002FUserGuide\u002FBestPractices",[39],"UMG优化指南",[267,673,675],{"id":674},"tick","Tick",[11,677,678],{},"逐帧逻辑中的性能消耗可以使用Dumpticks来进行详细的调试和查看。",[11,680,681],{},"在性能优化中，有一个关于MaterialParameterCollection的变更会在4.20中到来。主要的优化处是，在每一帧只会对参数集合进行一次更新，而不是在每次被调用时对被影响到的材质进行更新。",[267,683,685],{"id":684},"audio","Audio",[11,687,688],{},"声音对性能的消耗更多的表现在内存占用上。",[11,690,691],{},"根据平台性能的不同，对播放声音的数量进行了限制，PC平台上是32个，Android为12个，IOS为16个。",[11,693,694],{},"在一些性能较低的平台上，会关闭声音的Reverb和EQ效果。",[11,696,697],{},"同时，在性能越低的平台上，会降低声音的变化效果。在最低等级的移动平台上，是不启用随机变化的特效。",[11,699,700],{},"在4.20之后，将会提供按平台在cook时降低压缩的功能，方便针对不同的平台进行不同的压缩策略。",[267,702,704],{"id":703},"texture","Texture",[11,706,707],{},"贴图最大的问题自然会表现在Streaming上。",[11,709,710],{},"至于r.TextureStreaming，不可以随便关掉，因为会导致所有的mip全部被载入到内存中，即便根本就没有使用到。",[11,712,713,714,415],{},"这个方面可以参考[",[35,715,718],{"href":716,"rel":717},"https:\u002F\u002Fdocs.unrealengine.com\u002Fen-us\u002FEngine\u002FContent\u002FTypes\u002FTextures\u002FStreaming\u002FConfig",[39],"官方文档",[11,720,721],{},"TextureStreaming上，Fortnite似乎将其载入过程从game thread转移到了async task中。",[11,723,724],{},"贴图的大小在制作时也同样是尽可能的合并类似贴图以及尝试减小大的贴图的尺寸，针对不同的平台的特性，对法线贴图和Colormap进行优化。",[267,726,728],{"id":727},"level-streaming","Level Streaming",[11,730,731],{},"使用关卡流可以很好的降低性能的消耗，为其他的特性预留空间。",[11,733,734],{},"不仅能减少内存使用还可以削减DrawCall。",[11,736,737],{},"关卡流在使用时，文件的读取由Worker Thread负责，从FArchive进行Deserialize的过程中新版本中也可以放到AsyncLoadingThread。只有最后的PostLoad过程被分散到了GameThread的每一帧中。",[11,739,740],{},"这种分散，在空中的时候由于Stream的更新较快为5ms，在地面上移动时，由于实际的Stream需求变低，为每一帧3ms。",[11,742,743],{},"这个是针对60fps设计的值，如果在30fps模式的话必须乘以二。",[11,745,746],{},"为了保证关卡流的流畅性，会一直尝试保持IO的进行。在本区域载入完成后，会对邻接的区域进行Streaming。",[11,748,749],{},"在4.20中还将会有一个用于在编辑器中进行InstancedMesh合并的工具，虽然Fortnite本身最终并没有使用这个方案。",[267,751,753],{"id":752},"culling","Culling",[11,755,756],{},"一般的基于ViewDistance的裁剪，在设计上为了保证在各个平台的游戏性的一致性。",[11,758,759],{},"使用最低的平台的ViewDistance为1，在更高的平台上则放大这个值。",[11,761,762],{},"对于在游戏中无法躲藏玩家的物体，会更加积极的进行裁剪操作。",[267,764,766],{"id":765},"dhlod","DHLOD",[11,768,769],{},"由于游戏中本身提供了可破坏的建筑物。",[11,771,772],{},"所以不能够简单的使用HLOD进行显示控制，所以专门构建了一个DHLOD的系统。",[11,774,775],{},"为每一个可破坏的建筑块准备了对应的LOD低模，然后使用一张贴图来标识对应的块有没有破坏掉。",[11,777,778],{},"这样就组成了一个DHLOD，让它位于真正的Destructable的建筑和Proxy HLOD之间，保证在使用LOD的情况下也能处理建筑物被破坏的情况。使得玩家可以对远处建筑物内的状况进行把握。",[267,780,781],{"id":781},"线程策略",[11,783,784],{},"针对不同的平台的核心数和架构，采用了不同的线程策略。",[11,786,787],{},"对于低性能的双核IOS，除了经典的game\u002Frender thread之外，额外开出一个主要用于streaming的Async task thread。",[11,789,790],{},"对于高性能的IOS会额外开出3个task thread，主要用于前面提到的那些异步任务，包括：动画的Evaluation、粒子模拟、物理任务和查询、Texture Streaming和Scene culling。",[11,792,793],{},"Android平台上对性能优化最大的是用于提交OpenGl指令的RHI Thread。",[267,795,797],{"id":796},"draw-call","Draw Call",[11,799,800],{},"CPU的性能上的瓶颈之一是Draw Call，在场景中需要进行culling和relevance虽然会对性能造成消耗，但是在采用Level Streaming后这方面的消耗就降低了很多。",[11,802,803],{},"Fortnite在PC上有超过4500个DC，在主机端有3500多个DC，但是在移动端，这样的GC数量是无法承受的。",[11,805,806],{},"进行的优化包括：",[76,808,809,812,815,818],{},[79,810,811],{},"积极的进行距离裁剪，一旦物体玩家无法察觉或者不会影响游戏进行就裁剪",[79,813,814],{},"删除与游戏无关的装饰物品",[79,816,817],{},"使用遮蔽裁剪删除掉不可见的物体",[79,819,820],{},"使用HLOD合并DC",[11,822,823],{},"遮蔽裁剪使用的都是硬件裁剪，而HLOD对于有很长的可视距离的情况非常有效。",[11,825,826],{},"这里官方说的是全部使用硬件裁剪，不知道Target的目标设备是什么样的。UE4本身对于没有硬件剪裁的情况可以采用HZB裁剪，也可以预先进行Precomputed visibility volumes进行预先的计算。",[267,828,829],{"id":829},"联机性能",[11,831,832],{},"Fortnite由于同时支持100个玩家，实质上需要同步的物体和关卡流的管理对性能的占用是非常大的。",[11,834,835],{},"Dedicated Server",[11,837,838],{},"在DS端，使用ForceNetUpdate()进行配合降低了服务器的更新频率到20Hz左右。",[11,840,841],{},"同时，应用了前面提到的优先级管理系统，通过自行的列表维护对物品的同步进行维护。并没有使用UE4自带的IsNetRelevantFor()和GetNetPriority()体系。",[11,843,844],{},"而属性同步也由原本的分散发布改为了由一个新系统来控制保证尽可能的缓存和重用。",[11,846,847],{},"服务器上与服务器无关的逻辑全部都会被关闭掉，例如动画模拟以及一些装饰类的物品。用于角色碰撞逻辑的碰撞形态也是另外为服务端准备的：",[11,849,850],{},[227,851],{"alt":311,"src":852},"\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Fimage_thumb-9.png",[11,854,855],{},"Client",[11,857,858],{},"客户端这边也会降低RPC的发送频率，对于除了关键性的移动之外的数据，也会将发送频率降到20Hz。",[11,860,861],{},[227,862],{"alt":311,"src":863},"\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Fimage_thumb-10.png",[11,865,866],{},"上面的这张图可以比较直观的表现发送频率降低的过程。",[27,868,870],{"id":869},"gpu优化","GPU优化",[267,872,874],{"id":873},"temporal-upsampling","Temporal Upsampling",[11,876,877],{},"这个是4.19更新的新的引擎特性，能够动态的根据系统性能对渲染分辨率进行调整。",[11,879,880,881,886,887,415],{},"详细的原理和提升可以参考官方文档[",[35,882,885],{"href":883,"rel":884},"https:\u002F\u002Fdocs.unrealengine.com\u002Fen-us\u002FEngine\u002FRendering\u002FScreenPercentage",[39],"Dynamic Resolution","]和[",[35,888,891],{"href":889,"rel":890},"https:\u002F\u002Fdocs.unrealengine.com\u002Fen-us\u002FEngine\u002FRendering\u002FDynamicResolution",[39],"Screen Percentage with Temporal Upsample",[11,893,894],{},"为了提高TAAU的效果，可以设置MipBias，r.Streaming.MipBias来获得更好的效果。",[267,896,898],{"id":897},"z-prepass","Z PrePass",[11,900,901],{},"Fortnite使用完整的Z PrePass，r.EarlyZpass=2|r.EarlyZPassMoable=1。",[11,903,904],{},"这样主要的好处是可以开启EarlyZPassOnlyMaterialMasking，有效的降低后期在base pass中包括alpha测试之内的很多成本。",[11,906,907],{},"同时完整的Z Prepass可以使得SSAO能够提前进入异步计算的过程，减少时间上的消耗。",[267,909,910],{"id":910},"动态阴影",[11,912,913],{},"动态光照由于本身的成本非常的高，所以即便是在最高的scalability上也只使用了两级的CSM，而对于低端性能的设备，则会完全关闭CSM的阴影。",[11,915,916],{},"而为了弥补近处的阴影效果，则使用RTDF进行填补。",[11,918,919],{},"同时Fortnite还制作了一个轻量版的RTDF，RTDF的主要成本表现在Culling和Raytracing上。而这个轻量版将raymarching的步长从64降到了32，并且不会对subsurface scattering起作用。轻量版的更新将会在4.20到来。",[11,921,922],{},"AO方面，DFAO虽然效果比SSAO好，但是有更高的成本。关闭DFAO的时候为了防止出现亮度变高，可以降低Skylight的亮度并调高SSAO。",[267,924,926],{"id":925},"vertex","Vertex",[11,928,929],{},"与Vertex相关的性能是无法使用动态分辨率来解决的，所以这里的优化还是只能按照传统的策略来降低vertex相关的成本。",[11,931,932],{},"比如LOD、优化Foliage的Vertex Shader、减少远处的Foliage深度。",[11,934,935],{},"一个针对引擎的优化是，将异步计算中的同步锁改成了读写锁而不是排他锁，这个优化应当已经到了引擎中。",[11,937,938],{},"而Fortnite还对HLOD系统进行了很大的强化，以更好的保持Draw Call的稳定。",[267,940,942],{"id":941},"foliage","Foliage",[11,944,945],{},"根据不同的设备性能对Foliage的Density和distance进行调整。",[11,947,948],{},"对于装饰性的Foliage，在低内存的平台上会完全的隐藏掉。",[11,950,951],{},"在所有的平台上，都会关闭草的阴影。",[267,953,954],{"id":954},"材质",[11,956,957],{},"材质在调整时使用Quality Switch进行平台特性的粗略对应。",[11,959,960],{},"对于Vertex Animation，会尽量的进行简化或移除。",[11,962,963],{},"对于Pixel Cost，会尽量使用更少的贴图，采用更高效的blending。",[27,965,966],{"id":966},"总结",[11,968,969],{},"从总体上来看，Fortnite的优化虽然有很多UE4通用的优化策略，有的确是在实际进行制作中才会遇到的零散的性能瓶颈。",[11,971,972],{},"大的系统性的改动在联机同步策略、优先级控制上，另外由于游戏规则本身的需求，对Destructable的物体进行了HLOD系统的嵌入。以及其他的很多对引擎本身的优化都会在4.19和4.20中到达引擎中。",[11,974,975],{},"在这里只能做些记录，优化的事情往往在操作过程中才会出现很多意想不到的问题，希望到时能够成为参考的来源。",{"title":155,"searchDepth":156,"depth":157,"links":977},[978,999,1011,1019],{"id":236,"depth":156,"text":237,"children":979},[980,981,987],{"id":240,"depth":157,"text":241},{"id":253,"depth":157,"text":253,"children":982},[983,985,986],{"id":269,"depth":984,"text":270},4,{"id":347,"depth":984,"text":348},{"id":368,"depth":984,"text":368},{"id":418,"depth":157,"text":418,"children":988},[989,990,991,992,993,994,995,996,997,998],{"id":424,"depth":984,"text":425},{"id":459,"depth":984,"text":460},{"id":472,"depth":984,"text":472},{"id":487,"depth":984,"text":488},{"id":520,"depth":984,"text":520},{"id":547,"depth":984,"text":547},{"id":568,"depth":984,"text":568},{"id":580,"depth":984,"text":580},{"id":592,"depth":984,"text":592},{"id":604,"depth":984,"text":605},{"id":639,"depth":156,"text":639,"children":1000},[1001,1002,1003,1004,1005,1006,1007,1008,1009,1010],{"id":642,"depth":984,"text":643},{"id":674,"depth":984,"text":675},{"id":684,"depth":984,"text":685},{"id":703,"depth":984,"text":704},{"id":727,"depth":984,"text":728},{"id":752,"depth":984,"text":753},{"id":765,"depth":984,"text":766},{"id":781,"depth":984,"text":781},{"id":796,"depth":984,"text":797},{"id":829,"depth":984,"text":829},{"id":869,"depth":156,"text":870,"children":1012},[1013,1014,1015,1016,1017,1018],{"id":873,"depth":984,"text":874},{"id":897,"depth":984,"text":898},{"id":910,"depth":984,"text":910},{"id":925,"depth":984,"text":926},{"id":941,"depth":984,"text":942},{"id":954,"depth":984,"text":954},{"id":966,"depth":156,"text":966},"2018-04-08",{"layout":169,"status":170,"published":171,"author":1022,"author_login":174,"author_email":175,"wordpress_id":1023,"wordpress_url":1024,"date_gmt":1025,"excerpt":1026},{"display_name":173,"login":174,"email":175,"url":155},2328,"\u002F?p=2328","2018-04-07 17:01:09 +0000",{"type":8,"value":1027},[1028],[11,1029,197],{},"\u002F2018-04-08-gdc2018-fortnite-note",{"title":192,"description":197},"_legacy\u002F2018\u002F2018-04-08-gdc2018-fortnite-note",[187],"zL4my03OJ_DttUqNCWI2GYL6_Y2edaHGM9-ugxcPTEc",{"id":1036,"title":1037,"body":1038,"date":1532,"description":1042,"extension":167,"meta":1533,"navigation":171,"path":1542,"seo":1543,"stem":1544,"tags":1545,"__hash__":1546},"blogs\u002F_legacy\u002F2018\u002F2018-03-31-ue4-light-academy-note.md","UE4 Light Academy笔记",{"type":8,"value":1039,"toc":1497},[1040,1043,1046,1055,1058,1061,1064,1067,1070,1073,1081,1084,1087,1090,1093,1096,1099,1102,1105,1109,1112,1115,1118,1121,1124,1127,1130,1133,1137,1140,1143,1146,1149,1152,1157,1160,1163,1168,1171,1174,1178,1181,1184,1187,1190,1194,1197,1200,1203,1206,1209,1213,1216,1219,1223,1226,1229,1237,1240,1243,1246,1249,1252,1255,1258,1261,1264,1267,1270,1274,1278,1281,1284,1288,1291,1294,1297,1300,1304,1307,1311,1314,1322,1325,1328,1331,1334,1337,1340,1343,1346,1349,1353,1356,1359,1362,1366,1369,1372,1375,1378,1381,1385,1388,1391,1398,1402,1405,1408,1411,1414,1417,1420,1422,1426,1429,1432,1436,1439,1442,1446,1449,1452,1456,1459,1462,1465,1469,1472,1475,1478,1482,1485,1488,1491,1494],[11,1041,1042],{},"UE4在实际布局场景的时候，光照的布局是一个很重要的部分。",[11,1044,1045],{},"当前的UE4版本为4.19。",[11,1047,1048,1049,1054],{},"这是对社区的ighting artists大牛",[35,1050,1053],{"href":1051,"rel":1052},"https:\u002F\u002Ftwitter.com\u002Ftillederdon",[39],"@Daedalus51","发布的系列教程的笔记整理，虽然场景光照布局需要很多经验，但是通过看别人是如何进行光照布局的过程也能获得很多知识。",[11,1056,1057],{},"之所以关注这个系列的教程，是因为想要更加的熟练的使用引擎，而不是过多的关注技术上的细节。",[11,1059,1060],{},"场景的光照布局上，不仅需要对引擎的功能熟悉还要有一定的绘画或者摄影基础。对基本的构图原理，光影效果有了解的话才能更好的让场景显得更加真实。",[11,1062,1063],{},"会对场景产生真实感的东西包括：阴影、Reflection、Specular。",[11,1065,1066],{},"由于整个系列的时间跨度比较大，其间引擎有不少大的变更，所以内容上也有很多不再“正确”的地方。",[11,1068,1069],{},"正如Daedalus在视频中所说，他只是在分享个人的经验，并不代表场景光照必须按照这个流程去做。",[11,1071,1072],{},"在整个教材中Daedalus本人分享了很多有用的经验，如果你觉得这对你很有用，请务必向他Donate哦～",[11,1074,1075,1076,415],{},"原始的社区分享地址请到[",[35,1077,1080],{"href":1078,"rel":1079},"https:\u002F\u002Fforums.unrealengine.com\u002Fcommunity\u002Fcommunity-content-tools-and-tutorials\u002F106867-unreal-4-lighting-academy-or-something-like-that",[39],"Unreal4 Lighting Academy",[27,1082,1083],{"id":1083},"基础调整",[68,1085,1086],{"id":1086},"场景清理",[11,1088,1089],{},"关闭掉一些特效，以便对基础的光照结果进行调节。",[11,1091,1092],{},"尤其是PostProcess中的一些选项，对整体的渲染结果影响是很大的。",[11,1094,1095],{},"只有基础的光照结果是正确的情况下，后期进行调整才会有更大的自由度。",[11,1097,1098],{},"AutoExposion必须关掉，否则对场景的亮度判断基本很难进行。",[11,1100,1101],{},"光照布局时也要将一些动态的光照关掉，必要的时候需要依照次序对光照进行调整。",[11,1103,1104],{},"Fog之类的效果也要关掉，有时候雾气是有颜色的，光照效果会受到影响。",[68,1106,1108],{"id":1107},"light-map-resolution","Light Map Resolution",[11,1110,1111],{},"如果要使用静态光照的话，首先要调整的就是光照贴图的分辨率。当然在此之前需要布局Lightmass Important Volume。",[11,1113,1114],{},"这里需要注意的是，如果材质打开了Use for static light的话，会占用两个贴图位给光照贴图和阴影贴图。",[11,1116,1117],{},"另外记得在什么地方看过，Sampler Source可以基本上全部设置为shared， From Texture Asset是为了与旧版本兼容而存在的。",[11,1119,1120],{},"由于没有实际上的测试过，所以并不知道其准确性。",[11,1122,1123],{},"对于LightMap的UV设置不合理的情况，可以通过提高Lightmap的分辨率来解决，但是最好是在UV制作时就留意光照贴图的布局比较好。",[11,1125,1126],{},"LightMapDensity不是越高越好，而是在需要的地方使用需要的分辨率。",[11,1128,1129],{},"如果物体本身通过贴图应用了很多效果或者离玩家较远的话，在上面叠加细节的烘培阴影其实作用不大。",[11,1131,1132],{},"对于场景中很小的物体，光照贴图的分辨率调低成点都是可以的。因为它们不需要那么多的光照信息，对场景总体的贡献不大。",[68,1134,1136],{"id":1135},"base-color","Base Color",[11,1138,1139],{},"对于PBR的场景调整而言，需要注意的是。",[11,1141,1142],{},"不应当让Diffuse Color本身拥有太高的饱和度，似乎在PBR之前的工作流中需要将饱和度的细节烘培到贴图中去，但在PBR的渲染中，Diffuse Color会做进一步的运算。那么预先烘培进去的饱和度就会对场景的颜色产生必要以上的干扰。",[11,1144,1145],{},"另外一个需要注意的是贴图的整体的亮度，PBR渲染需要的Base Color的亮度会与体感有些不同。如果图片的明度不“正确”的话，会导致光照的效果比预先的更暗或者更亮。",[11,1147,1148],{},"通常情况下，白色的墙包括雪的base color都是灰色的而不是印象中的白色。整体而言，亮度的设定会比实际的体感亮度向内收缩。",[11,1150,1151],{},"在Photoshop的直方图中可以很好的对贴图的平均亮度进行观察",[11,1153,1154],{},[227,1155],{"alt":311,"src":1156},"\u002Fwp-content\u002Fuploads\u002F2018\u002F03\u002Fimage_thumb.png",[11,1158,1159],{},"明度的中间值即便对于很亮的物体，也不应当超过230，而再暗的物体也不应该低于50。",[11,1161,1162],{},"这些贴图的效果虽然可以在引擎中进行调节",[11,1164,1165],{},[227,1166],{"alt":311,"src":1167},"\u002Fwp-content\u002Fuploads\u002F2018\u002F03\u002Fimage_thumb-1.png",[11,1169,1170],{},"但是如果可以的话，对资源本身进行相应的调整是最好的。",[27,1172,1173],{"id":1173},"后期调整",[68,1175,1177],{"id":1176},"reflection-capture","Reflection Capture",[11,1179,1180],{},"在进行初步的预览烘培后，需要对反射捕捉进行合理的布局，可以在Reflection预览模式下查看效果。",[11,1182,1183],{},"必要的时候可以关掉SSR来进行调节。",[11,1185,1186],{},"在需要反射细节的地方需要额外的添加小的捕捉来获得更精细的反射效果。",[11,1188,1189],{},"对于室内场景这种完全方形的构造，可以选择使用方形的反射捕捉。",[68,1191,1193],{"id":1192},"fake-light","Fake Light",[11,1195,1196],{},"即便是进行了布局修改之后，引擎依然无法对现实中的光照进行完美的模拟。",[11,1198,1199],{},"这时候就需要使用到Fake Light的技巧。",[11,1201,1202],{},"通过Light Chanel来使得光照只影响需要的物品，来给过暗的物体添加更多的光照细节。必要的时候可以关闭灯光的投影，防止制造不必要的阴影。",[11,1204,1205],{},"这里还有一个小技巧，就是将光源放到Mesh内部，只要Mesh不是double side的，就可以模拟从内部发光的效果了。",[11,1207,1208],{},"除了Light Shaft和Volumetic Mesh这些常见的Fake Light之外，作者还介绍了一种模拟光照的Bloom效果的方法。",[68,1210,1212],{"id":1211},"fog","Fog",[11,1214,1215],{},"雾气的添加和调整被放到了后期，可以防止在前期调整时由于Fog本身的特效导致出现误判的情况。",[11,1217,1218],{},"可以通过调整Start Distance来改变雾气效果开始的距离。",[68,1220,1222],{"id":1221},"lut","LUT",[11,1224,1225],{},"Color Grading指的是在实际Tomemapper前进行的一次颜色的调整和修正。",[11,1227,1228],{},"虽然可以在引擎中进行调整，但是使用LUT会更方便一些。",[11,1230,1231,1232,415],{},"Color Grading可以使得场景向预先设定的场景风格靠拢，LUT的制作方法可以参考官方的[",[35,1233,1236],{"href":1234,"rel":1235},"https:\u002F\u002Fdocs.unrealengine.com\u002Fen-us\u002FEngine\u002FRendering\u002FPostProcessEffects\u002FUsingLUTs",[39],"LUT文档",[11,1238,1239],{},"Tonemapper的属性，像是WhiteClip可以将一些不真实的颜色Clip掉，但是Tonemapper的属性最好对于一个项目是不变的。针对场景颜色修正最好维持在Color Grading内。",[27,1241,1242],{"id":1242},"细节汇总",[68,1244,1245],{"id":1245},"光照",[11,1247,1248],{},"除了天空光之外，其他的光照都有提供Min Roughness的设置，方便在使用一些光照却不希望其本身产生过高的反射的情况。",[11,1250,1251],{},"点光源在进行光照计算时，成本上接近于6个Spot Light，在使用时要进行留意。",[11,1253,1254],{},"光照的Intensity不够的情况下，可能会出现无法覆盖设定的Radiu的情况。",[11,1256,1257],{},"UE4的动态光照是有缓存的，但是如果有巨大的动态光照的话，当玩家经过是会导致缓存更新，容易产生效率问题。不过本身动态光照优化时就需要注意光照的覆盖范围，所以不需要特别的留意。",[11,1259,1260],{},"Direct Light推荐的默认Intensity是3.141。",[11,1262,1263],{},"Stationary Light有一个Use Area Shadows for Stationary Light可以使得阴影效果更加柔和，光照的衰减也会更加真实。",[11,1265,1266],{},"当场景太暗时，首先检查Auto Exposion设置，然后检查Base Color是否太暗。",[11,1268,1269],{},"不要轻易使用Emmisive来调亮或者简单的调高灯光的亮度或者使用FakeLight，使用Emmisive的时候需要留意是否破坏了整体场景的光照平衡。",[68,1271,1273],{"id":1272},"skylight","SkyLight",[267,1275,1277],{"id":1276},"lower-hemisphere-is-solid-color","Lower Hemisphere Is Solid Color",[11,1279,1280],{},"可以模仿地面的反光效果，例如地面比较绿的话将天空光的这个选项打开，将颜色改为绿色就能获得仿佛是绿色的地面的反光效果。",[11,1282,1283],{},"在设置时注意要选用比较接近Base Color模式下的地面平均色为好。",[267,1285,1287],{"id":1286},"cubemap","CubeMap",[11,1289,1290],{},"除了捕捉之外，SkyLight也支持指定外部的CubeMap。",[11,1292,1293],{},"导入外部的贴图的话，要注意关掉MipMap和压缩。",[11,1295,1296],{},"天空贴图的导入最好选择EXR的方式，否则贴图的Wrap就需要天空球额外的作旋转处理。这个建议没有测试过，所以不是很明白。",[11,1298,1299],{},"自制天空球时要让其半径足够大，否则会阻挡大气雾和直射光。",[267,1301,1303],{"id":1302},"dfao","DFAO",[11,1305,1306],{},"这个功能是依赖于SkyLight的，必须将天空光置为Moveable才能使用。DFGI的功能开关目前似乎是与DFAO绑定的，所以要使用的话SkyLight必须也变成Moveable的。",[68,1308,1310],{"id":1309},"volumetric-lightmaps","Volumetric Lightmaps",[11,1312,1313],{},"近期的版本更新中，引擎引入了新的静态光照缓存方式。",[11,1315,1316,1317,1321],{},"详细的作用和变更可以参考[",[35,1318,718],{"href":1319,"rel":1320},"https:\u002F\u002Fdocs.unrealengine.com\u002Fen-us\u002FEngine\u002FRendering\u002FLightingAndShadows\u002FVolumetricLightmaps",[39],"]，由于变更了间接光照缓存的存储和插值方式。",[11,1323,1324],{},"使得预烘培的光照能够更好的对场景进行贡献。",[11,1326,1327],{},"最大的帮助之一就是Foliage的光照效果了。",[11,1329,1330],{},"在之前的版本中，Foliage的光照始终都有很多麻烦。尤其是对开放式的场景，如果使用静态光照的话，会导致贴图空间的大规模消耗。",[11,1332,1333],{},"虽然草的话可以使用Landscape Grass Type中的使用Landscape光照信息来解决，对于树而言就没有办法了。",[11,1335,1336],{},"基本上的解决方案只有将AO这些光照信息事先烘培到贴图上，静态光照的树的阴影确实很好看，但是对于游戏环境就很不适宜了。",[11,1338,1339],{},"在有了Volumetric LightMaps并更新到4.19的话，就可以通过调整物体的Lightmap Type来让树获得Volumetric LightMap的信息，形成很好的阴影效果。",[11,1341,1342],{},"另外，关于Foliage的wind，如果动态的效果不是很好，可以打开项目设置中的Accurate velocities from vertex deformation，这样的话WPO之类的动态效果就会写入到速度缓存中，能够获得TAA更好的处理。",[11,1344,1345],{},"Wind的特效，可以将其与水的运动材质在world space上相乘获得更好的随机运动效果。可以进一步使用UV来控制使得更高的像素获得更多的运动。",[11,1347,1348],{},"Speed tree color variation的随机方式有一定的问题，可以在world space上使用一张GrassNorise来实现颜色的随机效果。",[68,1350,1352],{"id":1351},"动态gi","动态GI",[11,1354,1355],{},"由于LPV是由LionHead提交到引擎的，而该工作室已经关闭，所以Epic官方在最近的版本中并没有对其进行太多的改进。所以LPV的“实验性”的标签也一直都没有去掉。",[11,1357,1358],{},"动态GI上，目前Nvidia有提供Vxgi的UE4分支，但由于是非Epic官方的分支，在更新步调上多少会有一些滞后性。",[11,1360,1361],{},"而引擎内部有DFGI可供使用，但是由于使用到了Volumetric Distance Field，对于显卡本身的特性支持是有要求的。",[68,1363,1365],{"id":1364},"postprocess","PostProcess",[11,1367,1368],{},"PP中的Lightmass相关选项，理所当然的，在强制不使用静态光照时是没有作用的。",[11,1370,1371],{},"而其中的AO CubeMap的作用会被SkyLight中的相关选项覆盖掉所以要注意。",[11,1373,1374],{},"AutoExposion在洞穴中可以使用Min Brightness 0.1和Max Brightness 0.13这样的值进行调整。",[11,1376,1377],{},"Lens中Dirt Mask用于模拟镜头上的不光滑点在光照下引起的折射泛光，而Lens Flare则模拟强光本身在镜头上引起的泛光。这两者都可以额外的指定Texture。",[11,1379,1380],{},"Bloom method中可以选择Convolution，使用FFT的方式来生成更真实的Bloom效果，但是由于性能消耗较大，可能不适合用于游戏中。",[68,1382,1384],{"id":1383},"distance-field","Distance Field",[11,1386,1387],{},"如果使用了DFAO之类的依赖距离场的特性的话，注意在Depth Field预览中查看场景中的物体是否正确的生成了距离场。",[11,1389,1390],{},"已知的使用率距离场的材质节点有：DistanceToNearestSurface和Distance Field Gradient，一旦用来的话就要注意场景的距离场设置是否正确。",[11,1392,1393,1394,415],{},"对于粒子使用了距离场碰撞的也是如此，更多的关于距离场的设定可以参照[",[35,1395,718],{"href":1396,"rel":1397},"https:\u002F\u002Fdocs.unrealengine.com\u002Fen-us\u002FEngine\u002FRendering\u002FLightingAndShadows\u002FMeshDistanceFields",[39],[68,1399,1401],{"id":1400},"world-setting","World Setting",[11,1403,1404],{},"世界设置中的LightMass的设定可以调整静态光照的烘培设置。",[11,1406,1407],{},"如果要在材质中使用Precomputed AO Mask的话，一定要记得在这里打开相应的生成开关。",[11,1409,1410],{},"关于Bounce的设定，在SkyLight支持Multi Bounce之后，之前版本中用到的一些Trick就不是那么必要了。",[11,1412,1413],{},"同时，虽然官方描述说Bounce的上升对场景的贡献比较少，但是当作封闭性比较强的场景中，多的Bounce可以让光照的预计算传播的更远。",[11,1415,1416],{},"如果Indirect Lighting Quality如果上升的话，就没有必要提高Indirect Lighting Smooth了。",[11,1418,1419],{},"Static Lighting Level Scale这个选项的影响很大，不再必要的时候最好不要修改。",[68,1421,368],{"id":368},[267,1423,1425],{"id":1424},"contact-shadow","Contact Shadow",[11,1427,1428],{},"这个是Point Light才有的功能。Contact Shadow可以用于增加角色的细节特效，同时可以加强视差贴图的效果。",[11,1430,1431],{},"对于视差贴图的情况，pixel depth offset上必须有对应的输出信息才能生成遮蔽效果。",[267,1433,1435],{"id":1434},"early-z-passd-buffer-decal","Early Z-Pass\u002FD-Buffer Decal",[11,1437,1438],{},"据说在没有这个功能之前，Decal与静态光照的结合相当的让人痛苦。",[11,1440,1441],{},"由于没有实际遇到过，所以不好描述呢。",[267,1443,1445],{"id":1444},"ssr","SSR",[11,1447,1448],{},"ssr对场景的遮蔽效果还是有比较大的贡献的，需要注意的是r.SSR.Quality会锁定PP中的SSR质量上限，导致即便调到更高的值也没有什么质量上的变化。",[11,1450,1451],{},"如果要调整Reflection Captrue的话，可以将SSR暂时的关掉。",[267,1453,1455],{"id":1454},"subsurface","Subsurface",[11,1457,1458],{},"似乎在材质的制作中，会有经常用到Subsurface的情况。",[11,1460,1461],{},"有看到冰块和树叶使用到这种Shading model，不过由于之前没有接触过这方面的工作流，只是对引擎方面的调整稍微看了一下，这个可能要到用到的时候才会继续研究呢。因为似乎在很多材质上都会用到Subsurface的样子。",[11,1463,1464],{},"subsurface需要注意的是，目前的引擎中Static Light并不会参与Subsurface的计算。",[267,1466,1468],{"id":1467},"tessellation","Tessellation",[11,1470,1471],{},"在使用的时候，world displacement可以与VertexNormalWs相乘，获得更好的效果。",[11,1473,1474],{},"以及，在Lighting Only的预览中是看不见Tessellation的节点效果的。",[11,1476,1477],{},"Tessellation最好做一下DepthFade以在远处物体上关闭，避免不必要的性能消耗。",[267,1479,1481],{"id":1480},"gbuffer-hint","Gbuffer Hint",[11,1483,1484],{},"这个功能在Show\u002FDeveloper里面，不过打开之后只有在左上角的功能提示。",[11,1486,1487],{},"在视频中也没有看到这个功能实际的被使用到，所以不是很明白到底应当如何使用呢。",[267,1489,1490],{"id":1490},"天空球",[11,1492,1493],{},"由于Daedalus本人似乎不是很喜欢使用官方的天空球所以只有一开始的视频中有使用到。",[11,1495,1496],{},"天空球有提供绑定到作为太阳光的Directional Light上的功能，同时可以由太阳光的角度来决定天空球的颜色。",{"title":155,"searchDepth":156,"depth":157,"links":1498},[1499,1504,1510],{"id":1083,"depth":156,"text":1083,"children":1500},[1501,1502,1503],{"id":1086,"depth":157,"text":1086},{"id":1107,"depth":157,"text":1108},{"id":1135,"depth":157,"text":1136},{"id":1173,"depth":156,"text":1173,"children":1505},[1506,1507,1508,1509],{"id":1176,"depth":157,"text":1177},{"id":1192,"depth":157,"text":1193},{"id":1211,"depth":157,"text":1212},{"id":1221,"depth":157,"text":1222},{"id":1242,"depth":156,"text":1242,"children":1511},[1512,1513,1518,1519,1520,1521,1522,1523],{"id":1245,"depth":157,"text":1245},{"id":1272,"depth":157,"text":1273,"children":1514},[1515,1516,1517],{"id":1276,"depth":984,"text":1277},{"id":1286,"depth":984,"text":1287},{"id":1302,"depth":984,"text":1303},{"id":1309,"depth":157,"text":1310},{"id":1351,"depth":157,"text":1352},{"id":1364,"depth":157,"text":1365},{"id":1383,"depth":157,"text":1384},{"id":1400,"depth":157,"text":1401},{"id":368,"depth":157,"text":368,"children":1524},[1525,1526,1527,1528,1529,1530,1531],{"id":1424,"depth":984,"text":1425},{"id":1434,"depth":984,"text":1435},{"id":1444,"depth":984,"text":1445},{"id":1454,"depth":984,"text":1455},{"id":1467,"depth":984,"text":1468},{"id":1480,"depth":984,"text":1481},{"id":1490,"depth":984,"text":1490},"2018-03-31",{"layout":169,"status":170,"published":171,"author":1534,"author_login":174,"author_email":175,"wordpress_id":1535,"wordpress_url":1536,"date_gmt":1537,"excerpt":1538},{"display_name":173,"login":174,"email":175,"url":155},2294,"\u002F?p=2294","2018-03-31 14:22:30 +0000",{"type":8,"value":1539},[1540],[11,1541,1042],{},"\u002F2018-03-31-ue4-light-academy-note",{"title":1037,"description":1042},"_legacy\u002F2018\u002F2018-03-31-ue4-light-academy-note",[187],"SEC1lbvUsitAL16HIKGovhP55NvlbCweQdMdPjpT-mE",{"id":1548,"title":1549,"body":1550,"date":1708,"description":1554,"extension":167,"meta":1709,"navigation":171,"path":1718,"seo":1719,"stem":1720,"tags":1721,"__hash__":1723},"blogs\u002F_legacy\u002F2018\u002F2018-02-26-ue4-note-2018-01.md","UE4年初笔记汇总",{"type":8,"value":1551,"toc":1700},[1552,1555,1558,1561,1565,1568,1571,1574,1577,1580,1586,1589,1595,1598,1603,1607,1610,1613,1616,1619,1625,1628,1631,1634,1638,1641,1644,1647,1650,1656,1659,1665,1668,1671,1674,1677,1682,1685,1688,1697],[11,1553,1554],{},"18年转眼马上过去两个月了，却有种还什么都没有做的感觉呢。",[11,1556,1557],{},"这里是最近的一些记录内容，由于比较零碎细小，干脆就合在一起了。",[11,1559,1560],{},"使用的UE4版本为UE4.15。",[27,1562,1564],{"id":1563},"widgetbind","WidgetBind",[11,1566,1567],{},"这个是一个Meta，在UMG的数据绑定时很有用。",[11,1569,1570],{},"虽然UMG的逻辑大部分可以在蓝图中完成，而且控件也有自己的UPROPERTY和UFUNCTION绑定机制。",[11,1572,1573],{},"但是在需要进行复杂的UI逻辑编写时还是会需要在代码中进行控制。",[11,1575,1576],{},"这个时候就会遇到需要对应的UMG控件的指针的问题，虽然可以通过事先定义的UPROPERTY指针然后再在蓝图中进行赋值等一些方法进行。但在使用上多少会有不便之处。",[11,1578,1579],{},"其实，UE4内部是有提供方便这种情况的Meta方便使用的：",[295,1581,1584],{"className":1582,"code":1583,"language":300},[298],"UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (BindWidget))\nUImage* ImgBinded;\n",[302,1585,1583],{"__ignoreMap":155},[11,1587,1588],{},"只要这样定义了之后，再做继承了的蓝图中拉出目标控件然后命名为ImgBinded就可以了。同时也可以将控件指定为可选的，例如：",[295,1590,1593],{"className":1591,"code":1592,"language":300},[298],"UPROPERTY(meta=(BindWidget, OptionalWidget=true))\n",[302,1594,1592],{"__ignoreMap":155},[11,1596,1597],{},"如果不指定为可选又没有在蓝图中添加对应名称的控件的话，就会报错",[11,1599,1600],{},[227,1601],{"alt":311,"src":1602},"\u002Fwp-content\u002Fuploads\u002F2018\u002F02\u002Fimage_thumb.png",[27,1604,1606],{"id":1605},"console","Console",[11,1608,1609],{},"这个也是最近遇到的麻烦，当程序的业务逻辑越来越复杂，往往会多出很多功能上的限制，导致在测试时遇到麻烦。",[11,1611,1612],{},"每次都要额外的添加测试UI，将一些C++函数暴露到蓝图，最后测试功能就一片混乱了。此时就可以添加一些Console来方便在编辑器中调试，由于Shipping模式默认是没有Console的，也不用担心一不小心测试代码被放到终端用户手中的尴尬情况。",[11,1614,1615],{},"同时，将Console代码进行一定程度的管理，也可以方便将调试功能整合在一起，避免不必要的混乱。",[11,1617,1618],{},"UE4的Console系统非常的易于扩展，使用起来也没有什么副作用：",[295,1620,1623],{"className":1621,"code":1622,"language":300},[298],"void HandleDbNextMap()\n{\n    \u002F** \u003C一些调试功能 *\u002F;\n}\n\nstatic FAutoConsoleCommand CVarDbGoNext(\n    TEXT(\"db.map.jmp\"),\u002F** \u003CConsole的指令 *\u002F\n    TEXT(\"Jump to nextmap\\n\"),\u002F** \u003C显示的帮助信息 *\u002F\n    FConsoleCommandDelegate::CreateStatic(&HandleDbNextMap)\n);\n",[302,1624,1622],{"__ignoreMap":155},[11,1626,1627],{},"使用UE4提供的帮助类，能够非常快速的定义Console指令。ConsoleCommand的使用和通常的Delegate是一样的，添加上面的代码后。",[11,1629,1630],{},"就可以在Console中输入db.map.jmp来使用了。",[11,1632,1633],{},"对于需要输入参数的情况，其实FAutoConsoleCommand还有其他的变体是可以携带参数的，详情可以参考其定义。",[27,1635,1637],{"id":1636},"media-frame-work","Media Frame Work",[11,1639,1640],{},"UE4默认使用WMF在Windows上进行视频播放的操作，但是在使用上还是会有些细节的部分。",[68,1642,1643],{"id":1643},"二次播放杂音",[11,1645,1646],{},"这个Bug一般是自己使用C++进行播放控制时容易出现，使用蓝图进行播放时没有遇到这个问题。",[11,1648,1649],{},"问题在于，MediaPlayer的声音播放并不会自动的跟随MediaPlayer的停止、播放、重新开启操作被控制，虽然表面上声音的播放确实停止了，但是重新开始播放声音的话就会出现状态重置不明确的问题，因此需要自己对事件进行绑定会进行控制。",[295,1651,1654],{"className":1652,"code":1653,"language":300},[298],"void UStreamPlayer::HandleMediaPlayerMediaEvent(EMediaEvent Event)\n{\n    if ((AudioComponent == nullptr) \u002F*|| !CurrentSoundWave.IsValid()*\u002F)\n    {\n        return;\n    }\n\n    switch (Event)\n    {\n    case EMediaEvent::PlaybackEndReached:\n    case EMediaEvent::PlaybackSuspended:\n        AudioComponent->Stop();\n        break;\n    case EMediaEvent::PlaybackResumed:\n        AudioComponent->Play();\n        break;\n    }\n}\n",[302,1655,1653],{"__ignoreMap":155},[11,1657,1658],{},"然后找一个时机绑定到MediaPlayer上去就可以了",[295,1660,1663],{"className":1661,"code":1662,"language":300},[298],"m_pMediaPlayer->OnMediaEvent().AddUObject(this, &UStreamPlayer::HandleMediaPlayerMediaEvent);\n",[302,1664,1662],{"__ignoreMap":155},[11,1666,1667],{},"这个是在4.15上遇到的，记得之后的版本更新中MediaPlayer有过改动，不知是不是就没有这样的问题了。",[68,1669,1670],{"id":1670},"打包后无法播放",[11,1672,1673],{},"首先要检查的是打包时对应的视频文件有没有被拷贝到发布目录中，由于UE4打包的时候只会对进行了链接注册的资源进行打包，所以如果视频文件没有被蓝图或者C++显式的引用的话，并不会被自动拷贝到发布目录中去。",[11,1675,1676],{},"所以需要在打包设置中进行设置，保证视频目录中的视频都被拷贝到了发行版中。",[11,1678,1679],{},[227,1680],{"alt":311,"src":1681},"\u002Fwp-content\u002Fuploads\u002F2018\u002F02\u002Fadditional-pack.jpg",[11,1683,1684],{},"当然，也可以手动将资源拷贝到对应的目录中去，不过这样不方便管理，容易忘记……",[11,1686,1687],{},"另外就是要检查系统本身的Wmf相关的设定了，最主要就是检查下WindowsMediaPlayer能否正常的播放视频，因为wmp和ue4一样都依赖wmf进行视频播放，所以容易排查问题。主要是有的Ghost的系统会有不必要的精简，导致wmf功能不正常。",[11,1689,1690,1691,1696],{},"如果你使用的是最新版本的引擎的话，也可以考虑社区提供的[",[35,1692,1695],{"href":1693,"rel":1694},"https:\u002F\u002Fgithub.com\u002Fue4plugins\u002FVlcMedia",[39],"VlcMedia","]的插件，经过测试4.18版本下工作很正常，不过由于这边使用的是4.15版本，就没有办法了呢。",[11,1698,1699],{},"同时Vlc还支持更多的播放格式和方式，例如Vlc是支持m3u8的，而wmf并不支持很多流播放的功能。",{"title":155,"searchDepth":156,"depth":157,"links":1701},[1702,1703,1704],{"id":1563,"depth":156,"text":1564},{"id":1605,"depth":156,"text":1606},{"id":1636,"depth":156,"text":1637,"children":1705},[1706,1707],{"id":1643,"depth":157,"text":1643},{"id":1670,"depth":157,"text":1670},"2018-02-26",{"layout":169,"status":170,"published":171,"author":1710,"author_login":174,"author_email":175,"wordpress_id":1711,"wordpress_url":1712,"date_gmt":1713,"excerpt":1714},{"display_name":173,"login":174,"email":175,"url":155},2264,"\u002F?p=2264","2018-02-25 16:00:00 +0000",{"type":8,"value":1715},[1716],[11,1717,1554],{},"\u002F2018-02-26-ue4-note-2018-01",{"title":1549,"description":1554},"_legacy\u002F2018\u002F2018-02-26-ue4-note-2018-01",[187,1722],"UMG","MMR8srEpeInDZHJaVZNbZRX20WDch0GtTv8IohtGnOo",{"id":1725,"title":1726,"body":1727,"date":2092,"description":1731,"extension":167,"meta":2093,"navigation":171,"path":2102,"seo":2103,"stem":2104,"tags":2105,"__hash__":2108},"blogs\u002F_legacy\u002F2018\u002F2018-01-02-ue4-rendering-code-view-02.md","UE4渲染代码逻辑总结（下）",{"type":8,"value":1728,"toc":2075},[1729,1732,1735,1739,1742,1745,1748,1752,1755,1787,1790,1793,1796,1799,1805,1808,1811,1816,1819,1822,1826,1829,1832,1835,1838,1844,1847,1853,1856,1859,1862,1868,1871,1874,1877,1880,1883,1886,1890,1893,1896,1899,1905,1908,1914,1917,1921,1924,1927,1930,1933,1936,1939,1942,1945,2036,2039,2043,2046,2049,2052,2055,2058,2061,2064,2067,2069,2072],[11,1730,1731],{},"前面的内容都集中在C++端了，这边的内容会往Shader靠的更近一些。",[11,1733,1734],{},"由于感觉上很多东西要全部梳理明白会花费很多时间却又没有多大用处，所以这里的内容实际上进行了精简，因此比预定的短了很多。",[27,1736,1738],{"id":1737},"shader端","Shader端",[11,1740,1741],{},"虚幻使用HLSL作为Shader的语言，引擎核心的Shader都可以在引擎目录的Shader文件夹下找到。",[11,1743,1744],{},"其中ush为Shader头文件，而usf为Shader的源文件。由于Shader部分的代码基本属于引擎渲染的核心部分，要全部理解起来就有些费时间了。",[11,1746,1747],{},"所以这里只是按照其与C++部分接洽的结构进行粗略的探索。",[68,1749,1751],{"id":1750},"vertexfatory","VertexFatory",[11,1753,1754],{},"VertexFatory是C++端将数据推送到Shader端的途径，在Shader文件夹中能够看到以下的几个：",[105,1756,1757,1760,1763,1766,1769,1772,1775,1778,1781,1784],{},[11,1758,1759],{},"VectorFieldVisualizationVertexFactory.ush",[11,1761,1762],{},"ParticleSpriteVertexFactory.ush",[11,1764,1765],{},"ParticleGPUSpriteVertexFactory.ush",[11,1767,1768],{},"ParticleBeamTrailVertexFactory.ush",[11,1770,1771],{},"NiagaraMeshVertexFactory.ush",[11,1773,1774],{},"NiagaraSpriteVertexFactory.ush",[11,1776,1777],{},"MeshParticleVertexFactory.ush",[11,1779,1780],{},"LocalVertexFactory.ush",[11,1782,1783],{},"LandscapeVertexFactory.ush",[11,1785,1786],{},"GpuSkinVertexFactory.ush",[11,1788,1789],{},"分别对应不同的使用情况，从命名上基本就能看出其用途。",[11,1791,1792],{},"Niagara是UE4的下一代粒子系统，目前版本可以在插件中打开，但是功能似乎并不完全，也没有文档，没有办法使用。",[11,1794,1795],{},"另外，这里的Shader与FVertexFatory并不是一一对应的关系。使用相同的渲染路径的类会在这里共用Shader。",[11,1797,1798],{},"如LocalVertexFactory.ush就被FLocalVertexFactory、FEmulatedInstancedStaticMeshVertexFactory、FInstancedStaticMeshVertexFactory、FGPUSkinPassthroughVertexFactory和FSplineMeshVertexFactory共同使用。",[11,1800,1801],{},[1802,1803,1804],"strong",{},"FVertexFactoryInput",[11,1806,1807],{},"这个数据结构是来自C++端的数据输入。在不同的Shader头文件中可能会有不同的定义，使用方式上自然也会有不同。",[11,1809,1810],{},"也有一些通用的Shader函数被定义来处理输入数据，如GetVertexFactoryIntermediates, VertexFactoryGetWorldPosition, GetMaterialVertexParameters。",[11,1812,1813],{},[1802,1814,1815],{},"FBasePassVSOutput",[11,1817,1818],{},"这个是另一个比较特殊的结构，由于不同的渲染路径可能在Vertex Shader结束后使用的路径是不同的。",[11,1820,1821],{},"所以也能看到对这个结构的不同定义。",[68,1823,1825],{"id":1824},"material","Material",[11,1827,1828],{},"所有的材质最终都会被编译成Shader，在材质编辑器中也能够看到材质的Shader预览。",[267,1830,1831],{"id":1831},"材质蓝图",[11,1833,1834],{},"用于将材质蓝图编译成Shader的模板在MaterialTemplate.ush中，查看这个文件的话，会看到有很多地方都是直接写成%s的。",[11,1836,1837],{},"这些都是由引擎将材质蓝图中的节点填充到这里的，例如",[295,1839,1842],{"className":1840,"code":1841,"language":300},[298],"\u002F**\n* Parameters calculated from the pixel material inputs.\n*\u002F\nstruct FPixelMaterialInputs\n{\n%s\n};\n",[302,1843,1841],{"__ignoreMap":155},[11,1845,1846],{},"可能会被填写成",[295,1848,1851],{"className":1849,"code":1850,"language":300},[298],"\u002F**\n* Parameters calculated from the pixel material inputs.\n*\u002F\nstruct FPixelMaterialInputs\n{\nMaterialFloat3 EmissiveColor;\nMaterialFloat Opacity;\nMaterialFloat OpacityMask;\nMaterialFloat3 BaseColor;\nMaterialFloat Metallic;\nMaterialFloat Specular;\nMaterialFloat Roughness;\nMaterialFloat3 Normal;\nMaterialFloat4 Subsurface;\nMaterialFloat AmbientOcclusion;\nMaterialFloat2 Refraction;\nMaterialFloat PixelDepthOffset;\n\n};\n",[302,1852,1850],{"__ignoreMap":155},[11,1854,1855],{},"想要详细的了解的话可以在材质编辑器中修改材质，然后预览HLSL代码并与MaterialTemplate.ush对比以了解更多的内部工作原理。",[267,1857,1858],{"id":1858},"数据获取",[11,1860,1861],{},"在材质完成编译之后，渲染路径中就可以在需要时对材质中定义的相应的属性进行获取了。",[295,1863,1866],{"className":1864,"code":1865,"language":300},[298],"half3 BaseColor = GetMaterialBaseColor(PixelMaterialInputs);\nhalf  Metallic = GetMaterialMetallic(PixelMaterialInputs);\nhalf  Specular = GetMaterialSpecular(PixelMaterialInputs);\n",[302,1867,1865],{"__ignoreMap":155},[11,1869,1870],{},"不同的shader根据不同的渲染路径将数据最终填充到GBuffer中，以便进行进一步的计算。",[267,1872,1873],{"id":1873},"计算",[11,1875,1876],{},"在GBuffer的生成过程前、过程中、过程后，都有很多复杂的计算。",[11,1878,1879],{},"这些过程包括各种裁剪、光照以及PostProcess，由于并非是要进行这些逻辑的修改或者扩展，便不再深究下去了。",[27,1881,1882],{"id":1882},"渲染逻辑",[11,1884,1885],{},"有了渲染用的C++端和Shader端代码之后，终于可以开始进行渲染工作了。",[68,1887,1889],{"id":1888},"fdeferredshadingscenerenderer","FDeferredShadingSceneRenderer",[11,1891,1892],{},"对于PC端的延迟渲染，最终负责进行渲染工作的就是这个类了。",[11,1894,1895],{},"渲染的调用来源为FRendererModule::BeginRenderingViewFamily，可以看到有些编辑器的缩略图也会调用这个函数进行渲染，和玩家看到的游戏界面有关的渲染调用来自UGameViewportClient::Draw。",[11,1897,1898],{},"BeginRenderingViewFamily这个函数内在进行一些渲染的准备后，将实际渲染的函数扔到渲染线程",[295,1900,1903],{"className":1901,"code":1902,"language":300},[298],"ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(\nFDrawSceneCommand,\nFSceneRenderer*,SceneRenderer,SceneRenderer,\n{\n  RenderViewFamily_RenderThread(RHICmdList, SceneRenderer);\n  FlushPendingDeleteRHIResources_RenderThread();\n});\n",[302,1904,1902],{"__ignoreMap":155},[11,1906,1907],{},"然后就实际执行渲染",[295,1909,1912],{"className":1910,"code":1911,"language":300},[298],"SceneRenderer->Render(RHICmdList);\n",[302,1913,1911],{"__ignoreMap":155},[11,1915,1916],{},"基本上渲染的主要逻辑就在这个函数中。",[68,1918,1920],{"id":1919},"rendershadowdepthmaps","RenderShadowDepthMaps",[11,1922,1923],{},"这个是SceneRenderer->Render中对深度贴图生成，从中可以看出渲染是如何最终使用Shader的。",[11,1925,1926],{},"比较关键的调用之一是ProjectedShadowInfo->RenderDepth(RHICmdList, this, SetShadowRenderTargets, ShadowDepthRenderMode_Normal);",[11,1928,1929],{},"这个函数进一步调用FProjectedShadowInfo::RenderDepth并继而调用FProjectedShadowInfo::RenderDepthInner",[11,1931,1932],{},"而这之中会有SceneRenderer->Scene->WholeSceneReflectiveShadowMapDrawList.DrawVisible",[11,1934,1935],{},"而这个WholeSceneReflectiveShadowMapDrawList就是一张DrawingPolicy列表了，到了这里就能与Shader相关的类型联系上了。",[68,1937,1938],{"id":1938},"渲染流程",[11,1940,1941],{},"关于渲染的流程，到了这一步其实就和之前看到的差不多了，因此这里不做赘述。",[11,1943,1944],{},"下面的内容直接引用自官方文档，所以不保证与当前版本的内容匹配：",[1946,1947,1948,1961],"table",{},[1949,1950,1951],"thead",{},[1952,1953,1954,1958],"tr",{},[1955,1956,1957],"th",{},"操作",[1955,1959,1960],{},"描述",[1962,1963,1964,1973,1981,1989,1997,2005,2013,2020,2028],"tbody",{},[1952,1965,1966,1970],{},[1967,1968,1969],"td",{},"GSceneRenderTargets.Allocate",[1967,1971,1972],{},"按需要重新分配全局场景渲染目标，使其对当前视图足够大。",[1952,1974,1975,1978],{},[1967,1976,1977],{},"InitViews",[1967,1979,1980],{},"‭通过多种剔除方法为视图初始化基元可见性，设立此帧可见的动态阴影、按需要交叉阴影视锥与世界场景（对整个场景的阴影或预阴影）。",[1952,1982,1983,1986],{},[1967,1984,1985],{},"PrePass \u002F Depth only pass",[1967,1987,1988],{},"RenderPrePass \u002F FDepthDrawingPolicy。渲染遮挡物，对景深缓冲区仅输出景深。该通道可以在多种模式下工作：禁用、仅遮蔽，或完全景深，具体取决于活动状态的功能的需要。该通道通常的用途是初始化 Hierarchical Z 以降低 Base 通道的着色消耗（Base 通道的像素着色器消耗非常大）。",[1952,1990,1991,1994],{},[1967,1992,1993],{},"Base pass",[1967,1995,1996],{},"RenderBasePass \u002F TBasePassDrawingPolicy。渲染不透明和遮盖的材质，向 GBuffer 输出材质属性。光照图贡献和天空光照也会在此计算并加入场景颜色。",[1952,1998,1999,2002],{},[1967,2000,2001],{},"Issue Occlusion Queries \u002F BeginOcclusionTests",[1967,2003,2004],{},"提出将用于下一帧的 InitViews 的延迟遮蔽查询。这会通过渲染所查询物体周围的相邻的框、有时还会将相邻的框组合在一起以减少绘制调用来完成。",[1952,2006,2007,2010],{},[1967,2008,2009],{},"Lighting",[1967,2011,2012],{},"阴影图将对各个光照渲染，光照贡献会累加到场景颜色，并使用标准延迟和平铺延迟着色。光照也会在透明光照体积中累加。",[1952,2014,2015,2017],{},[1967,2016,1212],{},[1967,2018,2019],{},"雾和大气在延迟通道中对不透明表面进行逐个像素计算。",[1952,2021,2022,2025],{},[1967,2023,2024],{},"Translucency",[1967,2026,2027],{},"透明度累加到屏外渲染目标，在其中它应用了逐个顶点的雾化，因而可以整合到场景中。光照透明度在一个通道中计算最终光照以正确融合。",[1952,2029,2030,2033],{},[1967,2031,2032],{},"Post Processing",[1967,2034,2035],{},"多种后期处理效果均通过 GBuffers 应用。透明度将合成到场景中。",[11,2037,2038],{},"直接阅读FRendererModule::BeginRenderingViewFamily就可以看到UE4是如何对渲染通路进行处理的，其中有的较为简单的就会直接调用Shader进行处理，较为复杂的就会有相应的过程封装。",[68,2040,2042],{"id":2041},"rendering-paths","Rendering paths",[11,2044,2045],{},"根据官方文档的描述，渲染路径分为Dymaic和Static两种。其中动态的速度会更慢些但是拥有更多的控制选项。",[11,2047,2048],{},"FPrimitiveSceneProxy会在GetViewRelevance中返回相关性标志，这样在渲染时引擎就会决定是否调用DrawDynamicElements和DrawStaticElements。",[11,2050,2051],{},"大致看来，static rendering path会在物体被加入FScene的时候，就将自己加入到绘制列表中。而dynamic rendering path由于可以做一些动态处理，并在DrawDynamicElements中提供了回调，所以就没有办法利用缓存机制了。",[11,2053,2054],{},"可以看到很多类似这样的调用",[11,2056,2057],{},"PrimitiveSceneInfo->Proxy->GetDynamicMeshElements(InViewFamily.Views, InViewFamily, ViewMask, Collector);",[11,2059,2060],{},"而SceneRender中也能看到GatherDynamicMeshElements这样的函数。",[11,2062,2063],{},"感觉上Static的渲染路径指的应该是FScene中大量存在的模板化的列表TStaticMeshDrawList。",[11,2065,2066],{},"不过由于这方面几乎找不到资料，文档中没有更加详细的说明，社区也基本看不到讨论，要从源码中回溯其意图就比较费时了，所以便没有进一步深究。",[27,2068,966],{"id":966},[11,2070,2071],{},"到了这里，这个UE4的渲染逻辑就能有一个大致的草图了。",[11,2073,2074],{},"虽然其中还有更多的细节和详细是实现，也只有到了需要的时候再深入了解了。毕竟这部分已经是引擎开发者的工作，而太过于深入就没有意义了。",{"title":155,"searchDepth":156,"depth":157,"links":2076},[2077,2085,2091],{"id":1737,"depth":156,"text":1738,"children":2078},[2079,2080],{"id":1750,"depth":157,"text":1751},{"id":1824,"depth":157,"text":1825,"children":2081},[2082,2083,2084],{"id":1831,"depth":984,"text":1831},{"id":1858,"depth":984,"text":1858},{"id":1873,"depth":984,"text":1873},{"id":1882,"depth":156,"text":1882,"children":2086},[2087,2088,2089,2090],{"id":1888,"depth":157,"text":1889},{"id":1919,"depth":157,"text":1920},{"id":1938,"depth":157,"text":1938},{"id":2041,"depth":157,"text":2042},{"id":966,"depth":156,"text":966},"2018-01-02",{"layout":169,"status":170,"published":171,"author":2094,"author_login":174,"author_email":175,"wordpress_id":2095,"wordpress_url":2096,"date_gmt":2097,"excerpt":2098},{"display_name":173,"login":174,"email":175,"url":155},2209,"\u002F\u002F?p=2209","2018-01-01 16:02:45 +0000",{"type":8,"value":2099},[2100],[11,2101,1731],{},"\u002F2018-01-02-ue4-rendering-code-view-02",{"title":1726,"description":1731},"_legacy\u002F2018\u002F2018-01-02-ue4-rendering-code-view-02",[2106,187,2107],"shader","Rendering","mZ30HsJT42pZP836dw4bOQTLX2RfTXQDvouudBp_UO8",85,1788763178207]