ShowFlag.HMDDistortion

ShowFlag.HMDDistortion

#Overview

name: ShowFlag.HMDDistortion

This variable is created as a Console Variable (cvar).

It is referenced in 15 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of ShowFlag.HMDDistortion is to control the distortion of output for Head-Mounted Display (HMD) devices in Unreal Engine’s rendering pipeline. This setting is primarily used in the post-processing stage of rendering for VR applications.

This variable is utilized by several Unreal Engine subsystems and plugins, including:

  1. The core rendering system (in MobileShadingRenderer and PostProcessing)
  2. The HeadMountedDisplay module
  3. Various VR-related plugins (SimpleHMD, PixelStreamingHMD, OpenXRHMD)
  4. The XRBase plugin

The value of this variable is typically set in the SetupViewFamily function of various HMD implementations. It can also be controlled through the engine’s show flags system.

The HMDDistortion flag interacts closely with other variables, particularly:

  1. StereoRendering: Often set alongside HMDDistortion to enable stereoscopic 3D rendering
  2. MotionBlur: Usually disabled when HMDDistortion is enabled
  3. Various other rendering flags that may need to be adjusted for VR rendering

Developers should be aware of the following when using this variable:

  1. It’s marked as SHOWFLAG_ALWAYS_ACCESSIBLE, meaning it’s available even in shipping builds
  2. Different HMD implementations may handle this flag differently. For example, PixelStreamingHMD sets it to false to avoid applying distortion on the UE side
  3. It’s part of the post-processing pass sequence and can affect performance

Best practices when using this variable include:

  1. Ensure it’s correctly set for your specific HMD implementation
  2. Be aware of its performance implications in your post-processing pipeline
  3. Consider its interaction with other rendering flags, especially in VR contexts
  4. Test thoroughly with your target HMD devices to ensure correct distortion application

The associated variable HMDDistortion is effectively the same as ShowFlag.HMDDistortion. It’s used in the same contexts and with the same considerations. The main difference is in how it’s accessed or set in different parts of the engine code.

#References in C++ code

#Callsites

This variable is referenced in the following C++ source code:

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Public/ShowFlagsValues.inl:311

Scope: file

Source code excerpt:

SHOWFLAG_FIXED_IN_SHIPPING(0, HighResScreenshotMask, SFG_Transient, NSLOCTEXT("UnrealEd", "HighResScreenshotMaskSF", "High Res Screenshot Mask"))
/** Distortion of output for HMD devices, SHOWFLAG_ALWAYS_ACCESSIBLE for now because USceneCaptureComponent needs that */
SHOWFLAG_ALWAYS_ACCESSIBLE(HMDDistortion, SFG_PostProcess, NSLOCTEXT("UnrealEd", "HMDDistortionSF", "HMD Distortion"))
/** Whether to render in stereoscopic 3d, for now SHOWFLAG_ALWAYS_ACCESSIBLE because it's used by StereoRendering */
SHOWFLAG_ALWAYS_ACCESSIBLE(StereoRendering, SFG_Hidden, NSLOCTEXT("UnrealEd", "StereoRenderingSF", "Stereoscopic Rendering"))
/** Show objects even if they should be distance culled, for now SHOWFLAG_ALWAYS_ACCESSIBLE because it's exposed in SceneCapture */
SHOWFLAG_ALWAYS_ACCESSIBLE(DistanceCulledPrimitives, SFG_Hidden, NSLOCTEXT("UnrealEd", "DistanceCulledPrimitivesSF", "Distance Culled Primitives"))
/** To visualize the culling in Tile Based Deferred Lighting, later for non tiled as well */
SHOWFLAG_FIXED_IN_SHIPPING(0, VisualizeLightCulling, SFG_Hidden, NSLOCTEXT("UnrealEd", "VisualizeLightCullingSF", "Light Culling"))

#Associated Variable and Callsites

This variable is associated with another variable named HMDDistortion. They share the same value. See the following C++ source code.

#Loc: <Workspace>/Engine/Plugins/Experimental/SimpleHMD/Source/SimpleHMD/Private/SimpleHMD.cpp:276

Scope (from outer to inner):

file
function     void FSimpleHMD::SetupViewFamily

Source code excerpt:

{
	InViewFamily.EngineShowFlags.MotionBlur = 0;
	InViewFamily.EngineShowFlags.HMDDistortion = true;
	InViewFamily.EngineShowFlags.StereoRendering = IsStereoEnabled();

	if (UWorld* World = GWorld)
	{
		WorldToMeters = World->GetWorldSettings()->WorldToMeters;
	}

#Loc: <Workspace>/Engine/Plugins/Media/PixelStreaming/Source/PixelStreamingHMD/Private/PixelStreamingHMD.cpp:289

Scope (from outer to inner):

file
function     void FPixelStreamingHMD::SetupViewFamily

Source code excerpt:

	InViewFamily.EngineShowFlags.MotionBlur = 0;
	// Note: We do not want to apply any distortion on the UE side.
	InViewFamily.EngineShowFlags.HMDDistortion = false;
	InViewFamily.EngineShowFlags.StereoRendering = IsStereoEnabled();

	if (UWorld* World = GWorld)
	{
		WorldToMeters = World->GetWorldSettings()->WorldToMeters;
	}

#Loc: <Workspace>/Engine/Plugins/Runtime/OpenXR/Source/OpenXRHMD/Private/OpenXRHMD.cpp:1229

Scope (from outer to inner):

file
function     void FOpenXRHMD::SetupViewFamily

Source code excerpt:

{
	InViewFamily.EngineShowFlags.MotionBlur = 0;
	InViewFamily.EngineShowFlags.HMDDistortion = false;
	InViewFamily.EngineShowFlags.StereoRendering = IsStereoEnabled();

	const FPipelinedFrameState& FrameState = GetPipelinedFrameStateForThread();
	if (FrameState.Views.Num() > 2)
	{
		InViewFamily.EngineShowFlags.Vignette = 0;

#Loc: <Workspace>/Engine/Plugins/Runtime/XRBase/Source/XRBase/Private/DefaultXRCamera.cpp:216

Scope (from outer to inner):

file
function     void FDefaultXRCamera::SetupViewFamily

Source code excerpt:

	if (InViewFamily.Views.Num() > 0 && !InViewFamily.Views[0]->bIsSceneCapture)
	{
		InViewFamily.EngineShowFlags.HMDDistortion = HMD != nullptr ? HMD->GetHMDDistortionEnabled(InViewFamily.Scene->GetShadingPath()) : false;
	}
	InViewFamily.EngineShowFlags.StereoRendering = bCurrentFrameIsStereoRendering;
	InViewFamily.EngineShowFlags.Rendering = HMD != nullptr ? !HMD->IsRenderingPaused() : true;
}

void FDefaultXRCamera::SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView)

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Public/ShowFlagsValues.inl:311

Scope: file

Source code excerpt:

SHOWFLAG_FIXED_IN_SHIPPING(0, HighResScreenshotMask, SFG_Transient, NSLOCTEXT("UnrealEd", "HighResScreenshotMaskSF", "High Res Screenshot Mask"))
/** Distortion of output for HMD devices, SHOWFLAG_ALWAYS_ACCESSIBLE for now because USceneCaptureComponent needs that */
SHOWFLAG_ALWAYS_ACCESSIBLE(HMDDistortion, SFG_PostProcess, NSLOCTEXT("UnrealEd", "HMDDistortionSF", "HMD Distortion"))
/** Whether to render in stereoscopic 3d, for now SHOWFLAG_ALWAYS_ACCESSIBLE because it's used by StereoRendering */
SHOWFLAG_ALWAYS_ACCESSIBLE(StereoRendering, SFG_Hidden, NSLOCTEXT("UnrealEd", "StereoRenderingSF", "Stereoscopic Rendering"))
/** Show objects even if they should be distance culled, for now SHOWFLAG_ALWAYS_ACCESSIBLE because it's exposed in SceneCapture */
SHOWFLAG_ALWAYS_ACCESSIBLE(DistanceCulledPrimitives, SFG_Hidden, NSLOCTEXT("UnrealEd", "DistanceCulledPrimitivesSF", "Distance Culled Primitives"))
/** To visualize the culling in Tile Based Deferred Lighting, later for non tiled as well */
SHOWFLAG_FIXED_IN_SHIPPING(0, VisualizeLightCulling, SFG_Hidden, NSLOCTEXT("UnrealEd", "VisualizeLightCullingSF", "Light Culling"))

#Loc: <Workspace>/Engine/Source/Runtime/HeadMountedDisplay/Public/IHeadMountedDisplay.h:117

Scope (from outer to inner):

file
class        class IHeadMountedDisplay : public IModuleInterface

Source code excerpt:


	/**
	 * Whether HMDDistortion post processing is enabled or not
	 */
	virtual bool GetHMDDistortionEnabled(EShadingPath ShadingPath) const = 0;

	/** 
	 * Called just before rendering the current frame on the render thread. Invoked before applying late update, so plugins that want to refresh poses on the
	 * render thread prior to late update. Use this to perform any initializations prior to rendering.
	 */
	UE_DEPRECATED(4.19, "Use IXRTrackingSystem::OnBeginRendering_Renderthread instead")

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/MobileShadingRenderer.cpp:506

Scope (from outer to inner):

file
function     void FMobileSceneRenderer::InitViews

Source code excerpt:

	// For the most part this is not the case when using scene captures. Thus scene captures always render to scene color target.
	const bool bShouldCompositeEditorPrimitives = FSceneRenderer::ShouldCompositeEditorPrimitives(Views[0]);
	const bool bStereoRenderingAndHMD = ViewFamily.EngineShowFlags.StereoRendering && ViewFamily.EngineShowFlags.HMDDistortion;
	bRenderToSceneColor = !bGammaSpace 
						|| bStereoRenderingAndHMD 
						|| bRequiresUpscale 
						|| bShouldCompositeEditorPrimitives 
						|| Views[0].bIsSceneCapture 
						|| Views[0].bIsReflectionCapture 

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp:389

Scope (from outer to inner):

file
function     void AddPostProcessingPasses

Source code excerpt:

		VisualizeTemporalUpscaler,
		PixelInspector,
		HMDDistortion,
		HighResolutionScreenshotMask,
#if UE_ENABLE_DEBUG_DRAWING
		DebugPrimitive,
#endif
		PrimaryUpscale,
		SecondaryUpscale,

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp:496

Scope (from outer to inner):

file
function     void AddPostProcessingPasses

Source code excerpt:

	PassSequence.SetEnabled(EPass::PixelInspector, false);
#endif
	PassSequence.SetEnabled(EPass::HMDDistortion, EngineShowFlags.StereoRendering && EngineShowFlags.HMDDistortion);
	PassSequence.SetEnabled(EPass::HighResolutionScreenshotMask, IsHighResolutionScreenshotMaskEnabled(View));
#if UE_ENABLE_DEBUG_DRAWING
	PassSequence.SetEnabled(EPass::DebugPrimitive, FSceneRenderer::ShouldCompositeDebugPrimitivesInPostProcess(View));
#endif
	PassSequence.SetEnabled(EPass::PrimaryUpscale, PaniniConfig.IsEnabled() || (View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::SpatialUpscale && PrimaryViewRect.Size() != View.GetSecondaryViewRectSize()));
	PassSequence.SetEnabled(EPass::SecondaryUpscale, View.RequiresSecondaryUpscale() || View.Family->GetSecondarySpatialUpscalerInterface() != nullptr);

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp:1644

Scope (from outer to inner):

file
function     void AddPostProcessingPasses

Source code excerpt:

#endif

	if (PassSequence.IsEnabled(EPass::HMDDistortion))
	{
		FHMDDistortionInputs PassInputs;
		PassSequence.AcceptOverrideIfLastPass(EPass::HMDDistortion, PassInputs.OverrideOutput);
		PassInputs.SceneColor = SceneColor;

		SceneColor = AddHMDDistortionPass(GraphBuilder, View, PassInputs);
	}

	if (PassSequence.IsEnabled(EPass::HighResolutionScreenshotMask))

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp:2146

Scope (from outer to inner):

file
function     void AddMobilePostProcessingPasses

Source code excerpt:

		SecondaryUpscale,
		Visualize,
		HMDDistortion,
		MAX
	};

	static const TCHAR* PassNames[] =
	{
		TEXT("Distortion"),

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp:2232

Scope (from outer to inner):

file
function     void AddMobilePostProcessingPasses

Source code excerpt:

	PassSequence.SetEnabled(EPass::Visualize, View.Family->EngineShowFlags.ShaderComplexity);

	PassSequence.SetEnabled(EPass::HMDDistortion, View.Family->EngineShowFlags.StereoRendering && View.Family->EngineShowFlags.HMDDistortion);

	// Always evaluate custom post processes
	// The scene color will be decoded at the first post-process material and output linear color space for the following passes
	// bMetalMSAAHDRDecode will be set to false if there is any post-process material exist

	auto AddPostProcessMaterialPass = [&GraphBuilder, &View, &Inputs, &SceneColor, &CustomDepth, &bMetalMSAAHDRDecode, &PassSequence](EBlendableLocation BlendableLocation, bool bLastPass)

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp:2922

Scope (from outer to inner):

file
function     void AddMobilePostProcessingPasses

Source code excerpt:

	}

	if (PassSequence.IsEnabled(EPass::HMDDistortion))
	{
		FHMDDistortionInputs PassInputs;
		PassSequence.AcceptOverrideIfLastPass(EPass::HMDDistortion, PassInputs.OverrideOutput);
		PassInputs.SceneColor = SceneColor;
		PassInputs.OverrideOutput.LoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad;

		SceneColor = AddHMDDistortionPass(GraphBuilder, View, PassInputs);
	}

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/ReflectionEnvironmentCapture.cpp:1291

Scope (from outer to inner):

file
function     void CaptureSceneIntoScratchCubemap

Source code excerpt:

		ViewFamily.EngineShowFlags.MotionBlur = 0;
		ViewFamily.EngineShowFlags.SetOnScreenDebug(false);
		ViewFamily.EngineShowFlags.HMDDistortion = 0;
		// Conditionally exclude particles and light functions as they are usually dynamic, and can't be captured well
		ViewFamily.EngineShowFlags.Particles = 0;
		ViewFamily.EngineShowFlags.LightFunctions = abs(GReflectionCaptureEnableLightFunctions) ? 1 : 0;
		ViewFamily.EngineShowFlags.SetCompositeEditorPrimitives(false);
		// These are highly dynamic and can't be captured effectively
		ViewFamily.EngineShowFlags.LightShafts = 0;