[{"data":1,"prerenderedAt":1838},["ShallowReactive",2],{"page-Rendering-1":3,"page-count-Rendering":378},[4,413,852,1609],{"id":5,"title":6,"body":7,"date":388,"description":13,"extension":389,"meta":390,"navigation":393,"path":405,"seo":406,"stem":407,"tags":408,"__hash__":412},"blogs\u002F_legacy\u002F2018\u002F2018-01-02-ue4-rendering-code-view-02.md","UE4渲染代码逻辑总结（下）",{"type":8,"value":9,"toc":368},"minimark",[10,14,17,22,25,28,31,36,39,72,75,78,81,84,90,93,96,101,104,107,111,114,118,121,124,135,138,144,147,150,153,159,162,165,168,171,174,177,181,184,187,190,196,199,205,208,212,215,218,221,224,227,230,233,236,328,331,335,338,341,344,347,350,353,356,359,362,365],[11,12,13],"p",{},"前面的内容都集中在C++端了，这边的内容会往Shader靠的更近一些。",[11,15,16],{},"由于感觉上很多东西要全部梳理明白会花费很多时间却又没有多大用处，所以这里的内容实际上进行了精简，因此比预定的短了很多。",[18,19,21],"h2",{"id":20},"shader端","Shader端",[11,23,24],{},"虚幻使用HLSL作为Shader的语言，引擎核心的Shader都可以在引擎目录的Shader文件夹下找到。",[11,26,27],{},"其中ush为Shader头文件，而usf为Shader的源文件。由于Shader部分的代码基本属于引擎渲染的核心部分，要全部理解起来就有些费时间了。",[11,29,30],{},"所以这里只是按照其与C++部分接洽的结构进行粗略的探索。",[32,33,35],"h3",{"id":34},"vertexfatory","VertexFatory",[11,37,38],{},"VertexFatory是C++端将数据推送到Shader端的途径，在Shader文件夹中能够看到以下的几个：",[40,41,42,45,48,51,54,57,60,63,66,69],"blockquote",{},[11,43,44],{},"VectorFieldVisualizationVertexFactory.ush",[11,46,47],{},"ParticleSpriteVertexFactory.ush",[11,49,50],{},"ParticleGPUSpriteVertexFactory.ush",[11,52,53],{},"ParticleBeamTrailVertexFactory.ush",[11,55,56],{},"NiagaraMeshVertexFactory.ush",[11,58,59],{},"NiagaraSpriteVertexFactory.ush",[11,61,62],{},"MeshParticleVertexFactory.ush",[11,64,65],{},"LocalVertexFactory.ush",[11,67,68],{},"LandscapeVertexFactory.ush",[11,70,71],{},"GpuSkinVertexFactory.ush",[11,73,74],{},"分别对应不同的使用情况，从命名上基本就能看出其用途。",[11,76,77],{},"Niagara是UE4的下一代粒子系统，目前版本可以在插件中打开，但是功能似乎并不完全，也没有文档，没有办法使用。",[11,79,80],{},"另外，这里的Shader与FVertexFatory并不是一一对应的关系。使用相同的渲染路径的类会在这里共用Shader。",[11,82,83],{},"如LocalVertexFactory.ush就被FLocalVertexFactory、FEmulatedInstancedStaticMeshVertexFactory、FInstancedStaticMeshVertexFactory、FGPUSkinPassthroughVertexFactory和FSplineMeshVertexFactory共同使用。",[11,85,86],{},[87,88,89],"strong",{},"FVertexFactoryInput",[11,91,92],{},"这个数据结构是来自C++端的数据输入。在不同的Shader头文件中可能会有不同的定义，使用方式上自然也会有不同。",[11,94,95],{},"也有一些通用的Shader函数被定义来处理输入数据，如GetVertexFactoryIntermediates, VertexFactoryGetWorldPosition, GetMaterialVertexParameters。",[11,97,98],{},[87,99,100],{},"FBasePassVSOutput",[11,102,103],{},"这个是另一个比较特殊的结构，由于不同的渲染路径可能在Vertex Shader结束后使用的路径是不同的。",[11,105,106],{},"所以也能看到对这个结构的不同定义。",[32,108,110],{"id":109},"material","Material",[11,112,113],{},"所有的材质最终都会被编译成Shader，在材质编辑器中也能够看到材质的Shader预览。",[115,116,117],"h4",{"id":117},"材质蓝图",[11,119,120],{},"用于将材质蓝图编译成Shader的模板在MaterialTemplate.ush中，查看这个文件的话，会看到有很多地方都是直接写成%s的。",[11,122,123],{},"这些都是由引擎将材质蓝图中的节点填充到这里的，例如",[125,126,131],"pre",{"className":127,"code":129,"language":130},[128],"language-text","\u002F**\n* Parameters calculated from the pixel material inputs.\n*\u002F\nstruct FPixelMaterialInputs\n{\n%s\n};\n","text",[132,133,129],"code",{"__ignoreMap":134},"",[11,136,137],{},"可能会被填写成",[125,139,142],{"className":140,"code":141,"language":130},[128],"\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",[132,143,141],{"__ignoreMap":134},[11,145,146],{},"想要详细的了解的话可以在材质编辑器中修改材质，然后预览HLSL代码并与MaterialTemplate.ush对比以了解更多的内部工作原理。",[115,148,149],{"id":149},"数据获取",[11,151,152],{},"在材质完成编译之后，渲染路径中就可以在需要时对材质中定义的相应的属性进行获取了。",[125,154,157],{"className":155,"code":156,"language":130},[128],"half3 BaseColor = GetMaterialBaseColor(PixelMaterialInputs);\nhalf  Metallic = GetMaterialMetallic(PixelMaterialInputs);\nhalf  Specular = GetMaterialSpecular(PixelMaterialInputs);\n",[132,158,156],{"__ignoreMap":134},[11,160,161],{},"不同的shader根据不同的渲染路径将数据最终填充到GBuffer中，以便进行进一步的计算。",[115,163,164],{"id":164},"计算",[11,166,167],{},"在GBuffer的生成过程前、过程中、过程后，都有很多复杂的计算。",[11,169,170],{},"这些过程包括各种裁剪、光照以及PostProcess，由于并非是要进行这些逻辑的修改或者扩展，便不再深究下去了。",[18,172,173],{"id":173},"渲染逻辑",[11,175,176],{},"有了渲染用的C++端和Shader端代码之后，终于可以开始进行渲染工作了。",[32,178,180],{"id":179},"fdeferredshadingscenerenderer","FDeferredShadingSceneRenderer",[11,182,183],{},"对于PC端的延迟渲染，最终负责进行渲染工作的就是这个类了。",[11,185,186],{},"渲染的调用来源为FRendererModule::BeginRenderingViewFamily，可以看到有些编辑器的缩略图也会调用这个函数进行渲染，和玩家看到的游戏界面有关的渲染调用来自UGameViewportClient::Draw。",[11,188,189],{},"BeginRenderingViewFamily这个函数内在进行一些渲染的准备后，将实际渲染的函数扔到渲染线程",[125,191,194],{"className":192,"code":193,"language":130},[128],"ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(\nFDrawSceneCommand,\nFSceneRenderer*,SceneRenderer,SceneRenderer,\n{\n  RenderViewFamily_RenderThread(RHICmdList, SceneRenderer);\n  FlushPendingDeleteRHIResources_RenderThread();\n});\n",[132,195,193],{"__ignoreMap":134},[11,197,198],{},"然后就实际执行渲染",[125,200,203],{"className":201,"code":202,"language":130},[128],"SceneRenderer->Render(RHICmdList);\n",[132,204,202],{"__ignoreMap":134},[11,206,207],{},"基本上渲染的主要逻辑就在这个函数中。",[32,209,211],{"id":210},"rendershadowdepthmaps","RenderShadowDepthMaps",[11,213,214],{},"这个是SceneRenderer->Render中对深度贴图生成，从中可以看出渲染是如何最终使用Shader的。",[11,216,217],{},"比较关键的调用之一是ProjectedShadowInfo->RenderDepth(RHICmdList, this, SetShadowRenderTargets, ShadowDepthRenderMode_Normal);",[11,219,220],{},"这个函数进一步调用FProjectedShadowInfo::RenderDepth并继而调用FProjectedShadowInfo::RenderDepthInner",[11,222,223],{},"而这之中会有SceneRenderer->Scene->WholeSceneReflectiveShadowMapDrawList.DrawVisible",[11,225,226],{},"而这个WholeSceneReflectiveShadowMapDrawList就是一张DrawingPolicy列表了，到了这里就能与Shader相关的类型联系上了。",[32,228,229],{"id":229},"渲染流程",[11,231,232],{},"关于渲染的流程，到了这一步其实就和之前看到的差不多了，因此这里不做赘述。",[11,234,235],{},"下面的内容直接引用自官方文档，所以不保证与当前版本的内容匹配：",[237,238,239,252],"table",{},[240,241,242],"thead",{},[243,244,245,249],"tr",{},[246,247,248],"th",{},"操作",[246,250,251],{},"描述",[253,254,255,264,272,280,288,296,304,312,320],"tbody",{},[243,256,257,261],{},[258,259,260],"td",{},"GSceneRenderTargets.Allocate",[258,262,263],{},"按需要重新分配全局场景渲染目标，使其对当前视图足够大。",[243,265,266,269],{},[258,267,268],{},"InitViews",[258,270,271],{},"‭通过多种剔除方法为视图初始化基元可见性，设立此帧可见的动态阴影、按需要交叉阴影视锥与世界场景（对整个场景的阴影或预阴影）。",[243,273,274,277],{},[258,275,276],{},"PrePass \u002F Depth only pass",[258,278,279],{},"RenderPrePass \u002F FDepthDrawingPolicy。渲染遮挡物，对景深缓冲区仅输出景深。该通道可以在多种模式下工作：禁用、仅遮蔽，或完全景深，具体取决于活动状态的功能的需要。该通道通常的用途是初始化 Hierarchical Z 以降低 Base 通道的着色消耗（Base 通道的像素着色器消耗非常大）。",[243,281,282,285],{},[258,283,284],{},"Base pass",[258,286,287],{},"RenderBasePass \u002F TBasePassDrawingPolicy。渲染不透明和遮盖的材质，向 GBuffer 输出材质属性。光照图贡献和天空光照也会在此计算并加入场景颜色。",[243,289,290,293],{},[258,291,292],{},"Issue Occlusion Queries \u002F BeginOcclusionTests",[258,294,295],{},"提出将用于下一帧的 InitViews 的延迟遮蔽查询。这会通过渲染所查询物体周围的相邻的框、有时还会将相邻的框组合在一起以减少绘制调用来完成。",[243,297,298,301],{},[258,299,300],{},"Lighting",[258,302,303],{},"阴影图将对各个光照渲染，光照贡献会累加到场景颜色，并使用标准延迟和平铺延迟着色。光照也会在透明光照体积中累加。",[243,305,306,309],{},[258,307,308],{},"Fog",[258,310,311],{},"雾和大气在延迟通道中对不透明表面进行逐个像素计算。",[243,313,314,317],{},[258,315,316],{},"Translucency",[258,318,319],{},"透明度累加到屏外渲染目标，在其中它应用了逐个顶点的雾化，因而可以整合到场景中。光照透明度在一个通道中计算最终光照以正确融合。",[243,321,322,325],{},[258,323,324],{},"Post Processing",[258,326,327],{},"多种后期处理效果均通过 GBuffers 应用。透明度将合成到场景中。",[11,329,330],{},"直接阅读FRendererModule::BeginRenderingViewFamily就可以看到UE4是如何对渲染通路进行处理的，其中有的较为简单的就会直接调用Shader进行处理，较为复杂的就会有相应的过程封装。",[32,332,334],{"id":333},"rendering-paths","Rendering paths",[11,336,337],{},"根据官方文档的描述，渲染路径分为Dymaic和Static两种。其中动态的速度会更慢些但是拥有更多的控制选项。",[11,339,340],{},"FPrimitiveSceneProxy会在GetViewRelevance中返回相关性标志，这样在渲染时引擎就会决定是否调用DrawDynamicElements和DrawStaticElements。",[11,342,343],{},"大致看来，static rendering path会在物体被加入FScene的时候，就将自己加入到绘制列表中。而dynamic rendering path由于可以做一些动态处理，并在DrawDynamicElements中提供了回调，所以就没有办法利用缓存机制了。",[11,345,346],{},"可以看到很多类似这样的调用",[11,348,349],{},"PrimitiveSceneInfo->Proxy->GetDynamicMeshElements(InViewFamily.Views, InViewFamily, ViewMask, Collector);",[11,351,352],{},"而SceneRender中也能看到GatherDynamicMeshElements这样的函数。",[11,354,355],{},"感觉上Static的渲染路径指的应该是FScene中大量存在的模板化的列表TStaticMeshDrawList。",[11,357,358],{},"不过由于这方面几乎找不到资料，文档中没有更加详细的说明，社区也基本看不到讨论，要从源码中回溯其意图就比较费时了，所以便没有进一步深究。",[18,360,361],{"id":361},"总结",[11,363,364],{},"到了这里，这个UE4的渲染逻辑就能有一个大致的草图了。",[11,366,367],{},"虽然其中还有更多的细节和详细是实现，也只有到了需要的时候再深入了解了。毕竟这部分已经是引擎开发者的工作，而太过于深入就没有意义了。",{"title":134,"searchDepth":369,"depth":370,"links":371},2,3,[372,381,387],{"id":20,"depth":369,"text":21,"children":373},[374,375],{"id":34,"depth":370,"text":35},{"id":109,"depth":370,"text":110,"children":376},[377,379,380],{"id":117,"depth":378,"text":117},4,{"id":149,"depth":378,"text":149},{"id":164,"depth":378,"text":164},{"id":173,"depth":369,"text":173,"children":382},[383,384,385,386],{"id":179,"depth":370,"text":180},{"id":210,"depth":370,"text":211},{"id":229,"depth":370,"text":229},{"id":333,"depth":370,"text":334},{"id":361,"depth":369,"text":361},"2018-01-02","md",{"layout":391,"status":392,"published":393,"author":394,"author_login":396,"author_email":397,"wordpress_id":398,"wordpress_url":399,"date_gmt":400,"excerpt":401},"post","publish",true,{"display_name":395,"login":396,"email":397,"url":134},"风铃","flinkor","flinkor@foxmail.com",2209,"\u002F\u002F?p=2209","2018-01-01 16:02:45 +0000",{"type":8,"value":402},[403],[11,404,13],{},"\u002F2018-01-02-ue4-rendering-code-view-02",{"title":6,"description":13},"_legacy\u002F2018\u002F2018-01-02-ue4-rendering-code-view-02",[409,410,411],"shader","UE4","Rendering","mZ30HsJT42pZP836dw4bOQTLX2RfTXQDvouudBp_UO8",{"id":414,"title":415,"body":416,"date":837,"description":420,"extension":389,"meta":838,"navigation":393,"path":847,"seo":848,"stem":849,"tags":850,"__hash__":851},"blogs\u002F_legacy\u002F2017\u002F2017-11-18-ue4-render-flow-overview.md","UE4 Render Flow纵览",{"type":8,"value":417,"toc":816},[418,421,424,427,430,437,440,448,452,455,458,464,467,471,474,477,485,488,492,495,498,501,504,507,510,513,516,519,523,526,529,532,539,542,546,549,552,555,561,564,570,573,576,580,583,586,589,595,598,601,604,607,610,616,619,622,627,630,633,637,640,644,647,650,656,659,662,668,671,674,677,680,683,686,690,693,696,699,703,706,709,729,732,736,739,742,745,748,751,757,760,763,766,769,772,775,779,782,787,791,794,800,803,806],[11,419,420],{},"渲染优化时质量和效率的平衡，虽然按照官方的建议进行相应的调整即可，但是不稍微了解其内部的原理的话还是有些许让人困惑的。",[11,422,423],{},"本文基于CEDEC2016的一篇讲稿，目标UE4版本为4.13。",[11,425,426],{},"由于到目前的版本（4.18）引擎渲染已经有了很大的变动，所以有的内容只有参考作用。",[11,428,429],{},"从概览的角度来看，UE4的渲染可以划分成以下的阶段：",[11,431,432],{},[433,434],"img",{"alt":435,"src":436},"clip_image001","\u002Fwp-content\u002Fuploads\u002F2017\u002F11\u002Fclip_image001_thumb-2.png",[11,438,439],{},"这个通路是针对延迟渲染的，与目前主要针对VR设备的前向渲染并不对应。",[11,441,442,447],{},[443,444,446],"a",{"href":445},"\u002F2017-07-30-ue4-profiling-preview\u002F#i-4","针对渲染通道的优化","虽然之前有做过总结，但是并没有详细的研究过各个通道在整体渲染中的地位。",[18,449,451],{"id":450},"base-pass","Base Pass",[11,453,454],{},"作为最重要的基础性通道，Base Pass运算的结果作为之后所有通道运算的基础。",[11,456,457],{},"基础通道里主要的可见操作是对Opaque\u002FMasked材质的物体进行的遮蔽运算并完成G-Buffer的生成，VS和PS也在这个阶段进行计算。",[11,459,460],{},[433,461],{"alt":462,"src":463},"image3","\u002Fwp-content\u002Fuploads\u002F2017\u002F11\u002Fimage3_thumb.png",[11,465,466],{},"针对Base Pass的优化方向主要针对Vertex Shader和Pixel Shader两个阶段，另外在材质的Shader制作本身上也需要注意降低运算量。",[32,468,470],{"id":469},"vertex-shader","Vertex Shader",[11,472,473],{},"顶点计算的优化主要就是一些通常的建议：对物体的Bound进行规划，不要让bound过大，避免使用覆盖视野前后的物体。以使得Culling能够在早期就剪除掉不需要的顶点计算。",[11,475,476],{},"在Console中可以使用一下命令辅助优化：",[40,478,479,482],{},[11,480,481],{},"Stat InitViews可以查看裁剪计算的效果",[11,483,484],{},"FreezeRendering可以冻结裁剪，对裁剪结果进行可视化分析",[11,486,487],{},"更进一步的可以根据平台的GPU特性不同进行对应的优化。",[32,489,491],{"id":490},"pixel-shader","Pixel Shader",[11,493,494],{},"由于Base Pass是后面所有通道的基础，所以会有较高的固有消耗。同时也是在场景中添加物品、Shader等产生性能消耗最直观的地方。",[11,496,497],{},"在理想的状态下，在没有Masked或者Translucent的情况，在PreZ阶段完成时就可以决定各个像素的深度并形成遮蔽计算了。在这种情况下，就可以极好的减小Piexel Shader阶段的运算量。但是实际上，为了场景中的特效质量，不能光依靠Opaque的材质，事情就没有那么简单了。",[11,499,500],{},"会导致PreZ完成时深度计算结果不完全的运算有，Masked材质的Alpha Test以及在Pixel Shader内部对深度数据的重写。因此需要有PostZ阶段对深度数据进行重新处理。",[11,502,503],{},"因此在这里容易形成两种造成性能影响的错误操作：在制作通用的材质时，明明有的不使用半透明蒙版通道情况却开启了半透明蒙版并往其上连接一个参数或者将参数连接到Piexel Depth Offset上。",[11,505,506],{},"由于PreZ和PostZ的决策是在GPU中完成的，无法在UE4中进行预览，因此在进行优化的时候要注意对上面的两种情况进行观察。",[11,508,509],{},"总体而言，作为G-Buffer的生成阶段，BassPass会直接的受到物体增加的影响，在添加物体时要注意检查以下两项：",[11,511,512],{},"Bounds的设置是否很好的完成了Culling。",[11,514,515],{},"Material的设置是否很好的避免了不必要的Pixel Shader计算。",[11,517,518],{},"另外，在项目设置中可以对G-Buffer的精度进行设定，对于需要高质量运算结果的情况或者想要降低性能消耗的情况，可以在这里进行调整。",[18,520,522],{"id":521},"z-prepass","Z PrePass",[11,524,525],{},"这是在BasePass之前尝试进行深度计算。经过Z PrePass计算之后，可以减少到达Vertex Shader的顶点数量，以提高效率。",[11,527,528],{},"在项目设置中可以对Early Z-Pass相关的选型进行调整。",[11,530,531],{},"对于单个物体，这里的Use as Occluder默认是开启的，将其去掉就不会参与Early Z-Pass的计算。这个大部分时间应该保持默认，让引擎自行决定是否让物体参与深度计算。",[11,533,534,535],{},"ll\n",[433,536],{"alt":537,"src":538},"clip_image0015","\u002Fwp-content\u002Fuploads\u002F2017\u002F11\u002Fclip_image0015_thumb-1.png",[11,540,541],{},"早期深度计算是为了减少Base Pass的运算负荷而存在的，但有时场景中物体布局可能会导致这个阶段形成瓶颈，可以在这里针对物体进行开启关闭来调整效果。",[18,543,545],{"id":544},"custom-depthstencil","Custom Depth\u002FStencil",[11,547,548],{},"在BasePass之后有一个可以自行进行定义的阶段，就是自定义深度。",[11,550,551],{},"自定义深度可以使得用户为物体在渲染时额外的生成一张深度贴图，可以很好的对需要的物体进行裁剪。",[11,553,554],{},"在项目设置中开启自定义深度",[11,556,557],{},[433,558],{"alt":559,"src":560},"clip_image002","\u002Fwp-content\u002Fuploads\u002F2017\u002F11\u002Fclip_image002_thumb-2.png",[11,562,563],{},"然后在需要自定义深度的物体上打开",[11,565,566],{},[433,567],{"alt":568,"src":569},"clip_image003","\u002Fwp-content\u002Fuploads\u002F2017\u002F11\u002Fclip_image003_thumb-2.png",[11,571,572],{},"需要注意的是，由于是额外进行的深度计算，基本上等同于对Base Pass进行了一次重新计算，自定义深度在场景较大的开放世界或者物体较多的场景中尤其会造成大的性能损失。",[11,574,575],{},"因此虽然使用Custom Depth可以相对简单的实现一些效果，但是却是以性能为代价的，应当尽量避免使用这个思路，如果非用不可的话，需要进行更加严格的Profiling。",[18,577,579],{"id":578},"pre-lighting","Pre-Lighting",[11,581,582],{},"这是光照计算之前的一个运算阶段，主要的作用是Decal和AO的计算。",[11,584,585],{},"在过去的版本中Decal经常与光照计算产生冲突，造成一些奇特的明显不符合预期的最终结果。",[11,587,588],{},"因此后来加入了",[11,590,591],{},[433,592],{"alt":593,"src":594},"clip_image0019","\u002Fwp-content\u002Fuploads\u002F2017\u002F11\u002Fclip_image0019_thumb.png",[11,596,597],{},"的选项，目前的引擎中是默认开启的，无需太多关心。由于之前的项目中Decal的使用似乎没有遇到过什么问题，想来目前的默认选项已经很好的解决了问题。",[11,599,600],{},"如果在Decal的使用过程中遇到了问题，可以针对性的进行搜索。",[18,602,300],{"id":603},"lighting",[11,605,606],{},"就是光照计算阶段，光照的优化其实能找到很多资料。光照在UE4的操作上分为三种，StaticLight是全静态光照，全部使用预计算的结果进行光照。而Movable的光照则是全动态的，所有的光照都在运行时进行计算。Stational的光照则介于两者之间，静态物体的阴影会在预计算阶段进行缓存。",[11,608,609],{},"另外Stationary Light有同一个区域只受5个光照作用的限制，多出来的范围最小的那个会变成红叉叉，变成动态光照，在使用时需要注意。在视图选项中可以使用",[11,611,612],{},[433,613],{"alt":614,"src":615},"image","\u002Fwp-content\u002Fuploads\u002F2017\u002F11\u002Fimage_thumb.png",[11,617,618],{},"来对整体场景进行排查。",[11,620,621],{},"在动态光照的优化上，动态光照是重叠的越多性能消耗就越高的，相反的个数很多却相互不重叠的话光照复杂度的上升却不是很快。可以在编辑器中使用光照复杂度视图进行确认和优化。",[11,623,624],{},[433,625],{"alt":614,"src":626},"\u002Fwp-content\u002Fuploads\u002F2017\u002F11\u002Fimage_thumb-1.png",[11,628,629],{},"还有一点是，静态光照并不是在运行时完全没有性能消耗。静态光照在运行时InitDynamic Setup计算阶段是会造成CPU消耗的，因此并不是由于是预计算的就可以无计划的放置。另外，据说StaticLight在被移动等时会自动的被变更为Movable的，没有进行过测试所以并不是很确定呢。",[11,631,632],{},"总之在进行光照布局时，首先使用Stationary是比较合理的策略。",[18,634,636],{"id":635},"reflect","Reflect",[11,638,639],{},"反射计算虽然在概念上算是光照的一部分，但是其实在运算中是一个额外的阶段。",[32,641,643],{"id":642},"reflection-probe","Reflection Probe",[11,645,646],{},"反射捕获，是预计算的反射。在引擎中提供了球体反射捕获和盒体反射捕获两个选择，在使用反射捕获时，可以在拖入后对所在区域进行手动的重新捕获，也可以自己在其中指定CubeMap。",[11,648,649],{},"在项目设置中可以进行设置来调整反射捕获的精度。",[11,651,652],{},[433,653],{"alt":654,"src":655},"clip_image00111","\u002Fwp-content\u002Fuploads\u002F2017\u002F11\u002Fclip_image00111_thumb.png",[11,657,658],{},"这部分的消耗是在Reflection Environment Compute Shader XXXX中反映的，与动态光照相同，个数对其性能消耗的影响不如区域重叠造成的影响。",[11,660,661],{},"官方的建议是，在场景全体放置一个总的反射捕捉，然后在一个单位房间内放一个整合性的捕捉，最后在反射性的物体上针对性的放置。",[11,663,664],{},[433,665],{"alt":666,"src":667},"clip_image0024","\u002Fwp-content\u002Fuploads\u002F2017\u002F11\u002Fclip_image0024_thumb-2.png",[11,669,670],{},"覆盖全体的捕捉",[11,672,673],{},"防止在场景内完全丢失反射信息的情况",[11,675,676],{},"房间单位的捕捉",[11,678,679],{},"在场景单元内形成详细的反射信息",[11,681,682],{},"物体单位的捕捉",[11,684,685],{},"反射要求较高的物体附近进行更加详细的捕捉",[32,687,689],{"id":688},"screen-space-reflection","Screen Space Reflection",[11,691,692],{},"动态反射计算，没有深入看过其实现。",[11,694,695],{},"这里的主要问题是，由于是在屏幕空间内进行的计算，在屏幕外的反射无法正确的反映，同时有较多的噪点而且对Translucent的材质在计算时容易出现问题。",[11,697,698],{},"因此通常是与上面的反射捕获共同使用，作为其补充而存在的。因此如果出现了反射表面投影质量比较奇怪的问题，通常也可以检查一下是否该区域没有放置反射捕获，而不是一味的去加强动态光照和间接光照的次数，毕竟他们的性能消耗还是非常可观的。",[32,700,702],{"id":701},"planar-reflection","Planar Reflection",[11,704,705],{},"效果很好的反射，全动态计算。",[11,707,708],{},"但是其负荷相当的高，如果场景中有两个以上的话，会有目视可见的性能消耗。",[40,710,711,714,717,720,723,726],{},[11,712,713],{},"无法控制反射通道中启用的渲染功能。",[11,715,716],{},"反射通道中的动态阴影不正确。",[11,718,719],{},"为保证达到目标帧率，需计算资源是否足以使用平面反射。",[11,721,722],{},"只支持恒定的粗糙系数，其在平面反射组件上（而非在材质上）进行指定。",[11,724,725],{},"如可能，须尽量将世界场景中的平面反射 Actor 数量限制为 1 个，将其移动、旋转、缩放，和世界场景搭配。也可使用多个平面反射 Actor，但需多加注意，因为平面反射 Actor 不执行任何距离剔除，只进行视锥和遮蔽剔除。因此，如果画面中同时存在两个平面反射 Actor，项目的帧率将受到严重影响。",[11,727,728],{},"渲染平面反射 Actor 的开销直接来自当前关卡中渲染的内容。启用此功能后，由三角形组成、绘制调用较大的场景将遭受严重的性能影响，因为这些开销不会随屏幕百分比变化。",[11,730,731],{},"以上内容引用自官方文档，在使用时需要额外的进行留意。",[18,733,735],{"id":734},"translucent","Translucent",[11,737,738],{},"由于深度计算的效率等问题，Translucent单独在另一条路径上进行处理。因此在半透明的计算在延迟渲染中，总是会有很多的问题。",[32,740,741],{"id":741},"深度计算",[11,743,744],{},"将半透明的粒子投放到场景中时，可以看到并不会在深度数据中产生影响。这样在一些使用深度数据进行的效果如DOF中就会出现BUG。",[11,746,747],{},"因此UE4使用Separate Translucency来对半透明物体的深度进行处理",[11,749,750],{},"在项目设置中可以看到开关",[11,752,753],{},[433,754],{"alt":755,"src":756},"clip_image00113","\u002Fwp-content\u002Fuploads\u002F2017\u002F11\u002Fclip_image00113_thumb.png",[11,758,759],{},"关于DepthOfField,在材质中有其运算结果的节点。",[32,761,762],{"id":762},"成本问题",[11,764,765],{},"半透明物体会极大的加重场景的渲染负担，在着色器复杂度中能够看到，通常半透明的粒子会导致复杂度变为红色。",[11,767,768],{},"优化上可以考虑降低Separate Translucency的分辨率，使用r.SeparateTranslucencyScreenPercentage指令可以通过降低分辨率来减小其消耗。",[11,770,771],{},"另一个解决方案是使用Particle CutOut有效的减少半透明计算的区域。",[11,773,774],{},"似乎只要使用Create SubUV Animation就会自动应用，没有测试所以并不清楚。",[32,776,778],{"id":777},"responsive-aa","Responsive AA",[11,780,781],{},"在半透明材质中可以看到这个选项，主要是针对使用了半透明材质的粒子的。",[11,783,784],{},[433,785],{"alt":614,"src":786},"\u002Fwp-content\u002Fuploads\u002F2017\u002F11\u002Fimage_thumb-2.png",[18,788,790],{"id":789},"post-process","Post Process",[11,792,793],{},"pp是渲染的最后一个阶段，可以在这里对渲染结果进行进一步的加工。",[11,795,796],{},[433,797],{"alt":798,"src":799},"clip_image0026","\u002Fwp-content\u002Fuploads\u002F2017\u002F11\u002Fclip_image0026_thumb-1.png",[11,801,802],{},"PostProcess的成本消耗与添加的特效相关，每一个特效都会产生额外的消耗。如果自己使用了pp的材质来进行控制的话，其Shader复杂度也会对性能产生影响。",[11,804,805],{},"自带的PP特效可以通过指令来调整其效果，例如r.BloomQuality，其运算负荷是作为PostProcessWeightedSampleSum显示。",[11,807,808,809,815],{},"UE4的后期处理能在官方找到很多详细的[",[443,810,814],{"href":811,"rel":812},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FEngine\u002FRendering\u002FPostProcessEffects\u002Findex.html",[813],"nofollow","文档","]。",{"title":134,"searchDepth":369,"depth":370,"links":817},[818,822,823,824,825,826,831,836],{"id":450,"depth":369,"text":451,"children":819},[820,821],{"id":469,"depth":370,"text":470},{"id":490,"depth":370,"text":491},{"id":521,"depth":369,"text":522},{"id":544,"depth":369,"text":545},{"id":578,"depth":369,"text":579},{"id":603,"depth":369,"text":300},{"id":635,"depth":369,"text":636,"children":827},[828,829,830],{"id":642,"depth":370,"text":643},{"id":688,"depth":370,"text":689},{"id":701,"depth":370,"text":702},{"id":734,"depth":369,"text":735,"children":832},[833,834,835],{"id":741,"depth":370,"text":741},{"id":762,"depth":370,"text":762},{"id":777,"depth":370,"text":778},{"id":789,"depth":369,"text":790},"2017-11-18",{"layout":391,"status":392,"published":393,"author":839,"author_login":396,"author_email":397,"wordpress_id":840,"wordpress_url":841,"date_gmt":842,"excerpt":843},{"display_name":395,"login":396,"email":397,"url":134},2126,"\u002F\u002F?p=2126","2017-11-18 09:45:59 +0000",{"type":8,"value":844},[845],[11,846,420],{},"\u002F2017-11-18-ue4-render-flow-overview",{"title":415,"description":420},"_legacy\u002F2017\u002F2017-11-18-ue4-render-flow-overview",[410,411],"FhVs1eAqGGNQqiSWGnuupyS0M44voIuEFC_oLNsA6WU",{"id":853,"title":854,"body":855,"date":1594,"description":859,"extension":389,"meta":1595,"navigation":393,"path":1604,"seo":1605,"stem":1606,"tags":1607,"__hash__":1608},"blogs\u002F_legacy\u002F2017\u002F2017-08-23-ue4-lighting-and-optimize.md","UE4光照及优化",{"type":8,"value":856,"toc":1554},[857,860,863,884,887,890,893,898,902,905,908,911,915,918,923,926,929,934,937,940,943,946,949,952,955,958,963,966,969,972,975,983,986,991,994,999,1002,1006,1009,1014,1017,1022,1025,1028,1031,1035,1038,1043,1046,1049,1054,1057,1060,1064,1067,1070,1073,1076,1081,1084,1089,1093,1096,1101,1104,1107,1110,1114,1117,1121,1124,1127,1130,1138,1141,1144,1149,1152,1156,1159,1162,1165,1171,1174,1179,1182,1187,1190,1195,1198,1202,1205,1208,1213,1216,1219,1223,1226,1231,1234,1239,1242,1247,1250,1254,1263,1266,1271,1274,1277,1280,1283,1287,1290,1296,1299,1302,1307,1310,1313,1317,1320,1323,1328,1332,1335,1340,1343,1347,1350,1355,1358,1362,1365,1370,1373,1376,1379,1384,1388,1391,1396,1399,1402,1405,1409,1412,1417,1420,1424,1427,1432,1435,1439,1442,1445,1453,1457,1460,1463,1466,1469,1474,1477,1480,1485,1488,1491,1495,1498,1501,1506,1509,1512,1515,1518,1522,1525,1530,1533,1541,1546,1549,1551],[11,858,859],{},"UE4的光照系统很强大，效果也很好，但是在场景复杂度较高或者开放世界的情况下，很容易造成性能瓶颈。",[11,861,862],{},"当前UE4版本为4.17.1。",[11,864,865,866,871,872,877,878,883],{},"光照系统的大部分应用都可以在[",[443,867,870],{"href":868,"rel":869},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FEngine\u002FRendering\u002FLightingAndShadows\u002Findex.html",[813],"官方文档","]中找到，这里的大部分内容是对官方Lighting系列[",[443,873,876],{"href":874,"rel":875},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=jCsrWzt9F28",[813],"视频1","][",[443,879,882],{"href":880,"rel":881},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=nm1slxtF_qA",[813],"视频2","]的总结。",[18,885,886],{"id":886},"静态光照",[11,888,889],{},"UE4的静态光照是在光照构建中进行预计算的部分，会对预计算的光照结果进行存储，例如光照贴图、阴影贴图这样的形式，可以在运行时支付较低的效率而获得较好的光照结果。",[11,891,892],{},"静态光照可以在世界设置中可以进行关闭",[11,894,895],{},[433,896],{"alt":614,"src":897},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-7.png",[32,899,901],{"id":900},"lightmass-importance-volume","Lightmass Importance Volume",[11,903,904],{},"这个是预计算光照用的Volume，LightMass的光照计算在点击“构建光照”之后会产生光照贴图。",[11,906,907],{},"控制良好的预计算光照可以让场景变得美观的同时降低动态光照的成本，但是在控制不好的时候就会造成贴图空间爆炸。",[11,909,910],{},"LightMass Importance Volume是用来控制预计算精度的，很明显的一点是，加上之后在空间内部会生成更多的间接光照缓存点，使得间接光照的效果变得更好。",[32,912,914],{"id":913},"ssao","SSAO",[11,916,917],{},"世界设定中还有一个LightMass相关的功能",[11,919,920],{},[433,921],{"alt":614,"src":922},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-8.png",[11,924,925],{},"LightMass这里的AO就是SSAO，由于是工作在屏幕空间的后期计算，要进行探索的话可以在PostProcess中进行调整测试。",[11,927,928],{},"还有一个额外的选项",[11,930,931],{},[433,932],{"alt":614,"src":933},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-9.png",[11,935,936],{},"打开之后可以使得PrecompoutedAOMask材质节点变为有效，这样就可以使用AO的数据来对场景内物体之间的混合效果进行控制。",[11,938,939],{},"详细的可以看这个选项的注释。",[11,941,942],{},"更正：",[11,944,945],{},"这里是错误的，LightMass选项里的AO选项是给预计算光照的，Generate Ambient Occlusion Material这个选项打开的PrecompoutedAOMask也是预计算光照的结果反映到材质中的方式。SSAO是工作在屏幕空间的一种PostProcess效果，与预计算光照是不同的。",[11,947,948],{},"感谢@裕 的指正~",[32,950,951],{"id":951},"间接光照缓存",[11,953,954],{},"间接光照缓存的作用是通过缓存某个点的间接光照，将其作用于经过该处的动态物体上，以获得好的间接光照效果。",[11,956,957],{},"在Movable的物体以及角色上可以对间接光照的计算类型进行设定",[11,959,960],{},[433,961],{"alt":614,"src":962},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-10.png",[11,964,965],{},"ILCQ为Point的时候就是一个点，Volume就是很多点。",[11,967,968],{},"在物体移动时，这些生成的点将根据自己所处的位置，通过周围的间接光照缓存点进行插值并计算出间接光照。",[11,970,971],{},"虽然插值点多的话间接光照效果会变好，但使用何种形式需要根据性能和需求进行权衡。",[11,973,974],{},"可以使用",[40,976,977,980],{},[11,978,979],{},"r.cache.drawinterplotionpoint 1",[11,981,982],{},"r.cache.updateeveryframe 1",[11,984,985],{},"这两条指令来预览缓存插值点的生成。",[11,987,988],{},[433,989],{"alt":614,"src":990},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-11.png",[11,992,993],{},"对于角色的光照缓存还有一个另外的",[11,995,996],{},[433,997],{"alt":614,"src":998},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-12.png",[11,1000,1001],{},"用于提高区域计算精度的体积。",[32,1003,1005],{"id":1004},"capsule-shadows","Capsule Shadows",[11,1007,1008],{},"这个是专门为骨骼模型设计的投影优化方式，在骨骼的Lighting属性中能够看到相关的选项：",[11,1010,1011],{},[433,1012],{"alt":614,"src":1013},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-13.png",[11,1015,1016],{},"要使用必须在骨骼网格中指定用于投影的Physics Asset。",[11,1018,1019],{},[433,1020],{"alt":614,"src":1021},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-14.png",[11,1023,1024],{},"使用这种方式可以防止一般较为复杂的角色模型在进行投影计算时产生过多的消耗，尤其是同一个场景内有很多角色模型的情况。",[18,1026,1027],{"id":1027},"光照问题",[11,1029,1030],{},"在光照的结果上，有时会出现并非预期的结果。在进行场景构建、模型制作时需要预先做好一些预防工作。",[32,1032,1034],{"id":1033},"indirect-seams","Indirect Seams",[11,1036,1037],{},"间接光照的运算结果在模型之间的接缝处会出现不自然的裂缝",[11,1039,1040],{},[433,1041],{"alt":614,"src":1042},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-15.png",[11,1044,1045],{},"这种的主要原因是两个Mesh之间虽然是平滑的，但是在间接光照进行阴影计算时并不知道这些信息。",[11,1047,1048],{},"可以通过在世界设置中调节间接光照的质量和平滑度来减少这种现象",[11,1050,1051],{},[433,1052],{"alt":614,"src":1053},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-16.png",[11,1055,1056],{},"提高间接光照质量会加重光照构建的成本，而如果过于提高平滑度的话，会导致间接光照的很多细节被丢弃。",[11,1058,1059],{},"所以一个更好的解决方案是，在构建关卡时，如果是一个平滑的面的话就直接使用一个整体的模型来做，而不是用好几个模型拼接而成。",[32,1061,1063],{"id":1062},"uv-seams","UV Seams",[11,1065,1066],{},"这个是由于模型的UV没有很好的接合造成的，由于邻近的顶点在UV上并不连接，在进行间接光照计算时，产生的结果就没有办法很好的利用这些信息。",[11,1068,1069],{},"根据官方的说明，提高间接光照质量并不会解决UV Seams的问题。",[11,1071,1072],{},"这个问题更多的是在建模上进行解决。",[11,1074,1075],{},"不要生成这样的UV",[11,1077,1078],{},[433,1079],{"alt":614,"src":1080},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-17.png",[11,1082,1083],{},"而是尽量保持邻接信息",[11,1085,1086],{},[433,1087],{"alt":614,"src":1088},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-18.png",[32,1090,1092],{"id":1091},"bleeding","Bleeding",[11,1094,1095],{},"光照泄露的主要原因是光照贴图的分辨率造成的",[11,1097,1098],{},[433,1099],{"alt":614,"src":1100},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-19.png",[11,1102,1103],{},"像这样室外的光照感觉上就像直接透过到了室内，显然不符合预期。",[11,1105,1106],{},"虽然通过修改光照贴图的分辨率来进行应对，但是这样就相当于绕过了问题的来源。",[11,1108,1109],{},"更根本的解决方法是，让“地板”与房间的尺寸匹配，这样在光照计算时，房间的地板就不会接收到外部的光的光照计算。",[18,1111,1113],{"id":1112},"tips","TIPS",[11,1115,1116],{},"UE4的光照系统中有一些通用的工具和功能，可以方便的对光照进行布置。",[32,1118,1120],{"id":1119},"lpv","LPV",[11,1122,1123],{},"Light Propagation Volumes目前处于开发阶段，但是在光照相关的属性的很多部分都能看到它的对应属性。",[11,1125,1126],{},"这个体积主要的作用是，在动态光照中对间接光照之类的效果进行光线传播运算。",[11,1128,1129],{},"功能上非常的有用，因为有时候确实会需要在动态光照中有间接光照这样的效果来加强场景的真实性。",[11,1131,1132,1133,815],{},"更多的内容可以参考官方的[",[443,1134,1137],{"href":1135,"rel":1136},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FEngine\u002FRendering\u002FLightingAndShadows\u002FLightPropagationVolumes\u002Findex.html",[813],"LPV文档",[11,1139,1140],{},"这里需要注意的是LPV虽然从命名上看起来像是一个体积控件，但是其实并不存在这样一个Volume，其属性是在PostProcess中进行修改的。",[11,1142,1143],{},"在开启之后可以在",[11,1145,1146],{},[433,1147],{"alt":614,"src":1148},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-20.png",[11,1150,1151],{},"打开LPV的属性可视化。",[32,1153,1155],{"id":1154},"csm","CSM",[11,1157,1158],{},"Cascade Shadow Map是UE4阴影贴图的使用方式，在Far Shadow、Dynamic Shadow和Static Shadow中使用了这个机制。",[11,1160,1161],{},"这个机制的主要作用是，在不同的距离层级，使用不同精度的阴影贴图。",[11,1163,1164],{},"在设定CSM的同时，还可以对过渡效果进行调节",[11,1166,1167],{},[433,1168],{"alt":1169,"src":1170},"clip_image00115","\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fclip_image00115_thumb.png",[11,1172,1173],{},"对应的CSM数量被设置为0的话，就相当于关闭了相应的阴影类型。",[11,1175,1176],{},[433,1177],{"alt":614,"src":1178},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-21.png",[11,1180,1181],{},"CSM的计算范围可以通过在显示中打开",[11,1183,1184],{},[433,1185],{"alt":614,"src":1186},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-22.png",[11,1188,1189],{},"这个选项来进行预览",[11,1191,1192],{},[433,1193],{"alt":614,"src":1194},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-23.png",[11,1196,1197],{},"类似这样，可以在调节相关属性的时候有一个好的可视化工具。",[32,1199,1201],{"id":1200},"emissive-material","Emissive Material",[11,1203,1204],{},"自发光颜色的材质是通过HDR来实现泛光效果的，因此它本身并不参与光照运算。",[11,1206,1207],{},"通过在使用了自发光颜色材质的物体上打开",[11,1209,1210],{},[433,1211],{"alt":614,"src":1212},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-24.png",[11,1214,1215],{},"可以让其能够照亮周围环境，但仅限于静态光照。当物体是Movable时，没有办法开启这个选项。",[11,1217,1218],{},"视频中实现的类型于动态照亮的效果是通过在物体上绑定一个改变GI的PP来实现的，然后两个PP之间的Blending就会改变空间内的灯光造成的影响，形成类似于被物体本身照亮的效果。",[32,1220,1222],{"id":1221},"error-coloring","Error Coloring",[11,1224,1225],{},"错误着色可以用于排查静态光照计算时报出的UV方面的错误，因为光照构建时只是提示物体上有UV的Overlapping和Wrapping有时候还是很难找到对应的问题的，尤其是模型并不是自己构建的情况下。",[11,1227,1228],{},[433,1229],{"alt":435,"src":1230},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fclip_image001_thumb.png",[11,1232,1233],{},"打开这个选项之后要将光照质量调整为预览，才能看到错误着色。",[11,1235,1236],{},[433,1237],{"alt":537,"src":1238},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fclip_image0015_thumb.png",[11,1240,1241],{},"重新构建一次光照，就能看到",[11,1243,1244],{},[433,1245],{"alt":614,"src":1246},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-25.png",[11,1248,1249],{},"橙色的部分是Overlapping而绿色的部分是Wrapping。",[32,1251,1253],{"id":1252},"volumetric-lighting","Volumetric Lighting",[11,1255,1256,1257,1262],{},"这是类似于聚光灯的[",[443,1258,1261],{"href":1259,"rel":1260},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FEngine\u002FRendering\u002FLightingAndShadows\u002FLightShafts\u002Findex.html",[813],"Light Shaft","]的效果，官方的Blueprint示例工程中也有类似的名为GodRay的效果实现。",[11,1264,1265],{},"在引擎内容中搜索LightBeam就能找到",[11,1267,1268],{},[433,1269],{"alt":614,"src":1270},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-26.png",[11,1272,1273],{},"这里的模拟方式是在类似光束效果的模型上贴上一个Translucent贴图。",[11,1275,1276],{},"虽然半透明材质的消耗比普通的材质高，但是在实现光束效果上却是很高的成本节约。",[18,1278,1279],{"id":1279},"动态光照",[11,1281,1282],{},"动态光照是实时计算的光照，如果关闭了光照预计算的话，所有的光照计算都是实时生成的。如果没有启用LPV的话，静态光照中的间接光照缓存无法使用的情况下，有时候也会造成画面质量的下降。",[32,1284,1286],{"id":1285},"distance-filed","Distance Filed",[11,1288,1289],{},"距离场在动态光照的优化和填补中有相当大的作用，距离场的基本原理就是在模型的周围向外扩散而成的距离场。",[11,1291,1292],{},[433,1293],{"alt":1294,"src":1295},"clip_image0017","\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fclip_image0017_thumb.png",[11,1297,1298],{},"距离场的生成需要在项目设置中进行开启，虽然距离场生成后会产生额外的存储成本，但是可以在动态光照优化中起到很大的作用。",[11,1300,1301],{},"利用距离场生成的阴影的成本会比动态光照的阴影低很多，通常情况下，将动态光照的作用范围调小",[11,1303,1304],{},[433,1305],{"alt":614,"src":1306},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-27.png",[11,1308,1309],{},"然后在基本上看不到阴影差别的距离上使用距离场生成的阴影进行替代可以很好的降低动态光照对场景的消耗。",[11,1311,1312],{},"事实是，大部分情况下，动态光照的阴影和距离场的阴影，在中距离上基本上没有什么太大的差别。",[115,1314,1316],{"id":1315},"distance-field-resolution","Distance field resolution",[11,1318,1319],{},"Distance field resolution这个属性是用于调整距离场的精细度的，在Mesh自己的设置里面可以找到。",[11,1321,1322],{},"稍微增加一点就会有明显的提升，重要的是平衡的选择，远距离物体完全没有必要性。",[11,1324,1325],{},[433,1326],{"alt":593,"src":1327},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fclip_image0019_thumb.png",[115,1329,1331],{"id":1330},"raytraced-distahcefield-shadows","RayTraced DistahceField Shadows",[11,1333,1334],{},"对于需要柔和的渐变光照的情况，可以使用",[11,1336,1337],{},[433,1338],{"alt":559,"src":1339},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fclip_image002_thumb.png",[11,1341,1342],{},"打开只有在下面的属性可以调节因距离而变化的边缘阴影的效果。",[115,1344,1346],{"id":1345},"selfshadow","SelfShadow",[11,1348,1349],{},"当物体的自身投影出问题的时候可以试着调节",[11,1351,1352],{},[433,1353],{"alt":568,"src":1354},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fclip_image003_thumb.png",[11,1356,1357],{},"来进行修正。",[115,1359,1361],{"id":1360},"two-sided-distance-field-generation","Two-Sided Distance Field Generation",[11,1363,1364],{},"一般在Foliage上会使用到的属性，如果觉得生成的DF数据形成的投影不够浓密的话，可以打开这个选项。",[11,1366,1367],{},[433,1368],{"alt":614,"src":1369},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-28.png",[11,1371,1372],{},"双面计算会产生额外的成本，在有必要的情况下使用即可，不过也可以考虑使用下面的替代物品。",[115,1374,1375],{"id":1375},"距离场替代物品",[11,1377,1378],{},"使用一个替代的物品来计算距离场，通常也是在Foliage中使用到，可以降低树的面多造成的性能影响。",[11,1380,1381],{},[433,1382],{"alt":614,"src":1383},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-29.png",[32,1385,1387],{"id":1386},"contact-shadow","Contact Shadow",[11,1389,1390],{},"这个是Light Source中的属性",[11,1392,1393],{},[433,1394],{"alt":654,"src":1395},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fclip_image00111_thumb.png",[11,1397,1398],{},"设置成0以上的值的话就会进行接触阴影的计算，这个计算时工作在屏幕空间的。",[11,1400,1401],{},"由于是工作在屏幕空间，在物体距离较远时阴影的损失也不会变的严重。",[11,1403,1404],{},"可以用于填补默认的级联阴影的距离损失，也就是说，可以调低级联阴影，降低阴影消耗。",[32,1406,1408],{"id":1407},"light-function","Light Function",[11,1410,1411],{},"光照函数相对阴影计算而言的成本是很低的，所以在实现云的投影的时候通常会使用光照函数。",[11,1413,1414],{},[433,1415],{"alt":614,"src":1416},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-30.png",[11,1418,1419],{},"官方的优化建议中也有说明，如果多个组合光源的效果可以用光照函数来替代的话，可以很好的提高效率。",[32,1421,1423],{"id":1422},"distance-filed-ao","Distance Filed AO",[11,1425,1426],{},"这个是天空光特有的功能，天空光可以利用距离场的生成数据进行环境光的投影计算",[11,1428,1429],{},[433,1430],{"alt":755,"src":1431},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fclip_image00113_thumb.png",[11,1433,1434],{},"只有当天空光是可移动的时候才能使用。",[32,1436,1438],{"id":1437},"ies","IES",[11,1440,1441],{},"IES的成本比光照函数还低一些，可以实现一些低成本的投影效果。",[11,1443,1444],{},"有助于关闭不必要的光照投影，例如只是用于模拟灯罩、遮挡的投影，或者干脆是车灯这样的效果。",[11,1446,1447,1448,815],{},"IES的使用可以参照官方的[",[443,1449,1452],{"href":1450,"rel":1451},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FEngine\u002FRendering\u002FLightingAndShadows\u002FIESLightProfiles\u002Findex.html",[813],"IES文档",[32,1454,1456],{"id":1455},"far-shadow","Far Shadow",[11,1458,1459],{},"这是与通常的静态光照和动态光照相反的一种阴影模式，主要是为了保持在远景上的投影来使得场景看起来更有真实感。",[11,1461,1462],{},"通常在Landscape上开启使用，对于需要的物体，也可以开启使用。注意，要在对应的光源上也开启Far Shadow才行。",[32,1464,1465],{"id":1465},"设置",[11,1467,1468],{},"在项目设置中个，有一个关闭静态光照的总开关",[11,1470,1471],{},[433,1472],{"alt":614,"src":1473},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-31.png",[11,1475,1476],{},"不过通常在对应的地图的世界设置中进行关闭更加具有可控制性一些。",[11,1478,1479],{},"还有针对移动端CSM的专有设定",[11,1481,1482],{},[433,1483],{"alt":614,"src":1484},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-32.png",[18,1486,1487],{"id":1487},"优化",[11,1489,1490],{},"UE4提供了一些用于场景优化的工具，像是LOD和Mipmap这样的机制在场景优化的时候就比较实用。",[32,1492,1494],{"id":1493},"cull-distance-volume","Cull Distance Volume",[11,1496,1497],{},"这个是一个用于裁剪的体积，其内部的物体在远离玩家的时候回直接被裁剪掉，不参与渲染。",[11,1499,1500],{},"由于动态光照的性能消耗与参与计算的面数成比例，这样就可以起到一定的优化作用了，思路基本和LOD相同。",[11,1502,1503],{},[433,1504],{"alt":614,"src":1505},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-33.png",[11,1507,1508],{},"CullDistance的属性比较简单，每个数组的元素表示的是物体大小小于该值的，应该在里玩家多少距离处隐藏。这里的数据虽然会被按照顺序使用，但是实际上打乱顺序也没有关闭。",[11,1510,1511],{},"不过有两个问题需要留意",[11,1513,1514],{},"首先，必须在PIE以上的状态Cull Distance Volume才会起作用。在编辑器内必须按G进入游戏预览模式才能看到效果。",[11,1516,1517],{},"然后，必须有一个cull distance为0的设置赋给一个大的size，如上面的(1000,0)，否则当距离到达最远的Distance时，会裁剪掉内部的所有物体，包括天空球、大气雾这些实际上没有Size的物体。",[32,1519,1521],{"id":1520},"foliage-culling","Foliage Culling",[11,1523,1524],{},"Foliage的Culling属性可以在每一个独立的Foliage上进行设置",[11,1526,1527],{},[433,1528],{"alt":614,"src":1529},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-34.png",[11,1531,1532],{},"如上面的Cull Distance设定，在没有使用PerinstanceFadeAmount的情况下，采用的裁剪距离时1000。",[11,1534,1535,1536,1540],{},"从500",[1537,1538,1539],"del",{},"1000的范围内PerInstanceFadeAmount由1","0进行变化，这样就可以在材质中对Foliage进行一个Fade的过程，而不是直接突兀的从边界消失。",[11,1542,1543],{},[433,1544],{"alt":614,"src":1545},"\u002Fwp-content\u002Fuploads\u002F2017\u002F08\u002Fimage_thumb-35.png",[11,1547,1548],{},"使用PerInstanceFadeAmount配合Translucent的Mask替代方案DitherTemporalAA，可以实现较好的过渡效果。",[18,1550,361],{"id":361},[11,1552,1553],{},"光照是渲染中的一个很重要的部分，如果没有处理好的话，不仅场景不好看，还极其的影响效率，是非常需要注意的一个部分。",{"title":134,"searchDepth":369,"depth":370,"links":1555},[1556,1562,1567,1574,1589,1593],{"id":886,"depth":369,"text":886,"children":1557},[1558,1559,1560,1561],{"id":900,"depth":370,"text":901},{"id":913,"depth":370,"text":914},{"id":951,"depth":370,"text":951},{"id":1004,"depth":370,"text":1005},{"id":1027,"depth":369,"text":1027,"children":1563},[1564,1565,1566],{"id":1033,"depth":370,"text":1034},{"id":1062,"depth":370,"text":1063},{"id":1091,"depth":370,"text":1092},{"id":1112,"depth":369,"text":1113,"children":1568},[1569,1570,1571,1572,1573],{"id":1119,"depth":370,"text":1120},{"id":1154,"depth":370,"text":1155},{"id":1200,"depth":370,"text":1201},{"id":1221,"depth":370,"text":1222},{"id":1252,"depth":370,"text":1253},{"id":1279,"depth":369,"text":1279,"children":1575},[1576,1583,1584,1585,1586,1587,1588],{"id":1285,"depth":370,"text":1286,"children":1577},[1578,1579,1580,1581,1582],{"id":1315,"depth":378,"text":1316},{"id":1330,"depth":378,"text":1331},{"id":1345,"depth":378,"text":1346},{"id":1360,"depth":378,"text":1361},{"id":1375,"depth":378,"text":1375},{"id":1386,"depth":370,"text":1387},{"id":1407,"depth":370,"text":1408},{"id":1422,"depth":370,"text":1423},{"id":1437,"depth":370,"text":1438},{"id":1455,"depth":370,"text":1456},{"id":1465,"depth":370,"text":1465},{"id":1487,"depth":369,"text":1487,"children":1590},[1591,1592],{"id":1493,"depth":370,"text":1494},{"id":1520,"depth":370,"text":1521},{"id":361,"depth":369,"text":361},"2017-08-23",{"layout":391,"status":392,"published":393,"author":1596,"author_login":396,"author_email":397,"wordpress_id":1597,"wordpress_url":1598,"date_gmt":1599,"excerpt":1600},{"display_name":395,"login":396,"email":397,"url":134},1952,"\u002F\u002F?p=1952","2017-08-23 07:00:16 +0000",{"type":8,"value":1601},[1602],[11,1603,859],{},"\u002F2017-08-23-ue4-lighting-and-optimize",{"title":854,"description":859},"_legacy\u002F2017\u002F2017-08-23-ue4-lighting-and-optimize",[410,411,300],"ImEbV9hxl70JgN620bn2U_qJFuejFQ8rUEAkeajNxaU",{"id":1610,"title":1611,"body":1612,"date":1822,"description":1616,"extension":389,"meta":1823,"navigation":393,"path":1832,"seo":1833,"stem":1834,"tags":1835,"__hash__":1837},"blogs\u002F_legacy\u002F2017\u002F2017-06-03-ue4-content-driven-mulipass-rendering-note.md","UE4官方Multipass Rendering笔记",{"type":8,"value":1613,"toc":1810},[1614,1617,1626,1629,1632,1635,1638,1643,1646,1649,1654,1657,1662,1665,1668,1674,1677,1680,1686,1689,1694,1697,1700,1703,1707,1710,1716,1719,1723,1726,1732,1735,1740,1743,1746,1749,1754,1757,1760,1763,1766,1769,1775,1779,1782,1785,1788,1792,1795,1798,1802,1805,1807],[11,1615,1616],{},"这是一篇记录性笔记，可能并没有什么实质性的内容。",[11,1618,1619,1620,1625],{},"看这个视频本质上就是出于一个误会，视频标题是",[443,1621,1624],{"href":1622,"rel":1623},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=QGIKrD7uHu8",[813],"Content-Driven Mulipass Rendering","，并不是针对某个特效的Step-by-step的教程，而是一些原理上的解释。",[11,1627,1628],{},"故而其中有很多内容都是关于3D渲染的公式和实现的，因此也就不会进行深入的研究。之所以选择UE4这样的引擎来做游戏，就是为了避免过度的接触细节。就像是学习高级语言之后，编译原理什么的粗略了解就可以了。",[11,1630,1631],{},"当然，遇到了非实现不可的特效的时候也没有办法。说实话，不太想和那些Paper呀、公式呀什么的打交道：D",[18,1633,1634],{"id":1634},"材质原理",[11,1636,1637],{},"视频中提到的两个概念很有作用，就是Scatter和Gather",[11,1639,1640],{},[433,1641],{"alt":435,"src":1642},"\u002Fwp-content\u002Fuploads\u002F2017\u002F06\u002Fclip_image001_thumb.png",[11,1644,1645],{},"官方的RT示例中，水波纹扩散就是使用的Gather的方式进行运算的。",[11,1647,1648],{},"这是以前在理解上的一个误区，不能将整个运算目标当作一张贴图来看待，而应该去思考没一个区域（像素）应该处于什么状态。",[11,1650,1651],{},[433,1652],{"alt":559,"src":1653},"\u002Fwp-content\u002Fuploads\u002F2017\u002F06\u002Fclip_image002_thumb.png",[11,1655,1656],{},"以及",[11,1658,1659],{},[433,1660],{"alt":568,"src":1661},"\u002Fwp-content\u002Fuploads\u002F2017\u002F06\u002Fclip_image003_thumb.png",[18,1663,1664],{"id":1664},"透视光学",[11,1666,1667],{},"视频中有提到水面的折射近似算法，就是所谓的Experience Based Magic Method。",[11,1669,1670],{},[433,1671],{"alt":1672,"src":1673},"clip_image004","\u002Fwp-content\u002Fuploads\u002F2017\u002F06\u002Fclip_image004_thumb.png",[11,1675,1676],{},"在光学现象中，还有提到chromatic aberration，也就是色差的实现方式。",[11,1678,1679],{},"色差的基础实现使用RGB Offset，不过演示中使用了spectral photons来进行。但pectral photons究竟是什么却没有搞明白，粗略搜索了一下也不得其门而入。感觉上像是光谱聚合吧，使用光谱这样的线条来进行模拟而不是根据点来进行计算，可以减少运算量。",[11,1681,1682],{},[433,1683],{"alt":1684,"src":1685},"clip_image005","\u002Fwp-content\u002Fuploads\u002F2017\u002F06\u002Fclip_image005_thumb.png",[11,1687,1688],{},"另外，色差是什么呢？可以参照维基百科的解释：",[40,1690,1691],{},[11,1692,1693],{},"色差是指光学上透镜无法将各种波长的色光都聚焦在同一点上的现象。它的产生是因为透镜对不同波长的色光有不同的折射率（色散现象）。",[11,1695,1696],{},"对于水面的模拟很有作用，不过实际上，这部分的原理并不是特别的重要，社区有个Ocean的项目对水面、水下的效果模拟都处理的比较好。",[18,1698,1699],{"id":1699},"特效",[11,1701,1702],{},"在视频页面能够找到项目的下载地址，项目中就是在视频里面演示的那些效果。",[32,1704,1706],{"id":1705},"flowmappainter","FlowMapPainter",[11,1708,1709],{},"基于RenderTarget绘制的FlowMap水面演示",[11,1711,1712],{},[433,1713],{"alt":1714,"src":1715},"clip_image006","\u002Fwp-content\u002Fuploads\u002F2017\u002F06\u002Fclip_image006_thumb.png",[11,1717,1718],{},"总体和ContentExample里面的RenderTarget在原理上是差不多的，使用的FlowMap也是材质节点，如果有用到的话可以参考一下。",[32,1720,1722],{"id":1721},"fluidsimulation","FluidSimulation",[11,1724,1725],{},"流体模拟",[11,1727,1728],{},[433,1729],{"alt":1730,"src":1731},"clip_image007","\u002Fwp-content\u002Fuploads\u002F2017\u002F06\u002Fclip_image007_thumb.png",[11,1733,1734],{},"基于纳维－斯托克斯方程（Navier-Stokes equations）的流体模拟，这个方程可以参考下维基的解释：",[40,1736,1737],{},[11,1738,1739],{},"纳维－斯托克斯方程（Navier-Stokes equations），以克劳德-路易·纳维（Claude-Louis Navier）和乔治·斯托克斯命名，是一组描述像液体和空气这样的流体物质的方程。这些方程建立了流体的粒子动量的改变率（力）和作用在液体内部的压力的变化和耗散粘滞力（类似于摩擦力）以及重力之间的关系。这些粘滞力产生于分子的相互作用，能告诉我们液体有多粘。这样，纳维-斯托克斯方程描述作用于液体任意给定区域的力的动态平衡。",[11,1741,1742],{},"很复杂……",[11,1744,1745],{},"第一步，Advect，似乎是叫平流计算？不过日式的叫法移流方程式似乎更加的能方便理解，据说原理和上面的FlowMap的差不多。",[11,1747,1748],{},"第二步，计算散度，散度这个东西似乎以前物理中有学过。",[40,1750,1751],{},[11,1752,1753],{},"散度是向量分析中的一个向量算子，将向量空间上的一个向量场（矢量场）对应到一个标量场上。散度描述的是向量场里一个点是汇聚点还是发源点，形象地说，就是这包含这一点的一个微小体元中的向量是“向外”居多还是“向内”居多。举例来说，考虑空间中的静电场，其空间里的电场强度是一个矢量场。正电荷附近，电场线“向外”发射，所以正电荷处的散度为正值，电荷越大，散度越大。负电荷附近，电场线“向内”，所以负电荷处的散度为负值，电荷越大，散度越小。",[11,1755,1756],{},"就是这样的一个感觉。",[11,1758,1759],{},"第三步，计算压力；第四步，计算压力梯度。",[11,1761,1762],{},"这两步都是压力处理，在视频中讲解时似乎被算做了一步：D",[11,1764,1765],{},"每一步都会使用到部分前面计算的结果，全部都是通过RenderTarget进行中继的。",[11,1767,1768],{},"项目中有几个示例，分别是2D空间和3D空间内的流体模拟。",[11,1770,1771],{},[433,1772],{"alt":1773,"src":1774},"clip_image008","\u002Fwp-content\u002Fuploads\u002F2017\u002F06\u002Fclip_image008_thumb.png",[32,1776,1778],{"id":1777},"hitmaskrender","HitMaskRender",[11,1780,1781],{},"就是在被命中的地方展示受到伤害的贴图，由于有密集恐惧症，就不贴图了，原理上比上面的都简单，就只是利用RenderTarget来做一个Mask而已。",[11,1783,1784],{},"但是其材质蓝图也是很复杂的，官方这个展示在各个细节方面都很\"认真\"。不仅仅做了mask，还有做法线上的Mask处理。",[11,1786,1787],{},"不过不知为何有几个地方有莫名的蓝图失连……所以怎么也没法把项目还原到官方视频中的那个效果。还有一个地方就是可能因为视频制作的时间比较靠前，在获取目标点的UV时，没有使用后面有的Trace UV功能，而是制作了一个UnWrap的Material来操作。",[32,1789,1791],{"id":1790},"paintingvoluemtextures","PaintingVoluemTextures",[11,1793,1794],{},"非常酷炫的使用VR进行绘制的云的效果，是VR的……没法测试。",[11,1796,1797],{},"原理上和流体的应该差不多。",[32,1799,1801],{"id":1800},"volumetextures","VolumeTextures",[11,1803,1804],{},"就是官方展示的那个3D的UV动画效果，使用了Custom Node，然后里面是有些复杂的Shader……",[18,1806,361],{"id":361},[11,1808,1809],{},"果然做特效是件很复杂是事情，3D图形学实在可怕，还好引擎已经将大部分东西都封装好了，只要不追求酷炫，基本就不太需要关心这些，大概……",{"title":134,"searchDepth":369,"depth":370,"links":1811},[1812,1813,1814,1821],{"id":1634,"depth":369,"text":1634},{"id":1664,"depth":369,"text":1664},{"id":1699,"depth":369,"text":1699,"children":1815},[1816,1817,1818,1819,1820],{"id":1705,"depth":370,"text":1706},{"id":1721,"depth":370,"text":1722},{"id":1777,"depth":370,"text":1778},{"id":1790,"depth":370,"text":1791},{"id":1800,"depth":370,"text":1801},{"id":361,"depth":369,"text":361},"2017-06-03",{"layout":391,"status":392,"published":393,"author":1824,"author_login":396,"author_email":397,"wordpress_id":1825,"wordpress_url":1826,"date_gmt":1827,"excerpt":1828},{"display_name":395,"login":396,"email":397,"url":134},1765,"\u002F\u002F?p=1765","2017-06-03 08:26:07 +0000",{"type":8,"value":1829},[1830],[11,1831,1616],{},"\u002F2017-06-03-ue4-content-driven-mulipass-rendering-note",{"title":1611,"description":1616},"_legacy\u002F2017\u002F2017-06-03-ue4-content-driven-mulipass-rendering-note",[410,1836,411],"RenderTarget","NJsTELXWuLwdbFnOd8Ez1DGEBE4ywsugN5G3tLrwTSg",1788763179381]