2021-09-09 20:42:29 -04:00

250 lines
12 KiB
HLSL

#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/RaytracingFragInputs.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/Common/AtmosphericScatteringRayTracing.hlsl"
// Generic function that handles the reflection code
[shader("closesthit")]
void ClosestHitForward(inout RayIntersection rayIntersection : SV_RayPayload, AttributeData attributeData : SV_IntersectionAttributes)
{
UNITY_XR_ASSIGN_VIEW_INDEX(DispatchRaysIndex().z);
// The first thing that we should do is grab the intersection vertice
IntersectionVertex currentVertex;
GetCurrentIntersectionVertex(attributeData, currentVertex);
// Build the Frag inputs from the intersection vertice
FragInputs fragInput;
BuildFragInputsFromIntersection(currentVertex, rayIntersection.incidentDirection, fragInput);
// Compute the view vector
float3 viewWS = -rayIntersection.incidentDirection;
// Let's compute the world space position (the non-camera relative one if camera relative rendering is enabled)
float3 pointWSPos = fragInput.positionRWS;
// Make sure to add the additional travel distance
float travelDistance = length(fragInput.positionRWS - rayIntersection.origin);
rayIntersection.t = travelDistance;
rayIntersection.cone.width += travelDistance * abs(rayIntersection.cone.spreadAngle);
PositionInputs posInput = GetPositionInput(rayIntersection.pixelCoord, _ScreenSize.zw, fragInput.positionRWS);
// Build the surfacedata and builtindata
SurfaceData surfaceData;
BuiltinData builtinData;
bool isVisible;
GetSurfaceAndBuiltinData(fragInput, viewWS, posInput, surfaceData, builtinData, currentVertex, rayIntersection.cone, isVisible);
// Compute the bsdf data
BSDFData bsdfData = ConvertSurfaceDataToBSDFData(posInput.positionSS, surfaceData);
// No need for SurfaceData after this line
#ifdef HAS_LIGHTLOOP
// Compute the prelight data
PreLightData preLightData = GetPreLightData(viewWS, posInput, bsdfData);
float3 reflected = float3(0.0, 0.0, 0.0);
float reflectedWeight = 0.0;
float3 transmitted = float3(0.0, 0.0, 0.0);
float refractedWeight = 0.0;
uint additionalRayCount = 0;
// The intersection will launch a refraction ray only if the object is transparent and is has the refraction flag
#ifdef _SURFACE_TYPE_TRANSPARENT
// If the mesh has a refraction mode, then we do proper refraction
#if HAS_REFRACTION
// Inverse the ior ratio if we are leaving the medium (we are hitting a back face)
float invIOR = bsdfData.ior;
if (fragInput.isFrontFace)
invIOR = 1.0f / invIOR;
// Let's compute the refracted direction
float3 refractedDir = refract(rayIntersection.incidentDirection, bsdfData.normalWS, invIOR);
// If the refracted direction ends going in the same direction than the normal, we do not want to throw it
// NOTE: The current state of the code does not support the case of the total internal reflection. So there is a problem in term
// of energy conservation
// We launch a ray if there is still some depth be used
if (rayIntersection.remainingDepth > 0 && dot(refractedDir, bsdfData.normalWS) < 0.0f)
{
// Make sure we apply ray bias on the right side of the surface
const float biasSign = sign(dot(fragInput.tangentToWorld[2], refractedDir));
// Build the transmitted ray structure
RayDesc transmittedRay;
transmittedRay.Origin = pointWSPos + biasSign * fragInput.tangentToWorld[2] * _RaytracingRayBias;
transmittedRay.Direction = refractedDir;
transmittedRay.TMin = 0;
transmittedRay.TMax = _RaytracingRayMaxLength;
// Build the following intersection structure
RayIntersection transmittedIntersection;
transmittedIntersection.color = float3(0.0, 0.0, 0.0);
transmittedIntersection.incidentDirection = transmittedRay.Direction;
transmittedIntersection.origin = transmittedRay.Origin;
transmittedIntersection.t = 0.0f;
transmittedIntersection.remainingDepth = rayIntersection.remainingDepth - 1;
transmittedIntersection.rayCount = 1;
transmittedIntersection.pixelCoord = rayIntersection.pixelCoord;
// In order to achieve filtering for the textures, we need to compute the spread angle of the pixel
transmittedIntersection.cone.spreadAngle = rayIntersection.cone.spreadAngle;
transmittedIntersection.cone.width = rayIntersection.cone.width;
// Evaluate the ray intersection
TraceRay(_RaytracingAccelerationStructure, RAY_FLAG_CULL_BACK_FACING_TRIANGLES, RAYTRACINGRENDERERFLAG_RECURSIVE_RENDERING, 0, 1, 0, transmittedRay, transmittedIntersection);
// Override the transmitted color
transmitted = transmittedIntersection.color;
refractedWeight = 1.0;
additionalRayCount += transmittedIntersection.rayCount;
// Given that we are sharing code with rasterization, we need to override properly the refraction parameters
OverrideRefractionData(surfaceData,
transmittedIntersection.t,
pointWSPos + transmittedIntersection.t * refractedDir,
bsdfData,
preLightData);
}
#else
if (rayIntersection.remainingDepth > 0)
{
// Make sure we apply ray bias on the right side of the surface
const float biasSign = sign(dot(fragInput.tangentToWorld[2], rayIntersection.incidentDirection));
// Build the transmitted ray structure
RayDesc transmittedRay;
transmittedRay.Origin = pointWSPos + biasSign * fragInput.tangentToWorld[2] * _RaytracingRayBias;
transmittedRay.Direction = rayIntersection.incidentDirection;
transmittedRay.TMin = 0;
transmittedRay.TMax = _RaytracingRayMaxLength;
// Build the following intersection structure
RayIntersection transmittedIntersection;
transmittedIntersection.color = float3(0.0, 0.0, 0.0);
transmittedIntersection.incidentDirection = transmittedRay.Direction;
transmittedIntersection.origin = transmittedRay.Origin;
transmittedIntersection.t = 0.0f;
transmittedIntersection.remainingDepth = rayIntersection.remainingDepth - 1;
transmittedIntersection.rayCount = 1;
transmittedIntersection.pixelCoord = rayIntersection.pixelCoord;
// In order to achieve filtering for the textures, we need to compute the spread angle of the pixel
transmittedIntersection.cone.spreadAngle = rayIntersection.cone.spreadAngle;
transmittedIntersection.cone.width = rayIntersection.cone.width;
// Evaluate the ray intersection
TraceRay(_RaytracingAccelerationStructure, RAY_FLAG_CULL_BACK_FACING_TRIANGLES, RAYTRACINGRENDERERFLAG_RECURSIVE_RENDERING, 0, 1, 0, transmittedRay, transmittedIntersection);
// Override the transmitted color
transmitted = transmittedIntersection.color;
refractedWeight = 0.0;
additionalRayCount += transmittedIntersection.rayCount;
}
#endif
#endif
// We only launch a ray if there is still some depth be used and if the reflection smoothnes threshold was not reached.
if (rayIntersection.remainingDepth > 0 && RecursiveRenderingReflectionPerceptualSmoothness(bsdfData) >= _RaytracingReflectionMinSmoothness)
{
// Compute the reflected direction
float3 reflectedDir = reflect(rayIntersection.incidentDirection, bsdfData.normalWS);
// Make sure we apply ray bias on the right side of the surface
const float biasSign = sign(dot(fragInput.tangentToWorld[2], reflectedDir));
// Build the reflected ray
RayDesc reflectedRay;
reflectedRay.Origin = pointWSPos + biasSign * fragInput.tangentToWorld[2] * _RaytracingRayBias;
reflectedRay.Direction = reflectedDir;
reflectedRay.TMin = 0;
reflectedRay.TMax = _RaytracingRayMaxLength;
// Create and init the RayIntersection structure for this
RayIntersection reflectedIntersection;
reflectedIntersection.color = float3(0.0, 0.0, 0.0);
reflectedIntersection.incidentDirection = reflectedRay.Direction;
reflectedIntersection.origin = reflectedRay.Origin;
reflectedIntersection.t = 0.0f;
reflectedIntersection.remainingDepth = rayIntersection.remainingDepth - 1;
reflectedIntersection.rayCount = 1;
reflectedIntersection.pixelCoord = rayIntersection.pixelCoord;
// In order to achieve filtering for the textures, we need to compute the spread angle of the pixel
reflectedIntersection.cone.spreadAngle = rayIntersection.cone.spreadAngle;
reflectedIntersection.cone.width = rayIntersection.cone.width;
// Evaluate the ray intersection
TraceRay(_RaytracingAccelerationStructure, RAY_FLAG_CULL_BACK_FACING_TRIANGLES, RAYTRACINGRENDERERFLAG_RECURSIVE_RENDERING, 0, 1, 0, reflectedRay, reflectedIntersection);
// Override the transmitted color
reflected = reflectedIntersection.color;
reflectedWeight = 1.0;
additionalRayCount += reflectedIntersection.rayCount;
}
// Run the lightloop
LightLoopOutput lightLoopOutput;
LightLoop(viewWS, posInput, preLightData, bsdfData, builtinData, reflectedWeight, refractedWeight, reflected, transmitted, lightLoopOutput);
// Alias
float3 diffuseLighting = lightLoopOutput.diffuseLighting;
float3 specularLighting = lightLoopOutput.specularLighting;
// Color display for the moment
rayIntersection.color = diffuseLighting + specularLighting;
rayIntersection.rayCount += additionalRayCount;
#ifdef _SURFACE_TYPE_TRANSPARENT
// If the mesh is transparent, not refractive we need to alpha blend
#if !HAS_REFRACTION
rayIntersection.color = lerp(transmitted, rayIntersection.color, builtinData.opacity);
#endif
#endif
#else
// Given that we will be multiplying the final color by the current exposure multiplier outside of this function, we need to make sure that
// the unlit color is not impacted by that. Thus, we multiply it by the inverse of the current exposure multiplier.
rayIntersection.color = bsdfData.color * GetInverseCurrentExposureMultiplier() + builtinData.emissiveColor;
#endif
// Apply fog attenuation
ApplyFogAttenuation(WorldRayOrigin(), WorldRayDirection(), rayIntersection.t, rayIntersection.color, true);
}
// Generic function that handles the reflection code
[shader("anyhit")]
void AnyHitMain(inout RayIntersection rayIntersection : SV_RayPayload, AttributeData attributeData : SV_IntersectionAttributes)
{
UNITY_XR_ASSIGN_VIEW_INDEX(DispatchRaysIndex().z);
// The first thing that we should do is grab the intersection vertice
IntersectionVertex currentVertex;
GetCurrentIntersectionVertex(attributeData, currentVertex);
// Build the Frag inputs from the intersection vertice
FragInputs fragInput;
BuildFragInputsFromIntersection(currentVertex, rayIntersection.incidentDirection, fragInput);
// Compute the view vector
float3 viewWS = -rayIntersection.incidentDirection;
// Compute the distance of the ray
float travelDistance = length(GetAbsolutePositionWS(fragInput.positionRWS) - rayIntersection.origin);
rayIntersection.t = travelDistance;
PositionInputs posInput;
posInput.positionWS = fragInput.positionRWS;
posInput.positionSS = rayIntersection.pixelCoord;
// Build the surfacedata and builtindata
SurfaceData surfaceData;
BuiltinData builtinData;
bool isVisible;
GetSurfaceAndBuiltinData(fragInput, viewWS, posInput, surfaceData, builtinData, currentVertex, rayIntersection.cone, isVisible);
// If this fella should be culled, then we cull it
if (!isVisible)
{
IgnoreHit();
}
}