上一篇文章有提到“魔改UE4”的三篇文章主要是為比賽做的一些準備和嘗試,我主要是想著記錄、分享一下過程中遇到的想法,這些內容並沒有經過實際專案的驗證,效果效率都會存在問題,大家看看即可,歡迎指出。
這一篇開始說水面半透明反射。
水面,或者說是水體作為一種特殊的半透明物體,如果想要非常精緻的效果,繪製過程中,是需要特殊對待的,尤其是渲染順序,
看完這篇文章你將會了解到
:
Trick,共用不透明GBuffer,一次追蹤,完成水面半透明反射。(SSR 也可以用)
針對水面,rtx 反射的最佳化技巧
水面\水體,水上水下,半透明特效渲染順序的思考。
和一堆絮絮叨。
一、背景概述
1。1 聊一聊背景
其實這算是個烏龍事件,因為比賽時間很緊張,實話說我對UE4 rtx 做到了什麼程度並不熟悉,在網上看了半段火車那個demo,以為可以了,就沒繼續往下看,如果繼續後面看到火車窗戶那段,可能也就不會有這篇文章了。
大概是這麼個過程:開啟UE4, 開啟rtx,材質金屬度1,粗糙度0,rtx 反射666啊~ 試試半透明,納尼?選了Translucent 以後,粗糙度不然調啊,這還玩個錘子rtx。。
可是rtx 反射和SSR 同宗同源吶,那別人的水面是怎麼做的呢?百度了一下發現,哦原來UE4的半透明材質是有等級的,勾上最高階的那個,確實可以了,還有個SSR 的選項,SSR 開啟了。
哦,原來在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 反射
流程概述
準備GBufferWithWater/SceneDepthWithWater
繪製場景GBuffer
複製場景GBuffer 到GBufferWithWater/SceneDepthWithWater
繪製水面GBuffer
使用水面GBuffer,繪製SSR/Rtx
使用正常GBuffer,繪製場景光照,及其他正常流程。
繪製水面Color。
逐個展開
2。1、準備GBuffer:
在RayTracingReflections。usf 中會發現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。
SSR 下的 水面和普通平面結果
水面繪製前後,GBufferA的結果
水面繪製前後,GBufferB的結果,b通道稍有不同
同理,左邊的平面是水面,右邊普通平面,因為開了SkyLight,正常平面獲得了反射補償,左邊只有SSR 結果
水面繪製前後,GBufferA的結果
水面繪製前後,GBufferB的結果,b通道稍有不同
2。3、在執行Rtx 反射之前,做攔截,使用我的GBuffer With Water。
UE4 不透明物件反射的入口函式在這裡。
void FDeferredShadingSceneRenderer::RenderDeferredReflectionsAndSkyLighting(FRHICommandListImmediate& RHICmdList, TRefCountPtr
可以看到裡面使用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反射圖。
可以看到左邊的平面上也出現了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,這就是最終的結果了。
SSR 結果
Rtx 結果
做到這一步驟,原本非常開心,以為可以繼續完善細節,勝券在握啊,想看鐵路demo挖掘點新點子的時候,看到了,原來人家UE4 原生就支援半透明反射!!!!!只是要在PostProcessVolume 中手動開啟!!!! 我了個去,這幾天白忙活了啊!!
不過,怎麼說呢,想到至少我的結果和UE4 原生半透明是一模一樣的,心裡平衡了一些。
我的水面半透明反射
三、反思與提高。
冷靜下來,分析總結一下。
3。1、水面半透明的正確做法:
Rtx 下
最正確的版本
應該是,水面畫素 純rtx 渲染,用rtx 去處理半透明(UE4 原生做法)。
最佳化一下
,就是水面單獨輸出到GBuffer 上,不透明追蹤一遍, 水面單獨追水面的。(我覺得這種做法應該是後期,大家將會普遍使用的方法,光柵化+rtx 的結合,才能讓rtx在目前的大環境想展開普及的必走之路,純rtx 平常玩家的機器跑不動的)。
再最佳化
,偷個懶,就是我這種,討巧的方式了,可謂是山路十八轉,很繞,但是的確可以滿足大多數的效果。
3。2、我這麼繞圈子,還有意義嗎?
答案是肯定的,如果同樣一片水,按照我的做法,我並不需要單獨為水做一遍追蹤求反射,UE4的做法是要單獨追蹤水的。
如果只是想給水面加個反射,我的方法可取。因為UE4 一旦在PostProcessVolume 中開啟半透明RayTracing,那麼整個場景的半透明都會走RayTracing 流程,那麼這個過程就誇張了,如果場景裡面的半透明物件非常多,那麼效率,效能問題就該提上日程了。用了我的方法,場景裡面的半透明可以照常用。
只是,我這個做法是有一個bug的,在一個特殊的情況下,這個效果bug 會非常明顯,這裡賣個關子,小夥伴們可以猜一下,是什麼bug。
3。3、水面渲染的思考。
水面是一種特殊的半透明,如果要出非常精細的效果,肯定是要單獨拿出來畫的,不能放到半透明通用的佇列裡面。因為存在一個渲染順序的問題,水上水下的引擎處理 半透明、特效這些渲染順序是不同的。舉個簡單的例子:
假設水面上方有一個半透明的特效。那麼:
當攝像機視角在水面上方時,應該先畫水面,再畫特效。
當攝像機視角在水面下方時,應該先畫特效,再畫水面。
不然就會有如下bug:
水下,畫面正不正常,看彩虹有沒有扭曲
水上,畫面不正常,看彩虹有沒有畫出來
我相信UE如果想要自身的水面效果好,不算外掛,
在未來某個版本內,肯定會對水面,或者水體單獨處理
,也許會把水面單獨拿出來作為一個特殊的Actor,也可能只是某個材質ID,調整繪製順序等等,不信,我們騎驢看賬本,走著瞧~
3。4、關於RenderDoc
吐槽其實也談不上,它居然不支援rtx,其實很正常,但是確實被坑慘了啊~~~~~在程式碼結構上,因為和SSR 極度相似,所以可以選擇在非光追的環境下寫程式碼,方便除錯,RenderDoc 是不支援rtx的。這裡吐槽一句,我就被RenderDoc 坑的不淺,本來寫的好好的,有一天突然rtx 開不開了,跟了半天程式碼,一直卡在建立裝置那裡,dx12 device5 怎麼也建立不成功,ue4 會自動建立預設device, 查了顯示卡版本,查了Windows版本,UE4 程式碼回滾,重編UE4 ,折騰了一天沒搞定,都要哭了,心想著,完了只能重新取一份最新的程式碼從頭來,可是第二天也不知道怎麼,把plugin 裡面 render doc 的勾勾上,一切回覆正常。菩薩保佑,謝天謝地。。。。。
慣例,過程圖,標題圖get~
我們的比賽demo,辛苦我們的美術小哥,用了3、4天拼出來的場景。
下期有緣再見~
附錄
1、為了參加這次比賽,專門抽出時間去看了rtx 相關。中間用dx12 自己實現過純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;
}