上一篇文章有提到“魔改UE4”的三篇文章主要是為比賽做的一些準備和嘗試,我主要是想著記錄、分享一下過程中遇到的想法,這些內容並沒有經過實際專案的驗證,效果效率都會存在問題,大家看看即可,歡迎指出。

這一篇開始說水面半透明反射。

水面,或者說是水體作為一種特殊的半透明物體,如果想要非常精緻的效果,繪製過程中,是需要特殊對待的,尤其是渲染順序,

看完這篇文章你將會了解到

Trick,共用不透明GBuffer,一次追蹤,完成水面半透明反射。(SSR 也可以用)

針對水面,rtx 反射的最佳化技巧

水面\水體,水上水下,半透明特效渲染順序的思考。

和一堆絮絮叨。

一、背景概述

1。1 聊一聊背景

其實這算是個烏龍事件,因為比賽時間很緊張,實話說我對UE4 rtx 做到了什麼程度並不熟悉,在網上看了半段火車那個demo,以為可以了,就沒繼續往下看,如果繼續後面看到火車窗戶那段,可能也就不會有這篇文章了。

大概是這麼個過程:開啟UE4, 開啟rtx,材質金屬度1,粗糙度0,rtx 反射666啊~ 試試半透明,納尼?選了Translucent 以後,粗糙度不然調啊,這還玩個錘子rtx。。

可是rtx 反射和SSR 同宗同源吶,那別人的水面是怎麼做的呢?百度了一下發現,哦原來UE4的半透明材質是有等級的,勾上最高階的那個,確實可以了,還有個SSR 的選項,SSR 開啟了。

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

哦,原來在RTX 的模式下,只有不透明的物體才會做RayTracing,半透明的都只會做SSR,因為效率嘛,總不能每一個半透明的物件,都追一次吧。一定是這樣的,好的,那我就做個半透明的rtx 來。

事實證明,自以為是,不自知,後面被真相打臉。

下面就看看我是怎麼自己實現水面“半透明反射的吧”。

1.2 UE4 Rt x 反射、水面特點

UE4 下的Rtx 模式,並不是完全的Rtx,他也是混了光柵化。不透明物體還是正常的套路GBuffer,半透明可選光柵化,還是走RayTracing。這麼做當然是為了效率考慮了,思路就是能不走rtx 就不走,用到刀刃上。

通讀一遍

UE4 RayTracingReflections.usf shader

,尤其是raygen shader,會得出一個結論,這tm和SSR 有個什麼區別。。。。也是在處理GBuffer 中不透明物體的反射,為了效能最佳化,它會按照一定規則篩選螢幕畫素,只有符合規則的畫素才會執行後面的ray tracing,不符合規則的直接跳過。

至於

這個規則,就是來源於GBuffer

,舉個粒子,在別的條件滿足的情況下,roughness 值小於某個值,意思就是說只有足夠光滑,才會反射,當然還會考慮到材質ID,甚至是自定義的深度等等。

通過了這個規則的畫素會執行ray tracing,

反射光線的方向,是要用到畫素的法線的

水面和別的半透明物體不太一樣的地方,水面很大,而且在我們的場景裡數量很少,可能只會有一片海洋。如果為水面單獨去寫一套Rtx shader,太複雜。

1。3 大膽想法

原始的不透明流程下的rtx 反射處理不了我的水面,是因為GBuffer 中沒有水面的資訊,如果有的話,不就可以追蹤出來了?。。。。於是我有了一個大膽的想法(其實我猜大家都知道這個Trick,因為它不止可以用在Rtx 反射,SSR 也是可以的):

是不是可以給水面單獨準備一份GBuffer

,來複用不透明GBuffer rtx 反射的流程,這樣多跑一遍rtx 反射不就成了?等下,是不是可以把水面繪製到原始的GBuffer上,這樣跑一邊追蹤不就成了?可是水面寫到GBuffer 上,水面下面的物件資訊不久被覆蓋了,那後面的光照怎麼半,這還算什麼水面半透明。等下,如果我有兩套GBuffer,一套專門用來做Rtx 反射,一套是正常的流程不久搞定了?

開搞~

二、兩套GBuffer 做水面Rtx 反射

流程概述

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

準備GBufferWithWater/SceneDepthWithWater

繪製場景GBuffer

複製場景GBuffer 到GBufferWithWater/SceneDepthWithWater

繪製水面GBuffer

使用水面GBuffer,繪製SSR/Rtx

使用正常GBuffer,繪製場景光照,及其他正常流程。

繪製水面Color。

逐個展開

2。1、準備GBuffer:

在RayTracingReflections。usf 中會發現Rtx 反射需要的資訊:

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

Roughness InGBufferB。b

ShadingModelID DecodeShadingModelId(InGBufferB。a)

WorldNormal DecodeNormal( InGBufferA。xyz );

Depth SceneDepth

SpecularColor InGBufferB。g

也就是需要三張圖:包含水面資訊的SceneDepth、GBufferA、GBufferB。仿照UE4的GBuffer,在SceneRenderTargets中新增下面三張RT:

TRefCountPtr

<

IPooledRenderTarget

>

GBufferWaterA

TRefCountPtr

<

IPooledRenderTarget

>

GBufferWaterB

TRefCountPtr

<

IPooledRenderTarget

>

SceneWaterDepthZ

2.1.1 申請資源:

void

FSceneRenderTargets

::

AllocGBufferTargets

FRHICommandList

&

RHICmdList

{

。。。

// Create the world-space normal g-buffer。

{

FPooledRenderTargetDesc

Desc

GetGBufferADesc

Desc

);

GRenderTargetPool

FindFreeElement

RHICmdList

Desc

GBufferA

TEXT

“GBufferA”

));

GRenderTargetPool

FindFreeElement

RHICmdList

Desc

GBufferWaterA

TEXT

“GBufferWaterA”

));

}

// Create the specular color and power g-buffer。

{

const

EPixelFormat

SpecularGBufferFormat

=

bHighPrecisionGBuffers

PF_FloatRGBA

PF_B8G8R8A8

FPooledRenderTargetDesc

Desc

FPooledRenderTargetDesc

::

Create2DDesc

BufferSize

SpecularGBufferFormat

FClearValueBinding

::

Transparent

TexCreate_None

TexCreate_RenderTargetable

false

));

Desc

Flags

|=

GFastVRamConfig

GBufferB

GRenderTargetPool

FindFreeElement

RHICmdList

Desc

GBufferB

TEXT

“GBufferB”

));

GRenderTargetPool

FindFreeElement

RHICmdList

Desc

GBufferWaterB

TEXT

“GBufferWaterB”

));

}

。。。

}

2.1.2 複製GBuffer/SceneDepth

這裡要注意的是,因為我們想著在不透明的那一次rtx 反射中把水面也捎帶著一次性追蹤完,所以我們要在場景本身GBuffer 的基礎上,把水面的資訊再填進去。

我這裡在場景GBuffer 繪製完成以後,先Copy Scene GBufferA/B 到水面對應的RT 上:

void FSceneRenderTargets::FinishGBufferPassAndResolve(FRHICommandListImmediate& RHICmdList)

{

。。。

//- QJN BEGIN CopyGBufferA

RHICmdList。CopyToResolveTarget(GBufferA->GetRenderTargetItem()。TargetableTexture, GBufferWaterA->GetRenderTargetItem()。TargetableTexture, ResolveParams);

RHICmdList。CopyToResolveTarget(GBufferB->GetRenderTargetItem()。TargetableTexture, GBufferWaterB->GetRenderTargetItem()。TargetableTexture, ResolveParams);

//- QJN END

。。。

}

場景深度寫完後,同樣做複製

void FSceneRenderTargets::ResolveSceneDepthToWaterTexture(FRHICommandList& RHICmdList)

{

RHICmdList。CopyToResolveTarget(GetSceneDepthSurface(), SceneWaterDepthZ->GetRenderTargetItem()。TargetableTexture, FResolveParams());

}

2。2 繪製水面GBuffer 資訊:

這裡要準備水面的shader 了,首先寫第一個pass, GBuffer Pass,首先簡單看一下shader:

// vs

float4x4 WorldToClip;

void VSMain(

in float3 InPosition : ATTRIBUTE0,

in float3 InNormal : ATTRIBUTE1,

in float3 InTangent : ATTRIBUTE2,

in float2 InTexCoord : ATTRIBUTE3,

out float2 OutTexCoord : TEXCOORD0,

out float4 OutWorldPos : TEXCOORD1,

out float4 OutPosition : SV_POSITION

{

//OutPosition = float4(InPosition, 0, 1);

OutWorldPos = float4(float4(InPosition, 1));

OutPosition = mul(float4(InPosition, 1), WorldToClip);

OutTexCoord = InTexCoord;

}

// ps

void PSMain_GBufferWrite(

float2 TexCoord : TEXCOORD0,

float4 WorldPos : TEXCOORD1,

float4 SVPos : SV_POSITION,

out float4 OutSceneColor : SV_Target0,

out float4 OutGBufferWaterA : SV_Target1,

out float4 OutGBufferWaterB : SV_Target2

{

float l_Roughness = 0。0;

float l_Metallic = 1。0;

float l_Spcular = 0。25;

int l_ShadingModelID = SHADINGMODELID_DEFAULT_LIT;

int l_SelectiveOutputMask = 0;

float3 l_WorldNormal = float3(0。0, 0。0, 1。0);

OutGBufferWaterA。rgb = EncodeNormal(l_WorldNormal。xyz);

OutGBufferWaterA。a = 0。0;

OutGBufferWaterB。r = l_Metallic;

OutGBufferWaterB。g = l_Spcular;

OutGBufferWaterB。b = l_Roughness;

OutGBufferWaterB。a = EncodeShadingModelIdAndSelectiveOutputMask(l_ShadingModelID, l_SelectiveOutputMask);

}

c++ 這邊的內容就不展示了,so easy。

如果大家對UE4 的GBuffer 的格式不太清楚,可以在DeferredShadingCommon。h 中找到Encode 和 Decode 的過程,文章最後我會貼出來。我這個水面先寫成這種最簡單的demo。

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

SSR 下的 水面和普通平面結果

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

水面繪製前後,GBufferA的結果

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

水面繪製前後,GBufferB的結果,b通道稍有不同

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

同理,左邊的平面是水面,右邊普通平面,因為開了SkyLight,正常平面獲得了反射補償,左邊只有SSR 結果

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

水面繪製前後,GBufferA的結果

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

水面繪製前後,GBufferB的結果,b通道稍有不同

2。3、在執行Rtx 反射之前,做攔截,使用我的GBuffer With Water。

UE4 不透明物件反射的入口函式在這裡。

void FDeferredShadingSceneRenderer::RenderDeferredReflectionsAndSkyLighting(FRHICommandListImmediate& RHICmdList, TRefCountPtr& DynamicBentNormalAO, TRefCountPtr& VelocityRT, TRefCountPtr& ReflectionColorRT)

可以看到裡面使用GBuffer 的函式是SetupSceneTextureParameters(),於是我們新添一個類似的取代他。

void SetupSceneTextureParameters_ForWaterReflection(

FRDGBuilder& GraphBuilder,

FSceneTextureParameters* OutTextures)

{

const FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(GraphBuilder。RHICmdList);

*OutTextures = FSceneTextureParameters();

// Should always have a depth buffer around allocated, since early z-pass is first。

OutTextures->SceneDepthBuffer = GraphBuilder。RegisterExternalTexture(SceneContext。SceneWaterDepthZ);

// Registers all the scene texture from the scene context。 No fallback is provided to catch mistake at shader parameter validation time

// when a pass is trying to access a resource before any other pass actually created it。

OutTextures->SceneVelocityBuffer = SceneContext。SceneVelocity ? GraphBuilder。RegisterExternalTexture(SceneContext。SceneVelocity) : nullptr;

//- QJN BEGIN

// For Reflection

//OutTextures->SceneGBufferA = SceneContext。GBufferA ? GraphBuilder。RegisterExternalTexture(SceneContext。GBufferA) : nullptr;

//OutTextures->SceneGBufferB = SceneContext。GBufferB ? GraphBuilder。RegisterExternalTexture(SceneContext。GBufferB) : nullptr;

OutTextures->SceneGBufferA = SceneContext。GBufferWaterA ? GraphBuilder。RegisterExternalTexture(SceneContext。GBufferWaterA) : nullptr; //For Water Reflection

OutTextures->SceneGBufferB = SceneContext。GBufferWaterB ? GraphBuilder。RegisterExternalTexture(SceneContext。GBufferWaterB) : nullptr; //For Water Reflection

OutTextures->SceneGBufferC = SceneContext。GBufferC ? GraphBuilder。RegisterExternalTexture(SceneContext。GBufferC) : nullptr;

OutTextures->SceneGBufferD = SceneContext。GBufferD ? GraphBuilder。RegisterExternalTexture(SceneContext。GBufferD) : nullptr;

OutTextures->SceneGBufferE = SceneContext。GBufferE ? GraphBuilder。RegisterExternalTexture(SceneContext。GBufferE) : nullptr;

// Ligthing channels might be disabled when all lights are on the same channel。

if (SceneContext。LightingChannels)

{

OutTextures->SceneLightingChannels = GraphBuilder。RegisterExternalTexture(SceneContext。LightingChannels, TEXT(“LightingChannels”));

OutTextures->bIsSceneLightingChannelsValid = true;

}

else

{

OutTextures->SceneLightingChannels = GraphBuilder。RegisterExternalTexture(GSystemTextures。WhiteDummy, TEXT(“LightingChannels”));

OutTextures->bIsSceneLightingChannelsValid = false;

}

}

SetupSceneTextureParameters_ForWaterReflection(GraphBuilder, &SceneTexturesWater);

ok,這就搞定了~這個就是輸出的SSR反射圖。

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

可以看到左邊的平面上也出現了SSR反射的結果

2。4、正常光照

場景光照等等其他用到GBuffer 的pass,照常用場景的GBuffer即可,不需要更改。

2。5、使用反射結果,渲染水面。

有了這個結果,就到了水面Color Pass 了,so easy, VertextShader 一樣,這裡我簡單輸出一下:

Texture2D ReflectionOutTexture;

SamplerState ReflectionOutSampler;

Texture2D SceneColorTexture;

SamplerState SceneColorSampler;

void PSMain_Color(

float2 TexCoord : TEXCOORD0,

float4 WorldPos : TEXCOORD1,

float4 SVPos : SV_POSITION,

out float4 OutSceneColor : SV_Target0

{

//OutSceneColor = float4(1, 1, 0, 1);

//OutSceneColor = float4(TexCoord, 0, 1);

float2 ScreenUV = SVPos。xy * View。ViewSizeAndInvSize。zw;

OutSceneColor = float4(ScreenUV, 0, 1);

float4 SSR = Texture2DSample(ReflectionOutTexture, ReflectionOutSampler, ScreenUV);

float4 SceneColor = Texture2DSample(SceneColorTexture, SceneColorSampler, ScreenUV);

OutSceneColor = lerp(SceneColor, SSR, 0。9);

}

ok,這就是最終的結果了。

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

SSR 結果

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

Rtx 結果

做到這一步驟,原本非常開心,以為可以繼續完善細節,勝券在握啊,想看鐵路demo挖掘點新點子的時候,看到了,原來人家UE4 原生就支援半透明反射!!!!!只是要在PostProcessVolume 中手動開啟!!!! 我了個去,這幾天白忙活了啊!!

不過,怎麼說呢,想到至少我的結果和UE4 原生半透明是一模一樣的,心裡平衡了一些。

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

我的水面半透明反射

三、反思與提高。

冷靜下來,分析總結一下。

3。1、水面半透明的正確做法:

Rtx 下

最正確的版本

應該是,水面畫素 純rtx 渲染,用rtx 去處理半透明(UE4 原生做法)。

最佳化一下

,就是水面單獨輸出到GBuffer 上,不透明追蹤一遍, 水面單獨追水面的。(我覺得這種做法應該是後期,大家將會普遍使用的方法,光柵化+rtx 的結合,才能讓rtx在目前的大環境想展開普及的必走之路,純rtx 平常玩家的機器跑不動的)。

再最佳化

,偷個懶,就是我這種,討巧的方式了,可謂是山路十八轉,很繞,但是的確可以滿足大多數的效果。

3。2、我這麼繞圈子,還有意義嗎?

答案是肯定的,如果同樣一片水,按照我的做法,我並不需要單獨為水做一遍追蹤求反射,UE4的做法是要單獨追蹤水的。

如果只是想給水面加個反射,我的方法可取。因為UE4 一旦在PostProcessVolume 中開啟半透明RayTracing,那麼整個場景的半透明都會走RayTracing 流程,那麼這個過程就誇張了,如果場景裡面的半透明物件非常多,那麼效率,效能問題就該提上日程了。用了我的方法,場景裡面的半透明可以照常用。

只是,我這個做法是有一個bug的,在一個特殊的情況下,這個效果bug 會非常明顯,這裡賣個關子,小夥伴們可以猜一下,是什麼bug。

3。3、水面渲染的思考。

水面是一種特殊的半透明,如果要出非常精細的效果,肯定是要單獨拿出來畫的,不能放到半透明通用的佇列裡面。因為存在一個渲染順序的問題,水上水下的引擎處理 半透明、特效這些渲染順序是不同的。舉個簡單的例子:

假設水面上方有一個半透明的特效。那麼:

當攝像機視角在水面上方時,應該先畫水面,再畫特效。

當攝像機視角在水面下方時,應該先畫特效,再畫水面。

不然就會有如下bug:

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

水下,畫面正不正常,看彩虹有沒有扭曲

水上,畫面不正常,看彩虹有沒有畫出來

我相信UE如果想要自身的水面效果好,不算外掛,

在未來某個版本內,肯定會對水面,或者水體單獨處理

,也許會把水面單獨拿出來作為一個特殊的Actor,也可能只是某個材質ID,調整繪製順序等等,不信,我們騎驢看賬本,走著瞧~

3。4、關於RenderDoc

吐槽其實也談不上,它居然不支援rtx,其實很正常,但是確實被坑慘了啊~~~~~在程式碼結構上,因為和SSR 極度相似,所以可以選擇在非光追的環境下寫程式碼,方便除錯,RenderDoc 是不支援rtx的。這裡吐槽一句,我就被RenderDoc 坑的不淺,本來寫的好好的,有一天突然rtx 開不開了,跟了半天程式碼,一直卡在建立裝置那裡,dx12 device5 怎麼也建立不成功,ue4 會自動建立預設device, 查了顯示卡版本,查了Windows版本,UE4 程式碼回滾,重編UE4 ,折騰了一天沒搞定,都要哭了,心想著,完了只能重新取一份最新的程式碼從頭來,可是第二天也不知道怎麼,把plugin 裡面 render doc 的勾勾上,一切回覆正常。菩薩保佑,謝天謝地。。。。。

慣例,過程圖,標題圖get~

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

我們的比賽demo,辛苦我們的美術小哥,用了3、4天拼出來的場景。

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

下期有緣再見~

附錄

1、為了參加這次比賽,專門抽出時間去看了rtx 相關。中間用dx12 自己實現過純rtx 的流程。來兩張截圖:

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

【魔改UE4】Rtx水面半透明反射的一次討巧嘗試

2、UE4 GBuffer Encode\Decode相關

GBuffer 結構

// all values that are output by the forward rendering pass

struct

FGBufferData

{

// normalized

float3

WorldNormal

// 0。。1 (derived from BaseColor, Metalness, Specular)

float3

DiffuseColor

// 0。。1 (derived from BaseColor, Metalness, Specular)

float3

SpecularColor

// 0。。1, white for SHADINGMODELID_SUBSURFACE_PROFILE and SHADINGMODELID_EYE (apply BaseColor after scattering is more correct and less blurry)

float3

BaseColor

// 0。。1

float

Metallic

// 0。。1

float

Specular

// 0。。1

float4

CustomData

// Indirect irradiance luma

float

IndirectIrradiance

// Static shadow factors for channels assigned by Lightmass

// Lights using static shadowing will pick up the appropriate channel in their deferred pass

float4

PrecomputedShadowFactors

// 0。。1

float

Roughness

// 0。。1 ambient occlusion e。g。SSAO, wet surface mask, skylight mask, 。。。

float

GBufferAO

// 0。。255

uint

ShadingModelID

// 0。。255

uint

SelectiveOutputMask

// 0。。1, 2 bits, use HasDistanceFieldRepresentation(GBuffer) or HasDynamicIndirectShadowCasterRepresentation(GBuffer) to extract

float

PerObjectGBufferData

// in world units

float

CustomDepth

// Custom depth stencil value

uint

CustomStencil

// in unreal units (linear), can be used to reconstruct world position,

// only valid when decoding the GBuffer as the value gets reconstructed from the Z buffer

float

Depth

// Velocity for motion blur (only used when WRITES_VELOCITY_TO_GBUFFER is enabled)

float4

Velocity

// 0。。1, only needed by SHADINGMODELID_SUBSURFACE_PROFILE and SHADINGMODELID_EYE which apply BaseColor later

float3

StoredBaseColor

// 0。。1, only needed by SHADINGMODELID_SUBSURFACE_PROFILE and SHADINGMODELID_EYE which apply Specular later

float

StoredSpecular

// 0。。1, only needed by SHADINGMODELID_EYE which encodes Iris Distance inside Metallic

float

StoredMetallic

};

Encode

//DeferredShadingCommon。h

/** Populates OutGBufferA, B and C */

void EncodeGBuffer(

FGBufferData GBuffer,

out float4 OutGBufferA,

out float4 OutGBufferB,

out float4 OutGBufferC,

out float4 OutGBufferD,

out float4 OutGBufferE,

out float4 OutGBufferVelocity,

float QuantizationBias = 0 // -0。5 to 0。5 random float。 Used to bias quantization。

{

if (GBuffer。ShadingModelID == SHADINGMODELID_UNLIT)

{

OutGBufferA = 0;

SetGBufferForUnlit(OutGBufferB);

OutGBufferC = 0;

OutGBufferD = 0;

OutGBufferE = 0;

}

else

{

#if 1

OutGBufferA。rgb = EncodeNormal( GBuffer。WorldNormal );

OutGBufferA。a = GBuffer。PerObjectGBufferData;

#else

float3 Normal = GBuffer。WorldNormal;

uint NormalFace = 0;

EncodeNormal( Normal, NormalFace );

OutGBufferA。rg = Normal。xy;

OutGBufferA。b = 0;

OutGBufferA。a = GBuffer。PerObjectGBufferData;

#endif

OutGBufferB。r = GBuffer。Metallic;

OutGBufferB。g = GBuffer。Specular;

OutGBufferB。b = GBuffer。Roughness;

OutGBufferB。a = EncodeShadingModelIdAndSelectiveOutputMask(GBuffer。ShadingModelID, GBuffer。SelectiveOutputMask);

OutGBufferC。rgb = EncodeBaseColor( GBuffer。BaseColor );

#if ALLOW_STATIC_LIGHTING

// No space for AO。 Multiply IndirectIrradiance by AO instead of storing。

OutGBufferC。a = EncodeIndirectIrradiance(GBuffer。IndirectIrradiance * GBuffer。GBufferAO) + QuantizationBias * (1。0 / 255。0);

#else

OutGBufferC。a = GBuffer。GBufferAO;

#endif

OutGBufferD = GBuffer。CustomData;

OutGBufferE = GBuffer。PrecomputedShadowFactors;

}

#if WRITES_VELOCITY_TO_GBUFFER

OutGBufferVelocity = GBuffer。Velocity;

#else

OutGBufferVelocity = 0;

#endif

}

Decode

//DeferredShadingCommon。h

/** Populates FGBufferData */

// @param bChecker High frequency Checkerboard pattern computed with one of the CheckerFrom。。 functions, todo: profile if float 0/1 would be better (need to make sure it‘s 100% the same)

FGBufferData DecodeGBufferData(

float4 InGBufferA,

float4 InGBufferB,

float4 InGBufferC,

float4 InGBufferD,

float4 InGBufferE,

float4 InGBufferVelocity,

float CustomNativeDepth,

uint CustomStencil,

float SceneDepth,

bool bGetNormalizedNormal,

bool bChecker)

{

FGBufferData GBuffer;

GBuffer。WorldNormal = DecodeNormal( InGBufferA。xyz );

if(bGetNormalizedNormal)

{

GBuffer。WorldNormal = normalize(GBuffer。WorldNormal);

}

GBuffer。PerObjectGBufferData = InGBufferA。a;

GBuffer。Metallic = InGBufferB。r;

GBuffer。Specular = InGBufferB。g;

GBuffer。Roughness = InGBufferB。b;

// Note: must match GetShadingModelId standalone function logic

// Also Note: SimpleElementPixelShader directly sets SV_Target2 ( GBufferB ) to indicate unlit。

// An update there will be required if this layout changes。

GBuffer。ShadingModelID = DecodeShadingModelId(InGBufferB。a);

GBuffer。SelectiveOutputMask = DecodeSelectiveOutputMask(InGBufferB。a);

GBuffer。BaseColor = DecodeBaseColor(InGBufferC。rgb);

#if ALLOW_STATIC_LIGHTING

GBuffer。GBufferAO = 1;

GBuffer。IndirectIrradiance = DecodeIndirectIrradiance(InGBufferC。a);

#else

GBuffer。GBufferAO = InGBufferC。a;

GBuffer。IndirectIrradiance = 1;

#endif

GBuffer。CustomData = !(GBuffer。SelectiveOutputMask & SKIP_CUSTOMDATA_MASK) ? InGBufferD : 0;

GBuffer。PrecomputedShadowFactors = !(GBuffer。SelectiveOutputMask & SKIP_PRECSHADOW_MASK) ? InGBufferE : ((GBuffer。SelectiveOutputMask & ZERO_PRECSHADOW_MASK) ? 0 : 1);

GBuffer。CustomDepth = ConvertFromDeviceZ(CustomNativeDepth);

GBuffer。CustomStencil = CustomStencil;

GBuffer。Depth = SceneDepth;

GBuffer。StoredBaseColor = GBuffer。BaseColor;

GBuffer。StoredMetallic = GBuffer。Metallic;

GBuffer。StoredSpecular = GBuffer。Specular;

FLATTEN

if( GBuffer。ShadingModelID == SHADINGMODELID_EYE )

{

GBuffer。Metallic = 0。0;

#if IRIS_NORMAL

GBuffer。Specular = 0。25;

#endif

}

// derived from BaseColor, Metalness, Specular

{

GBuffer。SpecularColor = ComputeF0(GBuffer。Specular, GBuffer。BaseColor, GBuffer。Metallic);

if (UseSubsurfaceProfile(GBuffer。ShadingModelID))

{

AdjustBaseColorAndSpecularColorForSubsurfaceProfileLighting(GBuffer。BaseColor, GBuffer。SpecularColor, GBuffer。Specular, bChecker);

}

GBuffer。DiffuseColor = GBuffer。BaseColor - GBuffer。BaseColor * GBuffer。Metallic;

#if USE_DEVELOPMENT_SHADERS

{

// this feature is only needed for development/editor - we can compile it out for a shipping build (see r。CompileShadersForDevelopment cvar help)

GBuffer。DiffuseColor = GBuffer。DiffuseColor * View。DiffuseOverrideParameter。www + View。DiffuseOverrideParameter。xyz;

GBuffer。SpecularColor = GBuffer。SpecularColor * View。SpecularOverrideParameter。w + View。SpecularOverrideParameter。xyz;

}

#endif //USE_DEVELOPMENT_SHADERS

}

GBuffer。Velocity = !(GBuffer。SelectiveOutputMask & SKIP_VELOCITY_MASK) ? InGBufferVelocity : 0;

return GBuffer;

}