Gregory Igehy

Dancing at hemisphere coordinate

Notes of RenderToTexture in UE4

Unreal Japan Stream | UE4のプロファイリングと最適化のTips!Part 1


Visual Studio Snippets for Unreal Engine C++ Projects

How to install snippets?

**Method One**
Paste .snippet files into: C:\Users\$user$\Documents\Visual Studio 2013\Code Snippets\Visual C++\My Code Snippets. Then restart VS.

**Method Two**
Open Visual Studio, navigate to TOOLS -> Code Snippets Manager… -> Import…

How to use snippets?

**Method One**
Just start typing ue4... snippet list should be loaded in a form of combo box. 
Then use arrows to select snippet. Hit ENTER or TAB to insert snippet.

**Method Two**
Type all snippet name and hit TAB. You don't have to wait for VS to show snippet list.

To navigate between highlighted fields you can use TAB and SHIFT + TAB. After you enter all names, hit ENTER.


*ue4classa* – Blueprintable class that derives from an AActor. Parameters are: comment, class name and base class name.

*ue4classu* – Blueprintable class that derives from an UObject. Parameters are: comment, class name, base class name.

*ue4struct* – Simple structure. Parameters are: comment and name.

*ue4interface* – Simple ue4 interface. Parameters are: comment and name.

*ue4bpevent* – This function can be used as an event in blueprint. Parameters are: comment,
 UI category, virtual and const modifiers, function name and arguments.

*ue4bpfunc* – This function is available for blueprint logic. Parameters are: comment (parameters and return value), 
UI category, virtual and const modifiers, function name and arguments.

*ue4prop* – This read/write property is available everywhere (blueprint, instance and archetype details). 
Parameters are: comment, category, type and name.

*ue4enum* – Simple enum. Parameters are: comment, enum name, first member name and it’s comment.

*ue4enumdisplay* – Enum that can be used with blueprints.
 Parameters are: comment, enum name, first member name, it’s display name and comment.

*ue4log* – Simplest log line. Parameters are category, verbosity and message.

*ue4logdeclare* – Declaration of log category. Place this in main header of your project to allow logging. 
Parameters are: category, default verbosity and compile time verbosity.

*ue4logdefine* – Definition of log category. Place this in main code file. Parameter is category name.

*ue4logfloat* – Log line that can be used to print float value. Parameters are: category, verbosity and variable name.

*ue4logint* – This log line can be used to log an integer value. Parameters are: category, verbosity and variable name.

*ue4loguobj* – This log line is designed to log from inside of the objects. 
By default, square brackets contains a name of an object that writes the log. 
Parameters are: category, verbosity, message and name of a pointer to the object.

*ue4mark* – Can be used to mark changes in engine classes. Parameters are: 
Company symbol, task/ticket number, name and surname of a developer and short description of modification.

*ue4eve* - 9 snippets for each params combination. Can be used to create event. 
Parameters are: owning type and event type name.

*ue4del* - 9 snippets for each params combination. Can be used to create delegate. 
Parameters are: delegate type name and param type names.

*ue4delmul* - 9 snippets for each params combination. Can be used to create multicast delegate. 
Parameters are: delegate type name and param type names.

*ue4deldyn* - 9 snippets for each params combination. Can be used to create dynamic delegate.
 Parameters are: delegate type name, param type names and display values.

*ue4deldynmul* - 9 snippets for each params combination. Can be used to create dynamic multicast delegate.
 Parameters are: delegate type name, param type names and display values`.

UE4 Console variables

static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VSync"));
// Limit framerate on console if VSYNC is enabled to avoid jumps from 30 to 60 and back.
if( CVar->GetValueOnGameThread() != 0 )
   // do something

// Use CommandLine command
// optionally set the vsync console variable
if( FParse::Param(FCommandLine::Get(), TEXT("vsync")) )
	new(GEngine->DeferredCommands) FString(TEXT("r.vsync 1"));


UE4.18 ソースのノート

FDeferredShadingSceneRenderer::Render( ) をざっくり

// DeferredShadingRenderer.cpp

// プリパスの前の処理 : DepthRendering.cpp

// 半透明ライティング用のライトグリッド : LightGridInjection.cpp

// オクルージョンクエリ

// デプスシャドウ : ShadowDepthRendering.cpp

// ボリューメトリックフォグ : VolumetricFog.cpp

if ( forward )
  // フォワードシャドウ投影 : 

  // カプセルシャドウ Indirect : CapsuleShadows.cpp

// GBuffer クリア

// ベースパスの描画 : BaseRendering.cpp

// カスタムデプス 

// 速度の描画 : VelocityRendering.cpp

// ベースパス後のポストプロセス : CompositionLighting.cpp

// ディファードのライティング処理
if ( deferred )
    // カプセルシャドウ Indirect : CapsuleShadowRendering.cpp

   // DFAO

   // ライティング : LightRendering.cpp 

   // ライトグリッドのフィルタリング : TranslucentLighting.cpp

   //  動的スカイライト : DistanceFieldAmbientOcclusion.cpp

    // リフレクション : ReflectionEnvironment.cpp
   // スクリーンスペース SSS とか : CompositionLighting
    // ライトシャフトのオクルージョン : LightShaftRendering.cpp

   // 大気散乱フォグ : AtmosphericRendering.cpp

   // フォグ : FogRendering.cpp

   // 半透明の描画 : TranslucentLighting.cpp

   // 屈折 : DistortionRendering.cpp

   // ブルーム付きライトシャフト : LightShaftRendering.cpp

   // DistanceField

   // ポストプロセス : PostProcessing.cpp

FDeferredShadingSceneRenderer::RenderLights() : LightRendering.cpp

	if (bAllowSimpleLights)
		GatherSimpleLights(ViewFamily, Views, SimpleLights); // LightRendering.cpp

	// Build a list of visible lights.
	for (TSparseArray<FLightSceneInfoCompact>::TConstIterator LightIt(Scene->Lights);
               LightIt; ++LightIt)

       // ソート
       SortedLights.Sort( FCompareFSortedLightSceneInfo() );

      // Iterate over all lights to be rendered and build ranges for tiled deferred and unshadowed lights
     for (int32 LightIndex = 0; LightIndex < SortedLights.Num(); LightIndex++)

         	if (ShouldUseTiledDeferred(SupportedByTiledDeferredLightEnd, SimpleLights.InstanceData.Num()) && !bAnyUnsupportedByTiledDeferred && !bAnyViewIsStereo)

               	if (bRenderSimpleLightsStandardDeferred)
			RenderSimpleLightsStandardDeferred(RHICmdList, SimpleLights);

		// Draw non-shadowed non-light function lights without changing render targets between them
		for (int32 LightIndex = StandardDeferredStart; LightIndex < AttenuationLightStart; LightIndex++)
			const FSortedLightSceneInfo& SortedLightInfo = SortedLights[LightIndex];
			const FLightSceneInfo* const LightSceneInfo = SortedLightInfo.LightSceneInfo;

			// Render the light to the scene color buffer, using a 1x1 white texture as input 
			RenderLight(RHICmdList, LightSceneInfo, NULL, false, false);

		if (GUseTranslucentLightingVolumes && GSupportsVolumeTextureRendering)
			if (AttenuationLightStart)
				// Inject non-shadowed, non-light function lights in to the volume.
				SCOPED_DRAW_EVENT(RHICmdList, InjectNonShadowedTranslucentLighting);
				InjectTranslucentVolumeLightingArray(RHICmdList, SortedLights, AttenuationLightStart);
			if (SimpleLights.InstanceData.Num() > 0)
				SCOPED_DRAW_EVENT(RHICmdList, InjectSimpleLightsTranslucentLighting);
				InjectSimpleTranslucentVolumeLightingArray(RHICmdList, SimpleLights);
 	// Draw shadowed and light function lights
	for (int32 LightIndex = AttenuationLightStart; LightIndex < SortedLights.Num(); LightIndex++)
                 if (bDrawShadows)
                 	RenderShadowProjections(RHICmdList, &LightSceneInfo, ScreenShadowMaskTexture, bInjectedTranslucentVolume);


		// Render light function to the attenuation buffer.
		if (bDirectLighting)
			if (bDrawLightFunction)
				const bool bLightFunctionRendered = RenderLightFunction(RHICmdList, &LightSceneInfo, ScreenShadowMaskTexture, bDrawShadows, false);
				bUsedShadowMaskTexture |= bLightFunctionRendered;

			if (bDrawPreviewIndicator)
				RenderPreviewShadowsIndicator(RHICmdList, &LightSceneInfo, ScreenShadowMaskTexture, bUsedShadowMaskTexture);

		// Render the light to the scene color buffer, conditionally using the attenuation buffer or a 1x1 white texture as input 
			RenderLight(RHICmdList, &LightSceneInfo, ScreenShadowMaskTexture, false, true);
FSceneRenderer::GatherSimpleLights( )
void FSceneRenderer::GatherSimpleLights(const FSceneViewFamily& ViewFamily, const TArray<FViewInfo>& Views, FSimpleLightArray& SimpleLights)
	TArray<const FPrimitiveSceneInfo*, SceneRenderingAllocator> PrimitivesWithSimpleLights;

	// Gather visible primitives from all views that might have simple lights
	for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
		const FViewInfo& View = Views[ViewIndex];
		for (int32 PrimitiveIndex = 0; PrimitiveIndex < View.VisibleDynamicPrimitives.Num(); PrimitiveIndex++)
			const FPrimitiveSceneInfo* PrimitiveSceneInfo = View.VisibleDynamicPrimitives[PrimitiveIndex];
			const int32 PrimitiveId = PrimitiveSceneInfo->GetIndex();
			const FPrimitiveViewRelevance& PrimitiveViewRelevance = View.PrimitiveViewRelevanceMap[PrimitiveId];

			if (PrimitiveViewRelevance.bHasSimpleLights)
				// TArray::AddUnique is slow, but not expecting many entries in PrimitivesWithSimpleLights

	// Gather simple lights from the primitives
	for (int32 PrimitiveIndex = 0; PrimitiveIndex < PrimitivesWithSimpleLights.Num(); PrimitiveIndex++)
		const FPrimitiveSceneInfo* Primitive = PrimitivesWithSimpleLights[PrimitiveIndex];
		Primitive->Proxy->GatherSimpleLights(ViewFamily, SimpleLights);

FPostProcessing::Process( ) をざっくり

// PostProcessing.cpp

FRenderingCompositePassContext CompositeContext(RHICmdList, View);
FPostprocessContext Context(RHICmdList, CompositeContext.Graph, View);
if (AllowFullPostProcessing(View, FeatureLevel))
   AddPostProcessMaterial(Context, BL_BeforeTranslucency, SeparateTranslucency);

   // ガウシアン DOF の場合
 	bSepTransWasApplied = AddPostProcessDepthOfFieldGaussian(Context, DepthOfFieldStat, VelocityInput, SeparateTranslucency);
	FRenderingCompositePass* NoVelocity = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(GSystemTextures.BlackDummy));
	FRenderingCompositeOutputRef NoVelocityRef(NoVelocity);
	bSepTransWasApplied = AddPostProcessDepthOfFieldGaussian(Context, DepthOfFieldStat, NoVelocityRef, SeparateTranslucency);
      if(SeparateTranslucency.IsValid() && !bSepTransWasApplied)
		// separate translucency is done here or in AddPostProcessDepthOfFieldBokeh()
    	        FRenderingCompositePass* NodeRecombined = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBokehDOFRecombine(bIsComputePass));
		NodeRecombined->SetInput(ePId_Input0, Context.FinalOutput);
		NodeRecombined->SetInput(ePId_Input2, SeparateTranslucency);

		Context.FinalOutput = FRenderingCompositeOutputRef(NodeRecombined);

	AddPostProcessMaterial(Context, BL_BeforeTonemapping, SeparateTranslucency);      

        // テンポラル AA
	if( AntiAliasingMethod == AAM_TemporalAA && ViewState)
		AddTemporalAA( Context, VelocityInput );
		FRenderingCompositePass* NoVelocity = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessInput(GSystemTextures.BlackDummy));
		FRenderingCompositeOutputRef NoVelocityRef(NoVelocity);
		AddTemporalAA( Context, NoVelocityRef );

       // 自動露出のヒストグラム

       // ダウンサンプリング

       // 自動露出補正

       // ブルーム

      // トーンマッパー
				auto Node = AddSinglePostProcessMaterial(Context, BL_ReplacingTonemapper);

					// a custom tonemapper is provided
					Node->SetInput(ePId_Input0, Context.FinalOutput);

					// We are binding separate translucency here because the post process SceneTexture node can reference 
					// the separate translucency buffers through ePId_Input1. 
					// TODO: Check if material actually uses this texture and only bind if needed.
					Node->SetInput(ePId_Input1, SeparateTranslucency);
					Node->SetInput(ePId_Input2, BloomOutputCombined);
					Context.FinalOutput = Node;
					Tonemapper = AddTonemapper(Context, BloomOutputCombined, AutoExposure.EyeAdaptation, AutoExposure.MethodId, false, bHDRTonemapperOutput);
      // FXAA 
// 可視化

if ( dbDOScreenPercentage )
    // アップスケール
    FRenderingCompositePass* Node = Context.Graph.RegisterPass(new(FMemStack::Get()) 
                                                            FRCPassPostProcessUpscale(View, UpscaleQuality, PaniniConfig));
    Node->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput)); // Bilinear sampling.
    Node->SetInput(ePId_Input1, FRenderingCompositeOutputRef(Context.FinalOutput)); // Point sampling.
    Context.FinalOutput = FRenderingCompositeOutputRef(Node);				

Context.FinalOutput = AddPostProcessMaterialChain(Context, BL_AfterTonemapping, SeparateTranslucency, PreTonemapHDRColor, PostTonemapHDRColor);

// グラフを実行
// execute the graph/DAG
CompositeContext.Process(Context.FinalOutput.GetPass(), TEXT("PostProcessing"));

Notes of RenderThread


  • WindowsRunnableThread.cpp
    • (0) uint32 FRunnableThreadWin::GuardedRun()
    • (1) uint32 FRunnableThreadWin::Run()
  • RenderingThread.cpp
    • (2) virtual uint32 RenderingThread::Run(void) override
    • (3) void RenderingThreadMain( FEvent* TaskGraphBoundSyncEvent )
  • TaskGraph.cpp
    • (4) virtual void FNamedTaskThread::ProcessTasksUntilQuit(int32 QueueIndex) override
    • (5) void FNamedTaskThread::ProcessTasksNamedThread(int32 QueueIndex, bool bAllowStall)
  • TaskGraphInterfaces.h
    • (6) virtual void TGraphTask::ExecuteTask(TArray& NewTasks, ENamedThreads::Type CurrentThread) final override
  • SceneRendering.cpp
    • (7) static void RenderViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneRenderer* SceneRenderer)
  • DeferredShading.cpp
    • (8) void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)

Forward shading


Game 側

  • The World is the top level object representing a map or a sandbox in which Actors and Components will exist and be rendered.
  • A World can be a single Persistent Level with an optional list of streaming levels
  • that are loaded and unloaded via volumes and blueprint functions or it can be a collection of levels organized with a World Composition.
  • In a standalone game, generally only a single World exists except during seamless area transitions
  • when both a destination and current world exists. In the editor many Worlds exist:
  • The level being edited, each PIE instance, each editor tool which has an interactive rendered viewport, and many more.
  • The level object. Contains the level's actor list, BSP information, and brush list.
  • Every Level has a World as its Outer and can be used as the PersistentLevel,
  • however, when a Level has been streamed in the OwningWorld represents the World that it is a part of.
  • A Level is a collection of Actors (lights, volumes, mesh instances etc.).
  • Multiple Levels can be loaded and unloaded into the World to create a streaming experience.
UPrimitiveComponent : 描画や物理と相互作用する基底クラス, カリングの単位
  • PrimitiveComponents are SceneComponents that contain or generate some sort of geometry,
  • generally to be rendered or used as collision data.
  • There are several subclasses for the various types of geometry,
  • but the most common by far are the ShapeComponents (Capsule, Sphere, Box),
  • StaticMeshComponent, and SkeletalMeshComponent.
  • ShapeComponents generate geometry that is used for collision detection but are not rendered,
  • while StaticMeshComponents and SkeletalMeshComponents contain pre-built geometry that is rendered, but can also be used for collision detection.
  • ULightComponent : ライト用のクラス
  • FSceneView : FScene を描画する 1 視点

RenderThread 側

  • FScene : UWorld のレンダースレッド版
USceneComponent : FScene に追加する要素の基底クラス
FPrimitiveSceneProxy : UPrimitiveComponent の RenderThread 版
FPrimitiveSceneInfo : レンダラーの内部状態,UPrimitiveComponent と FPrimitiveSceneProxy
FViewInfo : FSceneView のレンダースレッド版
FSceneViewState : 複数フレームに渡るビューの情報を保存するもの
FSceneRenderer : 毎フレーム作られる一時状態


FMaterial : 描画用のマテリアルのインターフェース
FMaterialResource : FMaterial の実装
UMaterialInstance : マテリアルのインスタンス
UMaterialInstanceConstant : マテリアルの定数
UMaterialInstanceDynamic : ランタイムで変えられる


#include "Engine.h"

if ( GEngine != nullptr )
FString str = FString::Printf( TEXT( "last_render_time = %f" ), render_time );
GEngine->AddOnScreenDebugMessage( 2, 1.0f, FColor::Red,
str );

UE4 RenderTask

  • class FRenderBasePassDynamicDataThreadTask : public FRenderTask
  • class FRenderPrepassDynamicDataThreadTask : public FRenderTask
  • class FDrawShadowMeshElementsThreadTask : public FRenderTask
  • class FRenderDepthDynamicThreadTask : public FRenderTask
  • class FDrawSortedTransAnyThreadTask : public FRenderTask
  • class FRenderVelocityDynamicThreadTask : public FRenderTask