DXR教程一
这个是Nvidia做的DXR的教程之一,总共14篇,主要是介绍DXR Api的使用,这套API还是非常繁琐的,如果一直关注细节的话很容易理不清思路。
事实上只需要关注TraceRay这个函数就可以很好的了解DXR的整体。这个流程中新的两个部分:加速结构(BLAS和TLAS)和ShaderTable都可以说是为TraceRay服务的,它们用于说明当ray与AccelerationStructure中的物体相交后执行哪部分程序。
Template<payload_t>
void TraceRay(RaytracingAccelerationStructure AccelerationStructure,
uint RayFlags,
uint InstanceInclusionMask,
uint RayContributionToHitGroupIndex,
uint MultiplierForGeometryContributionToHitGroupIndex,
uint MissShaderIndex,
RayDesc Ray,
inout payload_t Payload);
第一个参数是使用的Top-Level Acceleration Structure。
第二个参数是Ray Flags,用于定义透明、裁剪一些提前操作。
第三个参数则用于拒绝某些Instance,
if(!((InstanceInclusionMask & InstanceMask) & 0xff)) { ignore intersection }
pInstanceDescs[i].InstanceMask = 0xFF;
当Instance的Mask设为0时,该instance会被忽略。
第四个参数是Ray Index,它的主要作用是索引次级光线。
第五个参数是两个Geometry的hit shader的距离。
教程13中的样例场景有3个实例,其中第一个实例有两个Geometry,一个是三角形,另一个是Plane,另外两个实例为三角形。同时击中后会产生一个再次发射一条阴影光线。
当RayGen Shader发射一条光线击中物体后,要确定接下来执行那个Hit Shader ,计算公式:
entryIndex =
InstanceContributionToHitGroupIndex +
GeometryIndex * MultiplierForGeometryContributionToShaderIndex +
RayContributionToHitGroupIndex
假定击中的是Plane,那么就要执行hitshader,我们要去找hitShaderGroup的位置,HitGroupTable如下,它的起始的Offset是3个ShaderTableEntry的大小,包含8个HitShader。
size_t hitOffset = 3 * mShaderTableEntrySize;
raytraceDesc.HitGroupTable.StartAddress = mpShaderTable->GetGPUVirtualAddress() + hitOffset;
raytraceDesc.HitGroupTable.StrideInBytes = mShaderTableEntrySize;
raytraceDesc.HitGroupTable.SizeInBytes = mShaderTableEntrySize * 8; // 8 hit-entries
找到HitShaderGroup的位置后,我们查找HitShader的索引。在上面EntryIndex的计算公式中,有两个是Shader中的参数:MultiplierForGeometryContributionToShaderIndex,
RayContributionToHitGroupIndex这两个在shader中指定,对于前者,是Instance中Geometry的数量,对于后者是Ray的index,因为是第一条光线所以Index=0。
InstanceContributionToHitGroupIndex在Top-Level AC中指定,用于说明当前的Instance在HitGroup中的位置,
pInstanceDescs[0].InstanceID = 0;
pInstanceDescs[0].InstanceContributionToHitGroupIndex = 0;
``````
for (int i = 1; i < 3; i++)
{
pInstanceDescs[i].InstanceID = i;
pInstanceDescs[i].InstanceContributionToHitGroupIndex = (i * 2) + 2;
``````
}
3个Instace的位置分别为0,4,6.
GeometryIndex用于说明这个Geometry在Instace中的位置,这个在Bottom-Level AC中指定,对于Instace0,Plane在其中的位置为1。
经过计算得到HitShader的Index为0+1*2+0=2,即执行hitGroup中的第三个Shader。
它的代码如下:
[shader("closesthit")]
void planeChs(inout RayPayload payload, in BuiltInTriangleIntersectionAttributes attribs)
{
``````
ShadowPayload Shadowpayload;
TraceRay(gRtScene, 0 /*rayFlags*/, 0xFF, 1 /* ray index*/, 0, 1, ray, Shadowpayload);
float factor = Shadowpayload.hit ? 0.1 : 1.0;
payload.color = float4(0.9f, 0.9f, 0.9f, 1.0f) * factor;
}
也就是再发射次级光线进行阴影判断,所以Ray Index从0变成1,RayContributionToHitGroupIndex的值为1。
倒数第三个参数说明,如果未击中执行则执行MissGroup中的第2个Shader,index=1。
假设击中了Instance3,那么结果为:
entryIndex =
InstanceContributionToHitGroupIndex(6) +
GeometryIndex(0) * MultiplierForGeometryContributionToShaderIndex(0) +
RayContributionToHitGroupIndex(1)
即执行第8个shader,Index=7。
DXR剩下的部分(PipelineState,Resources等)类比光栅流程就可以了。
DXR教程二
第二份教程是在Falcor框架的基础上写的,同样是14节,与第一份教程相比,Falcor框架简化了DXR的操作,第一份教程的上千行代码在Falor框架下十几行就可以实现。
pipeline->setPass(0, LightProbeGBufferPass::create());
pipeline->setPass(1, GGXGlobalIlluminationPass::create("HDRColorOutput"));
pipeline->setPass(2, SimpleAccumulationPass::create("HDRColorOutput"));
pipeline->setPass(3, SimpleToneMappingPass::create("HDRColorOutput", ResourceManager::kOutputChannel));
总共四个Pass,第一个Pass是光栅化流程,输出一个GBuffer,包阔PosWorl,Normal,diffuse等信息。第2个Pass是RayTracing的核心,第三个Pass进行temporal accumulation,第四个Pass将结果输出。
第三个Pass的思路比较朴素,即如果前一帧和当前帧场景未发生改变,可以将前一帧(prevColor)的结果累加到当前帧(curColor),静止的帧数越多(gAccumCount),场景的噪点就会越小。
(gAccumCount * prevColor + curColor) / (gAccumCount + 1);
这个方法结合上相机位置的Jitter,不就在降噪的同时顺便进行了类似TAA的操作,Jitter是在第一个Pass中进行的。
第二个Pass的核心代码如下
if (isGeometryValid)
{
// Add any emissive color from primary rays
shadeColor = gEmitMult * pixelEmissive.rgb;
// (Optionally) do explicit direct lighting to a random light in the scene
if (gDoDirectGI)
shadeColor += ggxDirect(randSeed, worldPos.xyz, worldNorm.xyz, V,
difMatlColor.rgb, specMatlColor.rgb, roughness);
// (Optionally) do indirect lighting for global illumination
if (gDoIndirectGI && (gMaxDepth > 0))
shadeColor += ggxIndirect(randSeed, worldPos.xyz, worldNorm.xyz, noMapN,
V, difMatlColor.rgb, specMatlColor.rgb, roughness, 0);
}
如果当前击中点有效的话,进行直接光和间接光的计算。
在计算直接光的时候,为了减小计算量,教程在多个光源中随机选择一个光源计算,
int lightToSample = min(int(nextRand(rndSeed) * gLightsCount), gLightsCount - 1);
``````
// Shoot our shadow ray to our randomly selected light
float shadowMult = float(gLightsCount) * shadowRayVisibility(hit, toLight, gMinT, distToLight);
// Return the Lambertian shading color using the physically based Lambertian term (albedo / pi)
return shadowMult * LdotN * lightIntensity * difColor / M_PI;
在计算间接光时,根据概率选择采样Diffuse还是Spec。
UE4 RTAO
选择了各种RT效果中最简单的RTAO来实现,主要是为了熟悉下UE Ray Tracing的流程,之前在DXR的API文档上已经浪费了够多时间了。
SSAO的问题就是只有屏幕信息,无法判断柱子后有没有物体。
RTAO就没有这个问题
#include "../Common.ush"
#include "../DeferredShadingCommon.ush"
#include "../Montecarlo.ush"
#include "../PathTracing/Utilities/PathTracingRandomSequence.ush"
#include "../SceneTextureParameters.ush"
#include"RayTracingCommon.ush"
#include "RayTracingDeferredShadingCommon.ush"
RaytracingAccelerationStructure TLAS;
RWTexture2D<float> RWAmbientOcclusionMaskUAV;
RWTexture2D<float> RWAmbientOcclusionHitDistanceUAV;
RAY_TRACING_ENTRY_RAYGEN(MyRTAORGS)
{
//recompileShaders changed
const uint gNumRays = 2;
float IntensityLocal = 0.2;
float Visibility = 0.0;
uint2 PixelCoord = DispatchRaysIndex().xy + View.ViewRectMin;
RandomSequence RandSequence;
uint LinearIndex = CalcLinearIndex(PixelCoord);
RandomSequence_Initialize(RandSequence, LinearIndex, View.StateFrameIndex);
FGBufferData GBufferData = GetGBufferDataFromSceneTexturesLoad(PixelCoord);
float DeviceZ = SceneDepthTexture.Load(int3(PixelCoord, 0)).r;
float3 WorldPosition;
float3 CameraDirection;
ReconstructWorldPositionAndCameraDirectionFromDeviceZ(PixelCoord, DeviceZ, WorldPosition, CameraDirection);
float3 WorldNormal = GBufferData.WorldNormal;
float ClosestRayHitDistance = 10000.0;
for (uint index = 0; index < gNumRays;index++)
{
uint DummyVariable;
float2 RandSample = RandomSequence_GenerateSample2D(RandSequence, DummyVariable);
float3 Direction_Tangent;
Direction_Tangent.xy = UniformSampleDiskConcentric(RandSample);
Direction_Tangent.z = sqrt(1.0 - dot(Direction_Tangent.xy, Direction_Tangent.xy));
float3 Direction_World = TangentToWorld(Direction_Tangent.xyz, WorldNormal);
RayDesc Ray;
Ray.Origin = WorldPosition;
Ray.Direction = Direction_World;
Ray.TMin = 0.01;
Ray.TMax = 196.0;
uint RayFlags = 0;
const uint InstanceInclusionMask = RAY_TRACING_MASK_SHADOW | RAY_TRACING_MASK_THIN_SHADOW;
float RayWeight = 1.0;
FMinimalPayload MinimalPayload = TraceVisibilityRay(
TLAS,
RayFlags,
InstanceInclusionMask,
PixelCoord,
Ray);
Visibility += RayWeight * (MinimalPayload.IsMiss() ? 1.0 : (1.0 - IntensityLocal) * (MinimalPayload.HitT / Ray.TMax));
if (MinimalPayload.IsHit())
{
ClosestRayHitDistance = min(ClosestRayHitDistance, MinimalPayload.HitT);
}
}
RWAmbientOcclusionMaskUAV[PixelCoord] = Visibility / float(gNumRays);
RWAmbientOcclusionHitDistanceUAV[PixelCoord] = ClosestRayHitDistance;
}
#include"MyRTAO.h"
#include "DeferredShadingRenderer.h"
#if RHI_RAYTRACING
#include "ClearQuad.h"
#include "SceneRendering.h"
#include "SceneRenderTargets.h"
#include "SceneUtils.h"
#include "RenderTargetPool.h"
#include "RHIResources.h"
#include "UniformBuffer.h"
#include "RHI/Public/PipelineStateCache.h"
#include "Raytracing/RaytracingOptions.h"
#include "Raytracing/RayTracingMaterialHitShaders.h"//Note
#include "SceneTextureParameters.h"
#include "PostProcess/PostProcessing.h"
#include "PostProcess/SceneFilterRendering.h"
class FMyRTAORGS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FMyRTAORGS)
SHADER_USE_ROOT_PARAMETER_STRUCT(FMyRTAORGS, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileRayTracingShadersForProject(Parameters.Platform);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_SRV(RaytracingAccelerationStructure, TLAS)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float>, RWAmbientOcclusionMaskUAV)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float>, RWAmbientOcclusionHitDistanceUAV)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FMyRTAORGS, "/Engine/Private/RayTracing/MyRTAORGS.usf", "MyRTAORGS", SF_RayGen);
void FDeferredShadingSceneRenderer::MyRenderRTAO(
FRDGBuilder& GraphBuilder,
FViewInfo& View,
const FSceneTextureParameters& SceneTextures,
FRDGTextureRef* OutAmbientOcclusionTexture)
#if RHI_RAYTRACING
{
RDG_EVENT_SCOPE(GraphBuilder, "My_RTAO");
IScreenSpaceDenoiser::FAmbientOcclusionInputs DenoiserInputs;
{
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
SceneTextures.SceneDepthTexture->Desc.Extent,
PF_R16F,
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV);
DenoiserInputs.Mask = GraphBuilder.CreateTexture(Desc, TEXT("M_RTAO_TEX"));
DenoiserInputs.RayHitDistance = GraphBuilder.CreateTexture(Desc, TEXT("M_RTAO_Hit_TEX"));
}
FMyRTAORGS::FParameters *PassParameters = GraphBuilder.AllocParameters<FMyRTAORGS::FParameters>();
PassParameters->TLAS = View.RayTracingScene.RayTracingSceneRHI->GetShaderResourceView();
PassParameters->RWAmbientOcclusionMaskUAV = GraphBuilder.CreateUAV(DenoiserInputs.Mask);
PassParameters->RWAmbientOcclusionHitDistanceUAV = GraphBuilder.CreateUAV(DenoiserInputs.RayHitDistance);
PassParameters->ViewUniformBuffer = View.ViewUniformBuffer;
PassParameters->SceneTextures = SceneTextures;
TShaderMapRef<FMyRTAORGS> RayGenerationShader(GetGlobalShaderMap(FeatureLevel));
FIntPoint RayTracingResolution = View.ViewRect.Size();
ClearUnusedGraphResources(RayGenerationShader, PassParameters);
GraphBuilder.AddPass(
RDG_EVENT_NAME("M_RTAO"),
PassParameters,
ERDGPassFlags::Compute,
[PassParameters, this, &View, RayGenerationShader, RayTracingResolution](FRHICommandList& RHICmdList)
{
FRayTracingShaderBindingsWriter GlobalResources;
SetShaderParameters(GlobalResources, RayGenerationShader, *PassParameters);
// TODO: Provide material support for opacity mask
FRayTracingPipelineState* Pipeline = View.RayTracingMaterialPipeline;
//------------------------------------------------------
// Declare default pipeline
FRayTracingPipelineStateInitializer Initializer;
Initializer.MaxPayloadSizeInBytes = 64; // sizeof(FPackedMaterialClosestHitPayload)
FRHIRayTracingShader* RayGenShaderTable[] = { RayGenerationShader.GetRayTracingShader() };
Initializer.SetRayGenShaderTable(RayGenShaderTable);
FRHIRayTracingShader* HitGroupTable[] = { View.ShaderMap->GetShader<FOpaqueShadowHitGroup>().GetRayTracingShader() };
Initializer.SetHitGroupTable(HitGroupTable);
Initializer.bAllowHitGroupIndexing = false; // Use the same hit shader for all geometry in the scene by disabling SBT indexing.
Pipeline = PipelineStateCache::GetAndOrCreateRayTracingPipelineState(RHICmdList, Initializer);
//---------------------------------------------------------
FRHIRayTracingScene* RayTracingSceneRHI = View.RayTracingScene.RayTracingSceneRHI;
RHICmdList.RayTraceDispatch(Pipeline, RayGenerationShader.GetRayTracingShader(), RayTracingSceneRHI, GlobalResources, RayTracingResolution.X, RayTracingResolution.Y);
});
int32 DenoiserMode = 1;
if (DenoiserMode != 0)
{
IScreenSpaceDenoiser::FAmbientOcclusionRayTracingConfig RayTracingConfig;
RayTracingConfig.RayCountPerPixel = 2;
const IScreenSpaceDenoiser* DefaultDenoiser = IScreenSpaceDenoiser::GetDefaultDenoiser();
const IScreenSpaceDenoiser* DenoiserToUse = DenoiserMode == 1 ? DefaultDenoiser : GScreenSpaceDenoiser;
RDG_EVENT_SCOPE(GraphBuilder, "%s%s(AmbientOcclusion) %dx%d",
DenoiserToUse != DefaultDenoiser ? TEXT("ThirdParty ") : TEXT(""),
DenoiserToUse->GetDebugName(),
View.ViewRect.Width(), View.ViewRect.Height());
IScreenSpaceDenoiser::FAmbientOcclusionOutputs DenoiserOutputs = DenoiserToUse->DenoiseAmbientOcclusion(
GraphBuilder,
View,
&View.PrevViewInfo,
SceneTextures,
DenoiserInputs,
RayTracingConfig);
*OutAmbientOcclusionTexture = DenoiserOutputs.AmbientOcclusionMask;
}
else
{
*OutAmbientOcclusionTexture = DenoiserInputs.Mask;
}
}
#else
{
unimplemented();
}
#endif
#endif