[{"data":1,"prerenderedAt":1048},["ShallowReactive",2],{"page-shader-1":3,"page-count-shader":370},[4,413,908],{"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":893,"description":420,"extension":389,"meta":894,"navigation":393,"path":903,"seo":904,"stem":905,"tags":906,"__hash__":907},"blogs\u002F_legacy\u002F2018\u002F2018-01-01-ue4-rendering-code-view-01.md","UE4渲染代码逻辑总结（上）",{"type":8,"value":417,"toc":867},[418,421,424,427,430,434,437,440,445,448,451,454,459,462,465,468,471,476,479,482,487,490,493,496,499,503,506,511,514,519,522,527,530,535,538,542,545,548,594,597,602,605,608,613,616,619,623,627,630,633,639,642,645,651,654,657,661,664,670,673,676,682,685,689,692,695,698,701,705,708,711,715,718,721,724,728,734,737,740,746,749,752,758,761,767,770,774,780,783,786,792,795,798,802,805,816,819,822,825,831,834,837,843,846,852,855,858,864],[11,419,420],{},"本文是对UE4中渲染代码逻辑的总结。",[11,422,423],{},"当前使用的UE4版本为4.18.3。",[11,425,426],{},"本文内容是之前提到的这段时间的笔记的总结，内容主要来自于社区文章、官方文档以及引擎源码的阅读。",[11,428,429],{},"由于渲染系统比较复杂，没有时间遍历所有的代码，很多地方掺杂了自己的臆测，如有错误，欢迎指正。",[18,431,433],{"id":432},"fshader","FShader",[11,435,436],{},"FShader是负责Shader的代码端基类，继承自FDeferredCleanupInterface。",[32,438,439],{"id":439},"结构",[11,441,442],{},[87,443,444],{},"FDeferredCleanupInterface",[11,446,447],{},"FDeferredCleanupInterface这个基类是用于游戏线程与渲染线程的同步的，只有一个纯虚函数：FinishCleanup。",[11,449,450],{},"由于渲染用的资源在两边都有在使用，所以当要删除一个资源时，会向渲染线程推送资源删除请求，同时通过BeginCleanup将其加入待删除列表，等到渲染线程结束时FinishCleanup就会被调用，这时候就可以安全的在游戏线程中完全释放资源了。",[11,452,453],{},"除了FShader之外，其他与渲染有关的资源如FLightMap以及FShadowMap等都有继承自这个类。",[11,455,456],{},[87,457,458],{},"FShaderResource",[11,460,461],{},"这个类也继承自FDeferredCleanupInterface，如其名称，它负责保管Shader编译之后的资源。",[11,463,464],{},"为了减小材质编译等对资源的占用，同一个FShaderResource会被多个FShader引用。例如Material Function就利用了这个机制。",[32,466,467],{"id":467},"使用",[11,469,470],{},"FShader共有两种类型的之类，分别是FGlobalShader与FMaterialShader，对应不同的使用用途。",[11,472,473],{},[87,474,475],{},"FGlobalShader",[11,477,478],{},"这个是全局的Shader，只允许存在一个实例。",[11,480,481],{},"渲染的核心Shader部分有许多就是FGlobalShader。",[11,483,484],{},[87,485,486],{},"FMaterialShader",[11,488,489],{},"这个是用于具体材质的Shader，会拥有很多实例。",[11,491,492],{},"进一步被实现为FMeshMaterialShader，可以将Mesh的顶点数据引入到Shader端。",[18,494,495],{"id":495},"渲染数据",[11,497,498],{},"渲染数据通过FPrimitiveSceneProxy经由结构FVertexFactory绑定到Shader上。",[32,500,502],{"id":501},"fvertexfactory","FVertexFactory",[11,504,505],{},"这个类负责将顶点数据从C++端带到Shader端，继承自FRenderResource，是渲染资源的一种。",[11,507,508],{},[87,509,510],{},"FLocalVertexFactory",[11,512,513],{},"提供本地空间到全局空间的转换，Static Mesh以及Cables、Procedual Mesh等都使用的是它。",[11,515,516],{},[87,517,518],{},"FGPUBaseSkinVertexFactory",[11,520,521],{},"这个是Skeletel Mesh用的，因为需要一些更多的数据。但是似乎还要配合继承自LocalVertexFactory的FGPUSkinPassthroughVertexFactory。",[11,523,524],{},[87,525,526],{},"FLandscapeVertexFactory",[11,528,529],{},"Landscape是基于VTF(Vertex Texture Fetch)，使用高度图来修改顶点位置实现的，所以需要额外的处理。",[11,531,532],{},[87,533,534],{},"FParticleVertexFactoryBase",[11,536,537],{},"粒子系统用的。",[32,539,541],{"id":540},"fprimitivesceneproxy","FPrimitiveSceneProxy",[11,543,544],{},"这个类似UPrimitiveComponent在渲染线程中的对应版本，负责维护每个Component在渲染线程上需要的数据。",[11,546,547],{},"UE4的核心类大多有自己在渲染线程中的对应",[237,549,550,560],{},[240,551,552],{},[243,553,554,557],{},[246,555,556],{},"游戏线程",[246,558,559],{},"渲染线程",[253,561,562,570,578,586],{},[243,563,564,567],{},[258,565,566],{},"UWorld",[258,568,569],{},"FScene",[243,571,572,575],{},[258,573,574],{},"UPrimitiveComponent",[258,576,577],{},"FPrimitiveSceneProxy \u002F FPrimitiveSceneInfo‬",[243,579,580,583],{},[258,581,582],{},"ULocalPlayer",[258,584,585],{},"FSceneViewState",[243,587,588,591],{},[258,589,590],{},"ULightComponent",[258,592,593],{},"FLightSceneProxy \u002F FLightSceneInfo",[11,595,596],{},"不同的UPrimitiveComponent类通过重载CreateSceneProxy()来创建自己的FPrimitiveSceneProxy。",[11,598,599],{},[87,600,601],{},"UCableComponent",[11,603,604],{},"非常的直观，在CreateSceneProxy()中直接创建FCableSceneProxy，然后进行初始化。",[11,606,607],{},"然后在SendRenderDyamicData_Concurrent()中将数据发送到渲染线程并借由SetDynamicData_RenderThread进行数据构造。",[11,609,610],{},[87,611,612],{},"UImagePlateFrustumComponent",[11,614,615],{},"由于始终只是在渲染面向摄像的2D材质，所以不需要VertexFactory。",[11,617,618],{},"所以FImagePlateFrustumSceneProxy只是在GetDynamicMeshElements时返回演算的结果。",[18,620,622],{"id":621},"drawing-policy","Drawing Policy",[32,624,626],{"id":625},"fdepthdrawingpolicy","FDepthDrawingPolicy",[11,628,629],{},"这个Drawing Policy工作于depth-only通道时，负责将Mesh的opaque和masked的深度信息写出。",[11,631,632],{},"通过调用",[125,634,637],{"className":635,"code":636,"language":130},[128],"VertexShader = InMaterialResource.GetShader\u003CTDepthOnlyVS\u003Cfalse> >(VertexFactory->GetType())\n",[132,638,636],{"__ignoreMap":134},[11,640,641],{},"DrawingPolicy便找到了对应的shader，并将vertex factory传了进去。",[11,643,644],{},"在需要Tessellation的情况下，还会另外获取HullShader和DomainShader",[125,646,649],{"className":647,"code":648,"language":130},[128],"HullShader = InMaterialResource.GetShader\u003CFDepthOnlyHS>(VertexFactory->GetType());\nDomainShader = InMaterialResource.GetShader\u003CFDepthOnlyDS>(VertexFactory->GetType());\n",[132,650,648],{"__ignoreMap":134},[11,652,653],{},"代码的分支很多，但是作用还是比较明显的。",[11,655,656],{},"其中还有SetSharedState和SetMeshRenderState两个函数负责传递参数。",[32,658,660],{"id":659},"fbasepassdrawingpolicy","FBasePassDrawingPolicy",[11,662,663],{},"这个是在basepass通道时处理Mesh，根据不同的光照类型会有不同的处理",[125,665,668],{"className":666,"code":667,"language":130},[128],"template\u003Ctypename LightMapPolicyType>\nclass TBasePassDrawingPolicy : public FBasePassDrawingPolicy\n",[132,669,667],{"__ignoreMap":134},[11,671,672],{},"这个类才算是本体的感觉吧。",[11,674,675],{},"会根据不同的光照类型获取不同的Base pass的shader，这里的光照类型并不是单纯的编辑器中设置的类型，而是实际在Shader中用于计算的光照模型",[125,677,680],{"className":678,"code":679,"language":130},[128],"enum ELightMapPolicyType\n{\nLMP_NO_LIGHTMAP,\nLMP_PRECOMPUTED_IRRADIANCE_VOLUME_INDIRECT_LIGHTING,\nLMP_CACHED_VOLUME_INDIRECT_LIGHTING,\nLMP_CACHED_POINT_INDIRECT_LIGHTING,\nLMP_SIMPLE_NO_LIGHTMAP,\nLMP_SIMPLE_LIGHTMAP_ONLY_LIGHTING,\nLMP_SIMPLE_DIRECTIONAL_LIGHT_LIGHTING,\nLMP_SIMPLE_STATIONARY_PRECOMPUTED_SHADOW_LIGHTING,\nLMP_SIMPLE_STATIONARY_SINGLESAMPLE_SHADOW_LIGHTING,\nLMP_SIMPLE_STATIONARY_VOLUMETRICLIGHTMAP_SHADOW_LIGHTING,\nLMP_LQ_LIGHTMAP,\nLMP_HQ_LIGHTMAP,\nLMP_DISTANCE_FIELD_SHADOWS_AND_HQ_LIGHTMAP,\n\u002F\u002F Mobile specific\nLMP_MOBILE_DISTANCE_FIELD_SHADOWS_AND_LQ_LIGHTMAP,\nLMP_MOBILE_DISTANCE_FIELD_SHADOWS_LIGHTMAP_AND_CSM,\nLMP_MOBILE_DIRECTIONAL_LIGHT_AND_SH_INDIRECT,\nLMP_MOBILE_MOVABLE_DIRECTIONAL_LIGHT_AND_SH_INDIRECT,\nLMP_MOBILE_MOVABLE_DIRECTIONAL_LIGHT_CSM_AND_SH_INDIRECT,\nLMP_MOBILE_DIRECTIONAL_LIGHT_CSM_AND_SH_INDIRECT,\nLMP_MOBILE_MOVABLE_DIRECTIONAL_LIGHT,\nLMP_MOBILE_MOVABLE_DIRECTIONAL_LIGHT_CSM,\nLMP_MOBILE_MOVABLE_DIRECTIONAL_LIGHT_WITH_LIGHTMAP,\nLMP_MOBILE_MOVABLE_DIRECTIONAL_LIGHT_CSM_WITH_LIGHTMAP,\n\u002F\u002F LightMapDensity\nLMP_DUMMY\n};\n",[132,681,679],{"__ignoreMap":134},[11,683,684],{},"GetUniformBasePassShaders负责完成差分，最终通过LightMapPolicyType就会得到不同的basepass的shader，不同的basepass的shader中，使用的vertex factory也就不同了。",[32,686,688],{"id":687},"drawingpolicyfactory","DrawingPolicyFactory",[11,690,691],{},"这是一组负责生成DrawingPloicy的工厂类，但是他们都没有各自的基类，而是对应自己的功能有稍微有些不同的实现。",[11,693,694],{},"在FDepthDrawingPolicyFactory::AddStaticMesh能够看到一个FStaticMesh是如何被注册到FScene中去的。",[11,696,697],{},"而这个调用来自FStaticMesh::AddToDrawLists，在其中能够看到FStaticMesh在通过各种DrawingPolicyFactory来进行Shader的关联注册。",[11,699,700],{},"而之后，在渲染线程中，FDepthDrawingPolicyFactory::DrawStaticMesh之类的函数就会被调用来进行渲染。",[32,702,704],{"id":703},"fprimitivesceneinfo","FPrimitiveSceneInfo",[11,706,707],{},"对DrawingPolicyFactory的调用最终来自FPrimitiveSceneInfo，这个类与FPrimitiveSceneProxy 是一对一的关系，是最终注册到FScene的数据结构。",[11,709,710],{},"在FScene::UpdatePrimitiveTransform_RenderThread中能够看到渲染线程是如何通过FPrimitiveSceneProxy ::GetPrimitiveSceneInfo来更新与FScene的关系的。",[18,712,714],{"id":713},"c到shader的绑定","C++到Shader的绑定",[11,716,717],{},"上面这些类是在C++中负责渲染逻辑的，而为了最终代码与Shader之间能够相互交流需要进行绑定。",[32,719,720],{"id":720},"绑定帮助宏",[11,722,723],{},"UE4中有一组宏来帮助绑定C++类与Shader代码。",[115,725,727],{"id":726},"fshader绑定","FShader绑定",[125,729,732],{"className":730,"code":731,"language":130},[128],"IMPLEMENT_MATERIAL_SHADER_TYPE(TemplatePrefix,ShaderClass,SourceFilename,FunctionName,Frequency)\n",[132,733,731],{"__ignoreMap":134},[11,735,736],{},"这个是实际将C++的类与Shader进行绑定的宏。",[11,738,739],{},"在引擎中能够看到很多这个宏，例如",[125,741,744],{"className":742,"code":743,"language":130},[128],"IMPLEMENT_MATERIAL_SHADER_TYPE(,FVelocityVS,TEXT(\"\u002FEngine\u002FPrivate\u002FVelocityShader.usf\"),TEXT(\"MainVertexShader\"),SF_Vertex);\n",[132,745,743],{"__ignoreMap":134},[11,747,748],{},"这个宏将FVelocityVS绑定到VelocityShader.usf中，而MainVertexShader是shader端的入口函数。",[11,750,751],{},"最后一个ShaderFrequency感觉上更加接近Shader的类型：",[125,753,756],{"className":754,"code":755,"language":130},[128],"enum EShaderFrequency\n{\nSF_Vertex            = 0,\nSF_Hull                = 1,\nSF_Domain            = 2,\nSF_Pixel            = 3,\nSF_Geometry            = 4,\nSF_Compute            = 5,\n\nSF_NumFrequencies    = 6,\n\nSF_NumBits            = 3,\n};\n",[132,757,755],{"__ignoreMap":134},[11,759,760],{},"第一个参数用于宏的进一步模板化，在上面的BasePass进行绑定的时候就能看到",[125,762,765],{"className":763,"code":764,"language":130},[128],"IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE\n",[132,766,764],{"__ignoreMap":134},[11,768,769],{},"这个宏对这个参数的使用。",[115,771,773],{"id":772},"vertexfactory绑定","VertexFactory绑定",[125,775,778],{"className":776,"code":777,"language":130},[128],"IMPLEMENT_VERTEX_FACTORY_TYPE(FactoryClass,ShaderFilename,bUsedWithMaterials,bSupportsStaticLighting,bSupportsDynamicLighting,bPrecisePrevWorldPos,bSupportsPositionOnly)\n",[132,779,777],{"__ignoreMap":134},[11,781,782],{},"这个宏将VertexFactory绑定到对应的shader中去",[11,784,785],{},"例如",[125,787,790],{"className":788,"code":789,"language":130},[128],"IMPLEMENT_VERTEX_FACTORY_TYPE(FGPUSkinPassthroughVertexFactory, \"\u002FEngine\u002FPrivate\u002FLocalVertexFactory.ush\", true, false, true, false, false);\n",[132,791,789],{"__ignoreMap":134},[11,793,794],{},"这个绑定使得FGPUSkinPassthroughVertexFactory与LocalVertexFactory.ush进行关联，能够看到有很多不同的VertexFactory绑定到了这里，而且VertexFatory在各个不同的ush里面都有定义。",[11,796,797],{},"这是因为前面有提到MeshShader是有很多实例，每一个实例会根据自己的光照类型、数据类型进行不同的绑定。",[32,799,801],{"id":800},"shader-plugin","Shader Plugin",[11,803,804],{},"从UE4.17开始，已经可以在插件中自己定义Shader并进行调用了。不过UE4的核心渲染流程依然没有开放，所以想要自定义Shader Model之类的话还是必须对源码进行修改。",[11,806,807,808,815],{},"详细的操作可以参考[",[809,810,814],"a",{"href":811,"rel":812},"https:\u002F\u002Fdocs-origin.unrealengine.com\u002Flatest\u002FINT\u002FProgramming\u002FRendering\u002FShaderInPlugin\u002FQuickStart\u002Findex.html",[813],"nofollow","官方文档","]，不过官方文档的操作没有进行充分的解释，只是教你怎么把引擎插件的LensDistortion插件给拷贝并修改成自己的插件，不过刚好可以方便对Shader绑定进行理解。",[115,817,818],{"id":818},"基础重载",[11,820,821],{},"ShouldCache用于定义是否在指定的平台要编译这个材质。",[11,823,824],{},"ModifyCompilationEnvironment用于在特定的平台上添加自己的定义，但是示例中直接就加进了两个自己的定义",[125,826,829],{"className":827,"code":828,"language":130},[128],"OutEnvironment.SetDefine(TEXT(\"GRID_SUBDIVISION_X\"), kGridSubdivisionX);\nOutEnvironment.SetDefine(TEXT(\"GRID_SUBDIVISION_Y\"), kGridSubdivisionY);\n",[132,830,828],{"__ignoreMap":134},[115,832,833],{"id":833},"参数绑定",[11,835,836],{},"可以看到FLensDistortionUVGenerationShader继承自FGlobalShader，然后添加了",[125,838,841],{"className":839,"code":840,"language":130},[128],"FShaderParameter PixelUVSize;\nFShaderParameter RadialDistortionCoefs;\nFShaderParameter TangentialDistortionCoefs;\nFShaderParameter DistortedCameraMatrix;\nFShaderParameter UndistortedCameraMatrix;\nFShaderParameter OutputMultiplyAndAdd;\n",[132,842,840],{"__ignoreMap":134},[11,844,845],{},"几个成员，然后在构造中绑定",[125,847,850],{"className":848,"code":849,"language":130},[128],"PixelUVSize.Bind(Initializer.ParameterMap, TEXT(\"PixelUVSize\"));\nRadialDistortionCoefs.Bind(Initializer.ParameterMap, TEXT(\"RadialDistortionCoefs\"));\n",[132,851,849],{"__ignoreMap":134},[11,853,854],{},"第二个参数是参数在Shader中的名称。",[11,856,857],{},"之后就可以通过",[125,859,862],{"className":860,"code":861,"language":130},[128],"SetShaderValue(RHICmdList, ShaderRHI, PixelUVSize, PixelUVSizeValue);\nSetShaderValue(RHICmdList, ShaderRHI, DistortedCameraMatrix, CompiledCameraModel.DistortedCameraMatrix);\n",[132,863,861],{"__ignoreMap":134},[11,865,866],{},"来进行修改了。",{"title":134,"searchDepth":369,"depth":370,"links":868},[869,873,877,883],{"id":432,"depth":369,"text":433,"children":870},[871,872],{"id":439,"depth":370,"text":439},{"id":467,"depth":370,"text":467},{"id":495,"depth":369,"text":495,"children":874},[875,876],{"id":501,"depth":370,"text":502},{"id":540,"depth":370,"text":541},{"id":621,"depth":369,"text":622,"children":878},[879,880,881,882],{"id":625,"depth":370,"text":626},{"id":659,"depth":370,"text":660},{"id":687,"depth":370,"text":688},{"id":703,"depth":370,"text":704},{"id":713,"depth":369,"text":714,"children":884},[885,889],{"id":720,"depth":370,"text":720,"children":886},[887,888],{"id":726,"depth":378,"text":727},{"id":772,"depth":378,"text":773},{"id":800,"depth":370,"text":801,"children":890},[891,892],{"id":818,"depth":378,"text":818},{"id":833,"depth":378,"text":833},"2018-01-01",{"layout":391,"status":392,"published":393,"author":895,"author_login":396,"author_email":397,"wordpress_id":896,"wordpress_url":897,"date_gmt":898,"excerpt":899},{"display_name":395,"login":396,"email":397,"url":134},2198,"\u002F\u002F?p=2198","2017-12-31 16:01:54 +0000",{"type":8,"value":900},[901],[11,902,420],{},"\u002F2018-01-01-ue4-rendering-code-view-01",{"title":415,"description":420},"_legacy\u002F2018\u002F2018-01-01-ue4-rendering-code-view-01",[409,410],"rDtA9FsRgIkV52wCHpGUtcAfohNBZfb4k6UKJoiKUyo",{"id":909,"title":910,"body":911,"date":1029,"description":915,"extension":389,"meta":1030,"navigation":393,"path":1042,"seo":1043,"stem":1044,"tags":1045,"__hash__":1047},"blogs\u002F_legacy\u002F2013\u002F2013-09-30-cocos2d-x%e4%b8%adshader%e7%9a%84%e4%bd%bf%e7%94%a8.md","cocos2d-x中shader的使用",{"type":8,"value":912,"toc":1027},[913,916,919,922,925,928,934,940,943,946,949,955,958,961,967,970,976,979,985,988,994,997,1000,1008,1011,1018,1021],[11,914,915],{},"其实最近各种事情都比较乱本来不该研究这个的，但是个人习惯上一旦有什么事挂在那就会心烦，于是最终还是把整个事情搞到自己满意为止了。",[11,917,918],{},"shader这个东西貌似很高端，其实本质上和其他的语言没有什么差别，而且还是c风格的。所以主要的时间都花在了怎么让它跑起来上面了。",[11,920,921],{},"cocos2d-x使用shader上有一个麻烦的地方，就是shader里面有什么错的地方的话程序就直接报错退出，就算在debug模式下跟踪运行也不会输出任何错误信息。所以要让网上找到的shader代码正常运行起来花了我不少精神，好在sample里面有不少现成的shader，可以参照着进行修改。",[11,923,924],{},"首先还是来稍微了解下什么是shader吧，由于在程序方面我多少是个野狐禅，所以就从实用性的角度来解释吧。shader分为vertex顶点部分和Fragment像素部分两个部分，总体而言就是显卡上所提供的可编程部分。相当于可以交托给显卡运行的脚本的感觉，shader的一个好处就是可以跑出很多比较酷的效果而不用加载额外的资源。至于vertex和fagment的分法，其实是因为显卡在绘制的时候是先画点再涂颜色的。",[11,926,927],{},"不过就像是我们不必知道操作系统和硬件的详细机制一样，shader这个东西其实只要跑起来就可以了。因为是c风格的，所以算是非常的浅显易懂。废话那么多其实也没什么意义，目前找到两个比较好的在线的shader的预览站：",[11,929,930],{},[809,931,932],{"href":932,"rel":933},"https:\u002F\u002Fwww.shadertoy.com\u002F",[813],[11,935,936],{},[809,937,938],{"href":938,"rel":939},"http:\u002F\u002Fglsl.heroku.com\u002F",[813],[11,941,942],{},"两个网站都是用webgl演示的，webgl本身是用的gles2.0的api，所以本质上和cocos2d-x是一样的。因此上面的shader只要稍微做一下修改就可以直接搬动到游戏里面使用，当然如果想自己实现牛逼的效果的话，自然需要自己研究下语法，这么多sample可以参考的话理论上也不会花多少时间的。",[11,944,945],{},"以结果而言，cocos2d-x的node里面是有自己的shaderprogram机制的。也就是说只要定义好了shader的话，是可以直接调用setshaderprogram来切换shader的，因此对于一些简单的shader特效，完全没有必要新定义一个类来做。言归正传，要让别人写好的shader跑起来，最主要的就是要注意uniform的定义。",[11,947,948],{},"在shader的语法中，uniform是从外界传入的参数。只要解决掉这个问题，就可以让shader正常的跑起来了。对于shadertoy上的shader，只要这样添加下面的代码进fsh里面就基本没有问题了：",[125,950,953],{"className":951,"code":952,"language":130},[128],"uniform vec2 center;\nuniform vec2 resolution;\nuniform vec4 iMouse; \n\nfloat iGlobalTime = CC_Time[1];\nvec2 iResolution = resolution;\n",[132,954,952],{"__ignoreMap":134},[11,956,957],{},"如果要想让cocos2d-下能够将鼠标的移动传入shader中，以让大部分要读取鼠标位置的shader能够运行，需要做的改造如下：",[11,959,960],{},"首先定义一下作为uniform的中继的变量用来保存GLuint的id，这里面和gl差不多，因为是在显卡里的缘故，所以都会用一个uint的标记来做变量的索引。",[125,962,965],{"className":963,"code":964,"language":130},[128],"GLuint     m_uniformMouse;\n",[132,966,964],{"__ignoreMap":134},[11,968,969],{},"然后按照标准的做法在适当的地方初始化一下，之后重要的是要获得准确的uniform的值：",[125,971,974],{"className":972,"code":973,"language":130},[128],"m_uniformMouse = glGetUniformLocation(shader->getProgram(), \"iMouse\");\n",[132,975,973],{"__ignoreMap":134},[11,977,978],{},"然后在ondraw的地方，将新的鼠标位置通过这个id传递给shader",[125,980,983],{"className":981,"code":982,"language":130},[128],"getShaderProgram()->setUniformLocationWith4f(m_uniformMouse, x, y, z, 0);\n",[132,984,982],{"__ignoreMap":134},[11,986,987],{},"这个根据shader里面的定义会有不同，这里这样传递鼠标是因为shadertoy上的shader是这样统一定义的：",[125,989,992],{"className":990,"code":991,"language":130},[128],"uniform vec4 iMouse;\n",[132,993,991],{"__ignoreMap":134},[11,995,996],{},"这样的话鼠标的传递大体上就完成了，监听cctouch系列事件对x,y,z的值进行修改即可。",[11,998,999],{},"如果要传递texture的话，可以自己定义一个cctexure2d，然后通过getname获得它的id传递给shader即可。如果是基于ccsprite的子类的话就不用操这个心，具体可以参照下BYGraySprite的写法，代码在打包里。",[11,1001,1002,1003,1007],{},"有一个需要注意的问题是，cocos2d-x默认传递的参数CC_Time",[1004,1005,1006],"span",{},"1","其实是一个数组，可以选择0,1,2,3档的感觉，档越高速度越快。一般情况下随意调整即可，因为没有哪一个和shadertoy的iTime契合的很好……",[11,1009,1010],{},"代码里的2.1.4是自己测试用的，2.1.0是功能比较全的版本，里面的灰色的sprite的那个类有参照某前辈的博文，不过一时找不到出处了，好在代码里本身是有版权信息的。总之大概跑出来的效果是这样的：",[11,1012,1013],{},[1014,1015],"img",{"alt":1016,"src":1017},"截图","\u002Fwp-content\u002Fuploads\u002F2013\u002F09\u002Faa-300x216.jpg",[11,1019,1020],{},"代码附上：",[11,1022,1023],{},[809,1024,1025],{"href":1025,"rel":1026},"http:\u002F\u002Fpan.baidu.com\u002Fs\u002F1cKbPu",[813],{"title":134,"searchDepth":369,"depth":370,"links":1028},[],"2013-09-30",{"layout":391,"status":392,"published":393,"author":1031,"author_login":1032,"author_email":1033,"author_url":1034,"wordpress_id":1035,"wordpress_url":1036,"date_gmt":1037,"excerpt":1038},{"display_name":1032,"login":1032,"email":1033,"url":1034},"chaoshikari","chaoshikari@gmail.com","\u002F",731,"\u002F\u002F?p=731","2013-09-30 13:33:13 +0000",{"type":8,"value":1039},[1040],[11,1041,915],{},"\u002F2013-09-30-cocos2d-x中shader的使用",{"title":910,"description":915},"_legacy\u002F2013\u002F2013-09-30-cocos2d-x%e4%b8%adshader%e7%9a%84%e4%bd%bf%e7%94%a8",[1046,409],"cocos2d-x","Ie7OMH-iDQvkLT1qSB_bnD6IbKvyDYAXla2KzBIehuM",1788763179367]