【UE4】 GTAO

GTAO和HBAO比主要是加了cosine weight和multi-bounce拟合两个方面的改进
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

问题1:GTAO假设normal在sample slice上
解决:
在这里插入图片描述

其中 n p n_p np n n n在sample slice的投影

在这里插入图片描述

	float3 PlaneNormal = normalize(cross(float3(ScreenDir.xy, 0), ViewDir));
	float3 ProjNormal = ViewSpaceNormal - PlaneNormal * dot(ViewSpaceNormal, PlaneNormal);

首先通过ViewDir(图中 θ \theta θ)和screen dir叉乘得到Plane normal(图中Sn),再用Gram-Schmidt得到 n p n_p np

4.26版本的引擎的GTAO虽然有相应的代码,但好像无法使用,编辑器里没有,用r.AmbientMethod也没用。

结果:
在这里插入图片描述

PS:衰减,随着距离增加而衰减,如下,随着距离增加,之前Angle的权重不断增加,距离达到30.0后不再影响。

	const float WorldRadius = 30.0f;
	float AttenFactor = 2.0 / (WorldRadius * WorldRadius);
	......
	for (uint i = 0; i < NumSteps; i++)
	{
		......
		FallOff = saturate(LenDsSquare * AttenFactor);
		Ang = lerp(Ang, BestAng.x, FallOff);
		......
	}

请添加图片描述
请添加图片描述
PS:Muti-bounce

float MutiBounce(float AO,float3 albedo)
{
	float3 a = 2.0404 * albedo - 0.3324;
	float3 b = -4.7951 * albedo + 0.6417;
	float3 c = 2.7552 * albedo + 0.6903;

	return max(AO, ((AO * a + b) * AO + c) * AO);
}

在这里插入图片描述

PS:

float Thickness = 0.9f;

BestAng.x = (Ang > BestAng.x) ? Ang : lerp(Ang, BestAng.x, Thickness);

在这里插入图片描述

在这里插入图片描述

base color with AO +without AO

请添加图片描述
请添加图片描述

GTAO.usf
#include "/Engine/Private/Common.ush"
#include "/Engine/Private/SceneTexturesCommon.ush"


void MainVS(
    in float4 InPosition : ATTRIBUTE0,
    in float2 InUV : ATTRIBUTE1,
	out float2 OutUV: TEXCOORD0,
    out float4 OutPosition : SV_POSITION
)
{
	OutUV = InUV;
	OutPosition = float4(InPosition.x * 2.0 - 1.0, 1.0 - 2.0 * InPosition.y, 0, 1);
}

#define PI_HALF (PI*0.5)

static const float c_NumAngles = 8.0f;
static const uint GTAO_NUMTAPS = 10;
float2 Inv_View_Extent;

float4 GTAOParams[2];

//交错梯度噪声
//http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
//https://bartwronski.com/2016/10/30/dithering-part-three-real-world-2d-quantization-dithering/
float InterleavedGradientNoise(float2 iPos)
{
	return frac(52.9829189f * frac((iPos.x * 0.06711056) + (iPos.y * 0.00583715)));
}

float2 GetRandomAngleOffset(uint2 iPos)
{
	iPos.y = 4096 - iPos.y;
	float Angle = InterleavedGradientNoise(float2(iPos));
	float Offset = (1.0 / 4.0) * ((iPos.y - iPos.x) & 3);
	return float2(Angle, Offset);
}
float3 GetRandomVector(uint2 iPos)
{
	iPos.y = 16384 - iPos.y;

	float3 RandomVec = float3(0, 0, 0);
	float3 RandomTexVec = float3(0, 0, 0);
	float ScaleOffset;

	float TemporalCos = GTAOParams[0].x;
	float TemporalSin = GTAOParams[0].y;
	TemporalCos = 0.705;
	TemporalSin = 0.705;
	float GradientNoise = InterleavedGradientNoise(float2(iPos));

	RandomTexVec.x = cos((GradientNoise * PI));
	RandomTexVec.y = sin((GradientNoise * PI));

	ScaleOffset = (1.0 / 4.0) * ((iPos.y - iPos.x) & 3);
//	ScaleOffset = (1.0/5.0)  *  (( iPos.y - iPos.x) % 5);

	RandomVec.x = dot(RandomTexVec.xy, float2(TemporalCos, -TemporalSin));
	RandomVec.y = dot(RandomTexVec.xy, float2(TemporalSin, TemporalCos));
	RandomVec.z = frac(ScaleOffset + GTAOParams[0].z);

	return RandomVec;
}

float2 SearchForLargestAngleDual(uint NumSteps, float2 BaseUV,  float2 ScreenDir, float3 ViewPos, float3 ViewDir)
{
	float LenDsSquare, LenDsInv,Ang,FallOff;
	float3 Ds;
	float2 SceneDepths = 0;
	float2 BestAng = float2(-1, -1);
	
	const float WorldRadius = 30.0f;
	float AttenFactor = 2.0 / (WorldRadius * WorldRadius);
	
	float Thickness = 0.9f;
	for (uint i = 0; i < NumSteps; i++)
	{
		float fi = (float) i;
		
		float2 UVOffset = ScreenDir * (fi + 1.0) * Inv_View_Extent; 
		UVOffset.y *= -1;
		
		float4 UV2 = BaseUV.xyxy + float4(UVOffset.xy, -UVOffset.xy);
		
		SceneDepths.x = ConvertFromDeviceZ(Texture2DSample(SceneTexturesStruct.SceneDepthTexture, SceneTexturesStruct_SceneDepthTextureSampler, UV2.xy).r);
		SceneDepths.y = ConvertFromDeviceZ(Texture2DSample(SceneTexturesStruct.SceneDepthTexture, SceneTexturesStruct_SceneDepthTextureSampler, UV2.zw).r);
		
		//正向 slider54
		Ds = ScreenToViewPos(UV2.xy, SceneDepths.x) - ViewPos;	
		LenDsSquare = dot(Ds, Ds);
		LenDsInv = rsqrt(LenDsSquare + 0.0001);
		Ang = dot(Ds, ViewDir) * LenDsInv;
		
		FallOff = saturate(LenDsSquare * AttenFactor);
		Ang = lerp(Ang, BestAng.x, FallOff);
		
		BestAng.x = (Ang > BestAng.x) ? Ang : lerp(Ang, BestAng.x, Thickness);;

		//反向
		Ds = ScreenToViewPos(UV2.zw, SceneDepths.x) - ViewPos;
		LenDsSquare = dot(Ds, Ds);
		LenDsInv = rsqrt(LenDsSquare + 0.0001);
		Ang = dot(Ds, ViewDir) * LenDsInv;
		
		FallOff = saturate(LenDsSquare * AttenFactor);
		Ang = lerp(Ang, BestAng.x, FallOff);
		
		BestAng.y = (Ang > BestAng.y) ? Ang : lerp(Ang, BestAng.y, Thickness);;
	}
	BestAng.x = acosFast(clamp(BestAng.x, -1.0, 1.0));
	BestAng.y = acosFast(clamp(BestAng.y, -1.0, 1.0));
	
	return BestAng;

}
float ComputeInnerIntegral(float2 UV, float2 Angles, float2 ScreenDir, float3 ViewDir, float3 ViewSpaceNormal, float SceneDepth)
{
	// Given the angles found in the search plane we need to project the View Space Normal onto the plane defined by the search axis and the View Direction and perform the inner integrate
	float3 PlaneNormal = normalize(cross(float3(ScreenDir.xy, 0), ViewDir));
	float3 Perp = cross(ViewDir, PlaneNormal);
	float3 ProjNormal = ViewSpaceNormal - PlaneNormal * dot(ViewSpaceNormal, PlaneNormal);

	float LenProjNormal = length(ProjNormal) + 0.000001f;
	float RecipMag = 1.0f / (LenProjNormal);

	float CosAng = dot(ProjNormal, Perp) * RecipMag;
	float Gamma = acosFast(CosAng) - PI_HALF;
	float CosGamma = dot(ProjNormal, ViewDir) * RecipMag;
	float SinGamma = CosAng * -2.0f;

	// clamp to normal hemisphere 
	Angles.x = Gamma + max(-Angles.x - Gamma, -(PI_HALF));
	Angles.y = Gamma + min(Angles.y - Gamma, (PI_HALF));

	float AO = ((LenProjNormal) * 0.25 *
					    ((Angles.x * SinGamma + CosGamma - cos((2.0 * Angles.x) - Gamma)) +
				  	      (Angles.y * SinGamma + CosGamma - cos((2.0 * Angles.y) - Gamma))));

	return AO;
}
float MutiBounce(float AO,float3 albedo)
{
	float3 a = 2.0404 * albedo - 0.3324;
	float3 b = -4.7951 * albedo + 0.6417;
	float3 c = 2.7552 * albedo + 0.6903;

	return max(AO, ((AO * a + b) * AO + c) * AO);
}

void MainPS(
	in float2 UV: TEXCOORD0,
    out float4 OutColor : SV_Target0
)
{
	int2 iPos = int2(UV / Inv_View_Extent);
	
	float2 TexUV = UV;
	
	float DeviceZ = Texture2DSample(SceneTexturesStruct.SceneDepthTexture, SceneTexturesStruct_SceneDepthTextureSampler, UV).r;
	float SceneDepth = ConvertFromDeviceZ(DeviceZ);
	
	float3 ViewSpacePos = ScreenToViewPos(UV, SceneDepth);
	
	float3 WorldNormal = Texture2DSample(SceneTexturesStruct.GBufferATexture, SceneTexturesStruct_GBufferATextureSampler, UV).xyz;
	float3 ViewSpaceNormal = normalize(mul(WorldNormal, (float3x3) View.TranslatedWorldToView));
	
	float3 ViewDir = normalize(-ViewSpacePos.xyz);
	
	uint NumAngles = (uint) c_NumAngles;
	float SinDeltaAngle = sin(PI / c_NumAngles);
	float CosDeltaAngle = cos(PI / c_NumAngles);
	
	float3 RandomAndOffset = GetRandomVector(iPos);
	float2 RandomVec = RandomAndOffset.xy;
	float Offset = RandomAndOffset.z;
	
	float Sum = 0.0;
	
	float2 ScreenDir = float2(RandomVec.x, RandomVec.y);
	for (uint Angle = 0; Angle < NumAngles;Angle++)
	{
		float2 Angles = SearchForLargestAngleDual(GTAO_NUMTAPS, TexUV, ScreenDir, ViewSpacePos, ViewDir);
		
		Sum += ComputeInnerIntegral(TexUV, Angles, ScreenDir, ViewDir, ViewSpaceNormal, SceneDepth);
		
		// Rotate for the next angle
		float2 TempScreenDir = ScreenDir.xy;
		ScreenDir.x = (TempScreenDir.x * CosDeltaAngle) + (TempScreenDir.y * -SinDeltaAngle);
		ScreenDir.y = (TempScreenDir.x * SinDeltaAngle) + (TempScreenDir.y * CosDeltaAngle);
	}
	
	float AO = Sum;

	AO = AO / ((float) NumAngles);
	AO *= 2.0 / PI;
	
	float3 baseColor = Texture2DSample(SceneTexturesStruct.GBufferCTexture, SceneTexturesStruct.PointClampSampler, UV).rgb;
	AO = MutiBounce(AO, baseColor);
	AO = saturate(AO);
	
	OutColor = float4(AO, AO, AO, 1.0);
}

GTAO.cpp

void FDeferredShadingSceneRenderer::RenderGTAO(FRDGBuilder& GraphBuilder, FRDGTextureRef SceneColor, TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTexturesWithDepth)
{
	
	for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
	{
		FViewInfo& View = Views[ViewIndex];
		const FVector2D InverseViewExtent(
			1.0f / static_cast<float>(View.ViewRect.Max.X - View.ViewRect.Min.X),
			1.0f / static_cast<float>(View.ViewRect.Max.Y - View.ViewRect.Min.Y));

		RDG_EVENT_SCOPE(GraphBuilder, "SimpleUseRenderGraph(ViewId=%d)", ViewIndex);

		const FScreenPassTextureViewport GTAOTextureViewport(SceneColor);

		FRDGTextureDesc OutputDesc = SceneColor->Desc;
		OutputDesc.Reset();
		OutputDesc.Extent = GTAOTextureViewport.Extent;
		OutputDesc.Flags |= TexCreate_None;
		OutputDesc.ClearValue = FClearValueBinding(FLinearColor::Transparent);

		const FScreenPassRenderTarget Output(GraphBuilder.CreateTexture(OutputDesc, TEXT("BloomBlurTex")), GTAOTextureViewport.Rect, ERenderTargetLoadAction::ENoAction);

		TShaderMapRef<FGTAOShaderPS> PixelShader(View.ShaderMap);
		TShaderMapRef<FGTAOShaderVS> VertexShader(View.ShaderMap);

		FGTAOParameters* PassParameters = GraphBuilder.AllocParameters<FGTAOParameters>();
		PassParameters->SceneTextures = SceneTexturesWithDepth;
		PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding();

		uint32 TemporalFrame = 0;
		uint32 Frame = 0;
		const FSceneViewState* ViewState = static_cast<const FSceneViewState*>(View.State);
		if (ViewState)
		{
			TemporalFrame = ViewState->GetCurrentUnclampedTemporalAASampleIndex();
			Frame = ViewState->GetFrameIndex();
		}
		
		const float Rots[6] = { 60.0f, 300.0f, 180.0f, 240.0f, 120.0f, 0.0f };
		const float Offsets[4] = { 0.1f, 0.6f, 0.35f, 0.85f };
		float TemporalAngle = Rots[TemporalFrame % 6] * (PI / 360.0f);
		float SinAngle, CosAngle;
		FMath::SinCos(&SinAngle, &CosAngle, TemporalAngle);

		FMyGTAORenderingParameters Params;
		Params.GTAOParams[0] = FVector4(CosAngle, SinAngle, Offsets[(TemporalFrame / 6) % 4] * 0.25, Offsets[TemporalFrame % 4]);
		Params.GTAOParams[1] = FVector4(0, 0, 1, 1);
		Params.Inv_View_Extent = InverseViewExtent;

		GraphBuilder.AddPass(
			RDG_EVENT_NAME("SimpleTest"),
			PassParameters,
			ERDGPassFlags::Raster,
			[&View, Params,PixelShader, VertexShader](FRHICommandList& RHICmdList)
			{

				RenderFullScreenQuadGTAO(RHICmdList, View.ShaderMap, PixelShader, VertexShader, Params, View.ViewRect);
			});

		//Pass2 模糊
		FMyBloomBlurInputs BlurInputs;
		BlurInputs.Filter = Output;
		BlurInputs.BlurRadi = 3;
		BlurInputs.Tint = FLinearColor(1.0, 1.0, 1.0, 1.0);

		FScreenPassTexture Res = AddMyBloomBlurMultiPass(GraphBuilder, View, BlurInputs);

		//Pass3 显示最终结果
		FRHISamplerState* SamplerState = TStaticSamplerState<SF_Bilinear, AM_Border, AM_Border, AM_Clamp>::GetRHI();

		FOutGTAOPS::FParameters* PassResParameters = GraphBuilder.AllocParameters<FOutGTAOPS::FParameters>();
		PassResParameters->SceneTextures = SceneTexturesWithDepth;
		PassResParameters->AOInputSampler = SamplerState;
		PassResParameters->AOInputTexture = Res.Texture;
		PassResParameters->RenderTargets[0] = FRenderTargetBinding(SceneColor, ERenderTargetLoadAction::ENoAction);
		
		TShaderMapRef<FOutGTAOPS> PixelOutShader(View.ShaderMap);
		TShaderMapRef<FOutGTAOVS> VertexOutShader(View.ShaderMap);

		AddDrawScreenPass(
			GraphBuilder,
			RDG_EVENT_NAME("Final GTAO", TEXT("Final GTAO My")),
			View,
			FScreenPassTextureViewport(Res),
			FScreenPassTextureViewport(SceneColor),
			FScreenPassPipelineState(VertexOutShader, PixelOutShader, TStaticBlendState<>::GetRHI(), TStaticDepthStencilState<false, CF_Always>::GetRHI()),
			PassResParameters,
			[PixelOutShader, PassResParameters](FRHICommandListImmediate& RHICmdList)
			{
				SetShaderParameters(RHICmdList, PixelOutShader, PixelOutShader.GetPixelShader(), *PassResParameters);
			});
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值