PathTracingKernel 精简版

void PathTracingKernel(
	in uint RenderPassIndex,
	float3 TranslatedWorldPosition,
	float3 ShadingNormal,
	inout RandomSequence RandSequence,
	inout uint SampleIndex,
	inout bool bIsValidSample,
	inout float3 RadianceValue,
	inout float3 RadianceDirection,
	inout float3 DirectLightingIrradiance,
#ifdef LIGHTMAP_PATH_TRACING_MAIN_RG
	inout FL2SHAndCorrection DirectLightingSH,
#else
	inout FThreeBandSHVectorRGBFloat DirectLightingSH,
#endif
	inout float LuminanceForFirstBounceRayGuiding,
	inout float3 SkyLightBentNormal,
	inout float4 PrimaryRandSample)
{
	uint2 LaunchIndex = uint2(0, 0);
	
#if USE_IRRADIANCE_CACHING
	bool bShouldEmitGeometryHitPoint = false;
	
	FFinalGatherHitPoint IrradianceCacheFinalGatherHitPoint = (FFinalGatherHitPoint)0;	
	float RadianceProbe_Pdf = 1.0f / (2 * PI);
	
	uint NewEntrySize = 32;
	int NearestCacheEntryID = -1;
#endif

	float3 Radiance = 0;
	
	// GPULightmass's SampleEmitter(): generate a fake camera ray hitting the texel
	FRayDesc Ray;
	Ray.Origin = TranslatedWorldPosition + ShadingNormal;
	Ray.Direction = -ShadingNormal;
	Ray.TMin = -0.01f;
	Ray.TMax = 1.01f;
	
	// This array will hold a CDF for light picking
	// Seed the array with a uniform CDF at first so that we always have a valid CDF
	float LightPickingCdf[RAY_TRACING_LIGHT_COUNT_MAXIMUM];
	
	Ray.Direction = normalize(Ray.Direction);
	
	// path state variables (these cary information between bounces)
	float3 PathThroughput = 1.0;
	float PathRoughness = 0;

	// number of directly visible lights for the first bounce
	uint NumVisibleLights = SceneVisibleLightCount;
	
	for (int Bounce = 0; Bounce <= MaxBounces; Bounce++)
	{		
		const bool bIsCameraRay = Bounce == 0;
		const bool bIsLastBounce = Bounce == MaxBounces;
		const bool bIncludeEmissive = (EnableDirectLighting != 0 || Bounce > 1) &&
									  (EnableEmissive       != 0 || bIsCameraRay);
		FPathTracingPayload Payload = (FPathTracingPayload)0;
		
		if (bIsCameraRay)
		{
			// GPULightmass fakes a 'camera' ray that hits the lightmap texel perpendicularly
			Payload.TranslatedWorldPos = TranslatedWorldPosition;
			Payload.WorldNormal = ShadingNormal;
			Payload.WorldGeoNormal = ShadingNormal;
			Payload.WorldSmoothNormal = ShadingNormal;
			Payload.Radiance = float3(0, 0, 0);
			Payload.BaseColor = float3(1, 1, 1);
			Payload.DiffuseColor = float3(1, 1, 1);
			Payload.SpecularColor = float3(0, 0, 0);
			Payload.Specular = 0;
			Payload.Roughness = 1;
			Payload.Metallic = 0;
			Payload.Ior = 1;
			Payload.CustomData = float4(0, 0, 0, 0);
			Payload.Opacity = 1;
			Payload.ShadingModelID = SHADINGMODELID_DEFAULT_LIT;
			Payload.BlendingMode = RAY_TRACING_BLEND_MODE_OPAQUE;
			Payload.PrimitiveLightingChannelMask = 0b111;
			Payload.HitT = 1.0f;
			Payload.SetFrontFace();
		}
		else
		{
			Payload = TraceTransparentRay(Ray, false , bIsLastBounce, bIncludeEmissive, LaunchIndex, NumVisibleLights, RandSequence, PathThroughput, Radiance);
		}
		
		// As soon as the path is blurry enough, we can get away with diffuse sampling only
		const bool bSimplifySSS = PathRoughness >= 0.15;

		FLightLoopCount LightLoopCount = LightGridLookup(Payload.TranslatedWorldPos);

		// Choose a random number for both Light sampling and BxDF sampling
		float4 RandSample = RandomSequence_GenerateSample4D(RandSequence);

		// Does this material require NEE? (will be false if MaterialPdf is always +inf)
		const bool bIsNeeValid = IsNeeValidMaterial(Payload);

		const bool bDoLightLoop = EnableDirectLighting != 0 || Bounce > 0;

		float LightPickingCdfSum = 0;

		// If we are using Light sampling and the material can use it ...
		if (MISMode != 0 && bIsNeeValid && SceneLightCount > 0 && bDoLightLoop)
		{
			// Choose a light and sample it
			float3 TranslatedWorldPos = Payload.TranslatedWorldPos;
			float3 WorldNormal = Payload.WorldNormal;
			uint PrimitiveLightingChannelMask = Payload.PrimitiveLightingChannelMask;

			bool IsTransmissiveMaterial = ENABLE_TRANSMISSION && Payload.IsMaterialTransmissive();

			for (uint Index = 0, Num = LightLoopCount.NumLights; Index < Num; ++Index)
			{
				uint LightIndex = GetLightId(Index, LightLoopCount);
				LightPickingCdfSum += EstimateLight(LightIndex, TranslatedWorldPos, WorldNormal, PrimitiveLightingChannelMask, IsTransmissiveMaterial);
				LightPickingCdf[Index] = LightPickingCdfSum;
			}

			if (LightPickingCdfSum > 0)
			{
				// init worked
				int LightId;
				float LightPickPdf = 0;

				SelectLight(RandSample.x * LightPickingCdfSum, LightLoopCount.NumLights, LightPickingCdf, LightId, LightPickPdf);

				LightId = GetLightId(LightId, LightLoopCount);

				FLightSample LightSample = SampleLight(LightId, RandSample.yz, TranslatedWorldPos, WorldNormal);

				LightPickPdf /= LightPickingCdfSum;

				LightSample.RadianceOverPdf /= LightPickPdf;
				LightSample.Pdf *= LightPickPdf;
				if (LightSample.Pdf > 0)
				{
					float3 Visibility = float3(1, 1, 1);
					
					if (CastsShadow(LightId))
					{
						// for transmissive materials, bias the position to the other side of the surface if the light is coming from behind
						const float SignedPositionBias = IsTransmissiveMaterial ? sign(dot(Payload.WorldNormal, LightSample.Direction)) : 1.0;

						FRayDesc LightRay;
						LightRay.Origin = TranslatedWorldPos;
						LightRay.TMin = 0;
						LightRay.Direction = LightSample.Direction;
						LightRay.TMax = LightSample.Distance;
						ApplyRayBias(LightRay, Payload.HitT, SignedPositionBias * Payload.WorldGeoNormal);
					
						float AvgRoughness = ApproximateCaustics ? GetAverageRoughness(Payload) : 0.0;

						Visibility = TraceTransparentVisibilityRay(LightRay, LaunchIndex, AvgRoughness);
						LightSample.RadianceOverPdf *= Visibility;
					}

					// #dxr_todo: Is it cheaper to fire the ray first? Or eval the material first?
					if (any(LightSample.RadianceOverPdf > 0))
					{
						// Evaluate material
						FMaterialEval MaterialEval = EvalMaterial(Ray.Direction, LightSample.Direction, Payload, Bounce == 0);

						// Record the contribution
						float3 LightContrib = PathThroughput * LightSample.RadianceOverPdf * MaterialEval.Weight * MaterialEval.Pdf;						
						float3 BentNormalVector = LightSample.Direction / LightSample.Pdf / PI * Visibility;
						if (MISMode == 2)
						{
							LightContrib *= MISWeightRobust(LightSample.Pdf, MaterialEval.Pdf);
							BentNormalVector *= MISWeightRobust(LightSample.Pdf, MaterialEval.Pdf);
						}

						// Record the contribution
						if (Bounce > 0)
						{
							AccumulateRadiance(Radiance, LightContrib);
							LuminanceForFirstBounceRayGuiding += Luminance(LightContrib);
						}
						else
						{
							// GPU Lightmass records contribution of direct lighting separately, and only for static lights
							if (!IsStationary(LightId))
							{
							#ifdef LIGHTMAP_PATH_TRACING_MAIN_RG
								float TangentZ = saturate(dot(LightSample.Direction, Payload.WorldNormal));
								DirectLightingSH.AddIncomingRadiance(Luminance(LightContrib), LightSample.Direction, TangentZ);
								DirectLightingIrradiance += LightContrib * TangentZ;
							#else								
								DirectLightingSH.R.V0 = SHBasisFunction3Float(LightSample.Direction).V0 * LightContrib.r;
								DirectLightingSH.R.V1 = SHBasisFunction3Float(LightSample.Direction).V1 * LightContrib.r;
								DirectLightingSH.R.V2 = SHBasisFunction3Float(LightSample.Direction).V2 * LightContrib.r;
								DirectLightingSH.G.V0 = SHBasisFunction3Float(LightSample.Direction).V0 * LightContrib.g;
								DirectLightingSH.G.V1 = SHBasisFunction3Float(LightSample.Direction).V1 * LightContrib.g;
								DirectLightingSH.G.V2 = SHBasisFunction3Float(LightSample.Direction).V2 * LightContrib.g;
								DirectLightingSH.B.V0 = SHBasisFunction3Float(LightSample.Direction).V0 * LightContrib.b;
								DirectLightingSH.B.V1 = SHBasisFunction3Float(LightSample.Direction).V1 * LightContrib.b;
								DirectLightingSH.B.V2 = SHBasisFunction3Float(LightSample.Direction).V2 * LightContrib.b;
							#endif
							}

							if (IsStationary(LightId) && IsEnvironmentLight(LightId) && CastsShadow(LightId))
							{
								SkyLightBentNormal += BentNormalVector;
							}
						}
					}
				}
			}
		}
	
		// Sample material
		FMaterialSample MaterialSample = SampleMaterial(Ray.Direction, Payload, RandSample, Bounce == 0);

		float3 NextPathThroughput = PathThroughput * MaterialSample.Weight;

		float ContinuationProb = sqrt(saturate(max(NextPathThroughput.x, max(NextPathThroughput.y, NextPathThroughput.z)) / max(PathThroughput.x, max(PathThroughput.y, PathThroughput.z))));
		if (ContinuationProb < 1)
		{
			// If there is some chance we should terminate the ray, draw an extra random value
			float RussianRouletteRand = RandSample.w; // SampleMaterial does not use this value at the moment
			//RussianRouletteRand = RandomSequence_GenerateSample1D(RandSequence);
			if (RussianRouletteRand >= ContinuationProb)
			{
				// stochastically terminate the path
				break;
			}
			PathThroughput = NextPathThroughput / ContinuationProb;
		}
		else
		{
			PathThroughput = NextPathThroughput;
		}

		// Update ray according to material sample
		Ray.Origin = Payload.TranslatedWorldPos;
		Ray.Direction = MaterialSample.Direction;
		Ray.TMin = 0;
		Ray.TMax = POSITIVE_INFINITY;

		if (Bounce == 0)
		{
			RadianceDirection = Ray.Direction;
			PrimaryRandSample = RandSample;
		}
		
		// If we are using Material sampling for lights
		if (MISMode != 1 && bDoLightLoop)
		{
			// Check which lights can be seen by the material ray and trace a dedicated shadow ray
			// While it would be possible to just loop around and use the indirect ray to do this, it would prevent the application
			// of shadow ray specific logic for transparent shadows or various per light tricks like shadow casting
			const bool bUseMIS = MISMode == 2 && LightPickingCdfSum > 0;
			for (uint Index = 0, Num = LightLoopCount.NumMISLights; Index < Num; ++Index)
			{
				uint LightId = GetLightId(Index, LightLoopCount);
				if ((Payload.PrimitiveLightingChannelMask & GetLightingChannelMask(LightId)) == 0)
				{
					// light does not affect the current ray
					continue;
				}

				FLightHit LightResult = TraceLight(Ray, LightId);

				if (LightResult.IsMiss())
				{
					continue;
				}

				float3 LightContrib = PathThroughput * LightResult.Radiance;
				float3 BentNormalVector = PathThroughput * MaterialSample.Direction / PI;
				
				if (bUseMIS)
				{
					float PreviousCdfValue = 0.0;
					BRANCH if (Index > 0)
					{
						PreviousCdfValue = LightPickingCdf[Index - 1];
					}
					float LightPickPdf = (LightPickingCdf[Index] - PreviousCdfValue) / LightPickingCdfSum;

					LightContrib *= MISWeightRobust(MaterialSample.Pdf, LightResult.Pdf * LightPickPdf);

					// Separate direct lighting (bounce 0) for GPU Lightmass
					BentNormalVector *= MISWeightRobust(MaterialSample.Pdf, LightResult.Pdf * LightPickPdf);
				}

				if (any(LightContrib > 0))
				{
					if (CastsShadow(LightId))
					{
						FRayDesc LightRay = Ray;
						LightRay.TMax = LightResult.HitT;
						float3 Visibility = TraceTransparentVisibilityRay(LightRay, LaunchIndex, PathRoughness);
						LightContrib *= Visibility;
						
						// Separate direct lighting (bounce 0) for GPU Lightmass
						BentNormalVector *= Visibility;
					}

					if (Bounce > 0)
					{
						// the light made some contribution, and there was nothing along the shadow ray
						AccumulateRadiance(Radiance, LightContrib);
					}
					else // Bounce == 0
					{
						if (!IsStationary(LightId))
						{
						#ifdef LIGHTMAP_PATH_TRACING_MAIN_RG
							float TangentZ = saturate(dot(MaterialSample.Direction, Payload.WorldNormal));
							DirectLightingSH.AddIncomingRadiance(Luminance(LightContrib), MaterialSample.Direction, TangentZ);
							DirectLightingIrradiance += LightContrib * TangentZ;
						#else								
							DirectLightingSH.R.V0 = SHBasisFunction3Float(MaterialSample.Direction).V0 * LightContrib.r;
							DirectLightingSH.R.V1 = SHBasisFunction3Float(MaterialSample.Direction).V1 * LightContrib.r;
							DirectLightingSH.R.V2 = SHBasisFunction3Float(MaterialSample.Direction).V2 * LightContrib.r;
							DirectLightingSH.G.V0 = SHBasisFunction3Float(MaterialSample.Direction).V0 * LightContrib.g;
							DirectLightingSH.G.V1 = SHBasisFunction3Float(MaterialSample.Direction).V1 * LightContrib.g;
							DirectLightingSH.G.V2 = SHBasisFunction3Float(MaterialSample.Direction).V2 * LightContrib.g;
							DirectLightingSH.B.V0 = SHBasisFunction3Float(MaterialSample.Direction).V0 * LightContrib.b;
							DirectLightingSH.B.V1 = SHBasisFunction3Float(MaterialSample.Direction).V1 * LightContrib.b;
							DirectLightingSH.B.V2 = SHBasisFunction3Float(MaterialSample.Direction).V2 * LightContrib.b;
						#endif
						}

						if (IsStationary(LightId) && IsEnvironmentLight(LightId) && CastsShadow(LightId))
						{
							SkyLightBentNormal += BentNormalVector;
						}
					}
					
					LuminanceForFirstBounceRayGuiding += Luminance(LightContrib);
				}
			}
			
		}

		// from this point on, we don't need to include lights in the trace call
		// because NEE handled it for us
		NumVisibleLights = 0;
	}
	
	RadianceValue = Radiance;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值