You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

711 lines
36 KiB

using System;
using UnityEngine.Jobs;
using Unity.Jobs;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Burst.CompilerServices;
using System.Threading;
namespace UnityEngine.Rendering.HighDefinition
{
internal partial class HDGpuLightsBuilder
{
JobHandle m_CreateGpuLightDataJobHandle;
internal struct CreateGpuLightDataJobGlobalConfig
{
public bool lightLayersEnabled;
public float specularGlobalDimmer;
public int invalidScreenSpaceShadowIndex;
public float maxShadowFadeDistance;
public static CreateGpuLightDataJobGlobalConfig Create(
HDCamera hdCamera,
HDShadowSettings hdShadowSettings)
{
return new CreateGpuLightDataJobGlobalConfig()
{
lightLayersEnabled = hdCamera.frameSettings.IsEnabled(FrameSettingsField.LightLayers),
specularGlobalDimmer = hdCamera.frameSettings.specularGlobalDimmer,
maxShadowFadeDistance = hdShadowSettings.maxShadowDistance.value,
invalidScreenSpaceShadowIndex = (int)LightDefinitions.s_InvalidScreenSpaceShadow
};
}
}
#if ENABLE_BURST_1_5_0_OR_NEWER
[Unity.Burst.BurstCompile]
#endif
internal struct CreateGpuLightDataJob : IJobParallelFor
{
#region Parameters
[ReadOnly]
public int totalLightCounts;
[ReadOnly]
public int outputLightCounts;
[ReadOnly]
public int outputDirectionalLightCounts;
[ReadOnly]
public int outputLightBoundsCount;
[ReadOnly]
public CreateGpuLightDataJobGlobalConfig globalConfig;
[ReadOnly]
public Vector3 cameraPos;
[ReadOnly]
public int directionalSortedLightCounts;
[ReadOnly]
public bool isPbrSkyActive;
[ReadOnly]
public int defaultDataIndex;
[ReadOnly]
public int viewCounts;
[ReadOnly]
public bool useCameraRelativePosition;
[ReadOnly]
public float maxShadowDistance;
#endregion
#region input light entity data
[NativeDisableContainerSafetyRestriction]
public NativeArray<HDLightRenderData> lightRenderDataArray;
#endregion
#region input visible lights processed
[ReadOnly]
public NativeArray<uint> sortKeys;
[ReadOnly]
public NativeArray<HDProcessedVisibleLight> processedEntities;
[ReadOnly]
public NativeArray<VisibleLight> visibleLights;
[ReadOnly]
public NativeArray<LightBakingOutput> visibleLightBakingOutput;
[ReadOnly]
public NativeArray<LightShadowCasterMode> visibleLightShadowCasterMode;
#endregion
#region output processed lights
[WriteOnly]
[NativeDisableContainerSafetyRestriction]
public NativeArray<LightData> lights;
[WriteOnly]
[NativeDisableContainerSafetyRestriction]
public NativeArray<DirectionalLightData> directionalLights;
[WriteOnly]
[NativeDisableContainerSafetyRestriction]
public NativeArray<LightsPerView> lightsPerView;
[WriteOnly]
[NativeDisableContainerSafetyRestriction]
public NativeArray<SFiniteLightBound> lightBounds;
[WriteOnly]
[NativeDisableContainerSafetyRestriction]
public NativeArray<LightVolumeData> lightVolumes;
[WriteOnly]
[NativeDisableContainerSafetyRestriction]
public NativeArray<int> gpuLightCounters;
#endregion
#if DEBUG
[IgnoreWarning(1370)] //Ignore throwing exception warning.
#endif
private ref HDLightRenderData GetLightData(int dataIndex)
{
#if DEBUG
if (dataIndex < 0 || dataIndex >= totalLightCounts)
throw new Exception("Trying to access a light from the DB out of bounds. The index requested is: " + dataIndex + " and the length is " + totalLightCounts);
#endif
unsafe
{
HDLightRenderData* data = (HDLightRenderData*)lightRenderDataArray.GetUnsafePtr<HDLightRenderData>() + dataIndex;
return ref UnsafeUtility.AsRef<HDLightRenderData>(data);
}
}
private static uint GetLightLayer(bool lightLayersEnabled, in HDLightRenderData lightRenderData)
{
int lightLayerMaskValue = (int)lightRenderData.renderingLayerMask;
uint lightLayerValue = lightLayerMaskValue < 0 ? (uint)RenderingLayerMask.Everything : lightRenderData.renderingLayerMask;
return lightLayersEnabled ? lightLayerValue : uint.MaxValue;
}
private static Vector3 GetLightColor(in VisibleLight light) => new Vector3(light.finalColor.r, light.finalColor.g, light.finalColor.b);
private void IncrementCounter(HDGpuLightsBuilder.GPULightTypeCountSlots counterSlot)
{
unsafe
{
int* ptr = (int*)gpuLightCounters.GetUnsafePtr<int>() + (int)counterSlot;
Interlocked.Increment(ref UnsafeUtility.AsRef<int>(ptr));
}
}
public static void ConvertLightToGPUFormat(
LightCategory lightCategory, GPULightType gpuLightType,
in CreateGpuLightDataJobGlobalConfig globalConfig,
LightShadowCasterMode visibleLightShadowCasterMode,
in LightBakingOutput visibleLightBakingOutput,
in VisibleLight light,
in HDProcessedVisibleLight processedEntity,
in HDLightRenderData lightRenderData,
out Vector3 lightDimensions,
ref LightData lightData)
{
int dataIndex = processedEntity.dataIndex;
lightData.lightLayers = GetLightLayer(globalConfig.lightLayersEnabled, lightRenderData);
lightData.lightType = gpuLightType;
var visibleLightAxisAndPosition = light.GetAxisAndPosition();
lightData.positionRWS = visibleLightAxisAndPosition.Position;
lightData.range = light.range;
if (lightRenderData.applyRangeAttenuation)
{
lightData.rangeAttenuationScale = 1.0f / (light.range * light.range);
lightData.rangeAttenuationBias = 1.0f;
}
else
{
// Solve f(x) = b - (a * x)^2 where x = (d/r)^2.
// f(0) = huge -> b = huge.
// f(1) = 0 -> huge - a^2 = 0 -> a = sqrt(huge).
const float hugeValue = 16777216.0f;
const float sqrtHuge = 4096.0f;
lightData.rangeAttenuationScale = sqrtHuge / (light.range * light.range);
lightData.rangeAttenuationBias = hugeValue;
}
float shapeWidthVal = lightRenderData.shapeWidth;
float shapeHeightVal = lightRenderData.shapeHeight;
if (lightData.lightType == GPULightType.Tube) shapeHeightVal = 0;
lightData.color = GetLightColor(light);
lightData.forward = visibleLightAxisAndPosition.Forward;
lightData.up = visibleLightAxisAndPosition.Up;
lightData.right = visibleLightAxisAndPosition.Right;
lightDimensions.x = shapeWidthVal;
lightDimensions.y = shapeHeightVal;
lightDimensions.z = light.range;
lightData.boxLightSafeExtent = 1.0f;
if (lightData.lightType == GPULightType.ProjectorBox)
{
// Rescale for cookies and windowing.
lightData.right *= 2.0f / Mathf.Max(shapeWidthVal, 0.001f);
lightData.up *= 2.0f / Mathf.Max(shapeHeightVal, 0.001f);
}
else if (lightData.lightType == GPULightType.ProjectorPyramid)
{
// Get width and height for the current frustum
var spotAngle = light.spotAngle;
float aspectRatioValue = lightRenderData.aspectRatio;
float frustumWidth, frustumHeight;
if (aspectRatioValue >= 1.0f)
{
frustumHeight = 2.0f * Mathf.Tan(spotAngle * 0.5f * Mathf.Deg2Rad);
frustumWidth = frustumHeight * aspectRatioValue;
}
else
{
frustumWidth = 2.0f * Mathf.Tan(spotAngle * 0.5f * Mathf.Deg2Rad);
frustumHeight = frustumWidth / aspectRatioValue;
}
// Adjust based on the new parametrization.
lightDimensions.x = frustumWidth;
lightDimensions.y = frustumHeight;
//// Rescale for cookies and windowing.
lightData.right *= 2.0f / frustumWidth;
lightData.up *= 2.0f / frustumHeight;
}
if (lightData.lightType == GPULightType.Spot)
{
var spotAngle = light.spotAngle;
var innerConePercent = lightRenderData.innerSpotPercent / 100.0f;
var cosSpotOuterHalfAngle = Mathf.Clamp(Mathf.Cos(spotAngle * 0.5f * Mathf.Deg2Rad), 0.0f, 1.0f);
var sinSpotOuterHalfAngle = Mathf.Sqrt(1.0f - cosSpotOuterHalfAngle * cosSpotOuterHalfAngle);
var cosSpotInnerHalfAngle = Mathf.Clamp(Mathf.Cos(spotAngle * 0.5f * innerConePercent * Mathf.Deg2Rad), 0.0f, 1.0f); // inner cone
var val = Mathf.Max(0.0001f, (cosSpotInnerHalfAngle - cosSpotOuterHalfAngle));
lightData.angleScale = 1.0f / val;
lightData.angleOffset = -cosSpotOuterHalfAngle * lightData.angleScale;
lightData.iesCut = lightRenderData.spotIESCutoffPercent / 100.0f;
// Rescale for cookies and windowing.
float cotOuterHalfAngle = cosSpotOuterHalfAngle / sinSpotOuterHalfAngle;
lightData.up *= cotOuterHalfAngle;
lightData.right *= cotOuterHalfAngle;
}
else
{
// These are the neutral values allowing GetAngleAnttenuation in shader code to return 1.0
lightData.angleScale = 0.0f;
lightData.angleOffset = 1.0f;
lightData.iesCut = 1.0f;
}
float shapeRadiusVal = lightRenderData.shapeRadius;
if (lightData.lightType != GPULightType.Directional && lightData.lightType != GPULightType.ProjectorBox)
{
// Store the squared radius of the light to simulate a fill light.
lightData.size = new Vector4(shapeRadiusVal * shapeRadiusVal, 0, 0, 0);
}
if (lightData.lightType == GPULightType.Rectangle || lightData.lightType == GPULightType.Tube || lightData.lightType == GPULightType.Disc)
{
lightData.size = new Vector4(shapeWidthVal, shapeHeightVal, Mathf.Cos(lightRenderData.barnDoorAngle * Mathf.PI / 180.0f), lightRenderData.barnDoorLength);
}
var lightDimmerVal = lightRenderData.lightDimmer;
lightData.lightDimmer = processedEntity.lightDistanceFade * lightDimmerVal;
lightData.diffuseDimmer = processedEntity.lightDistanceFade * (lightRenderData.affectDiffuse ? lightDimmerVal : 0);
lightData.specularDimmer = processedEntity.lightDistanceFade * (lightRenderData.affectSpecular ? lightDimmerVal * globalConfig.specularGlobalDimmer : 0);
lightData.volumetricLightDimmer = Mathf.Min(processedEntity.lightVolumetricDistanceFade, processedEntity.lightDistanceFade) * (lightRenderData.affectVolumetric ? lightRenderData.volumetricDimmer : 0.0f);
lightData.cookieMode = CookieMode.None;
lightData.shadowIndex = -1;
lightData.screenSpaceShadowIndex = globalConfig.invalidScreenSpaceShadowIndex;
lightData.isRayTracedContactShadow = 0.0f;
var distanceToCamera = processedEntity.distanceToCamera;
var lightsShadowFadeDistance = lightRenderData.shadowFadeDistance;
var shadowDimmerVal = lightRenderData.shadowDimmer;
var volumetricShadowDimmerVal = lightRenderData.affectVolumetric ? lightRenderData.volumetricShadowDimmer : 0.0f;
float shadowDistanceFade = HDUtils.ComputeLinearDistanceFade(distanceToCamera, Mathf.Min(globalConfig.maxShadowFadeDistance, lightsShadowFadeDistance));
lightData.shadowDimmer = shadowDistanceFade * shadowDimmerVal;
lightData.volumetricShadowDimmer = shadowDistanceFade * volumetricShadowDimmerVal;
// We want to have a colored penumbra if the flag is on and the color is not gray
var shadowTintVal = lightRenderData.shadowTint;
bool penumbraTintVal = lightRenderData.penumbraTint && ((shadowTintVal.r != shadowTintVal.g) || (shadowTintVal.g != shadowTintVal.b));
lightData.penumbraTint = penumbraTintVal ? 1.0f : 0.0f;
if (penumbraTintVal)
lightData.shadowTint = new Vector3(Mathf.Pow(shadowTintVal.r, 2.2f), Mathf.Pow(shadowTintVal.g, 2.2f), Mathf.Pow(shadowTintVal.b, 2.2f));
else
lightData.shadowTint = new Vector3(shadowTintVal.r, shadowTintVal.g, shadowTintVal.b);
//Value of max smoothness is derived from Radius. Formula results from eyeballing. Radius of 0 results in 1 and radius of 2.5 results in 0.
float maxSmoothness = Mathf.Clamp01(1.1725f / (1.01f + Mathf.Pow(1.0f * (shapeRadiusVal + 0.1f), 2f)) - 0.15f);
// Value of max smoothness is from artists point of view, need to convert from perceptual smoothness to roughness
lightData.minRoughness = (1.0f - maxSmoothness) * (1.0f - maxSmoothness);
lightData.shadowMaskSelector = Vector4.zero;
if (processedEntity.isBakedShadowMask)
{
lightData.shadowMaskSelector[visibleLightBakingOutput.occlusionMaskChannel] = 1.0f;
lightData.nonLightMappedOnly = visibleLightShadowCasterMode == LightShadowCasterMode.NonLightmappedOnly ? 1 : 0;
}
else
{
// use -1 to say that we don't use shadow mask
lightData.shadowMaskSelector.x = -1.0f;
lightData.nonLightMappedOnly = 0;
}
}
#if DEBUG
[IgnoreWarning(1370)] //Ignore throwing exception warning.
#endif
private void StoreAndConvertLightToGPUFormat(
int outputIndex, int lightIndex,
LightCategory lightCategory, GPULightType gpuLightType, LightVolumeType lightVolumeType, bool offscreen)
{
var light = visibleLights[lightIndex];
var processedEntity = processedEntities[lightIndex];
var lightData = new LightData();
ref HDLightRenderData lightRenderData = ref GetLightData(processedEntity.dataIndex);
ConvertLightToGPUFormat(
lightCategory, gpuLightType, globalConfig,
visibleLightShadowCasterMode[lightIndex],
visibleLightBakingOutput[lightIndex],
light,
processedEntity,
lightRenderData,
out var lightDimensions,
ref lightData);
for (int viewId = 0; viewId < viewCounts; ++viewId)
{
var lightsPerViewContainer = lightsPerView[viewId];
ComputeLightVolumeDataAndBound(
lightCategory, gpuLightType, lightVolumeType,
light, lightData, lightDimensions, lightsPerViewContainer.worldToView, outputIndex + lightsPerViewContainer.boundsOffset);
}
// Light volumes has been calculated, now we can update positionRWS to be relative camera
if (useCameraRelativePosition)
lightData.positionRWS -= cameraPos;
switch (lightCategory)
{
case LightCategory.Punctual:
IncrementCounter(HDGpuLightsBuilder.GPULightTypeCountSlots.Punctual);
break;
case LightCategory.Area:
IncrementCounter(HDGpuLightsBuilder.GPULightTypeCountSlots.Area);
break;
default:
Debug.Assert(false, "TODO: encountered an unknown LightCategory.");
break;
}
#if DEBUG
if (outputIndex < 0 || outputIndex >= outputLightCounts)
throw new Exception("Trying to access an output index out of bounds. Output index is " + outputIndex + "and max length is " + outputLightCounts);
#endif
lights[outputIndex] = lightData;
}
#if DEBUG
[IgnoreWarning(1370)] //Ignore throwing exception warning.
#endif
private void ComputeLightVolumeDataAndBound(
LightCategory lightCategory, GPULightType gpuLightType, LightVolumeType lightVolumeType,
in VisibleLight light, in LightData lightData, in Vector3 lightDimensions, in Matrix4x4 worldToView, int outputIndex)
{
// Then Culling side
var range = lightDimensions.z;
var lightToWorld = light.localToWorldMatrix;
Vector3 positionWS = lightData.positionRWS; // Currently not including camera relative transform
Vector3 positionVS = worldToView.MultiplyPoint(positionWS);
Vector3 xAxisVS = worldToView.MultiplyVector(lightToWorld.GetColumn(0));
Vector3 yAxisVS = worldToView.MultiplyVector(lightToWorld.GetColumn(1));
Vector3 zAxisVS = worldToView.MultiplyVector(lightToWorld.GetColumn(2));
// Fill bounds
var bound = new SFiniteLightBound();
var lightVolumeData = new LightVolumeData();
lightVolumeData.lightCategory = (uint)lightCategory;
lightVolumeData.lightVolume = (uint)lightVolumeType;
lightVolumeData.affectVolumetric = lightData.volumetricLightDimmer > 0.0f ? 1 : 0;
if (gpuLightType == GPULightType.Spot || gpuLightType == GPULightType.ProjectorPyramid)
{
Vector3 lightDir = lightToWorld.GetColumn(2);
// represents a left hand coordinate system in world space since det(worldToView)<0
Vector3 vx = xAxisVS;
Vector3 vy = yAxisVS;
Vector3 vz = zAxisVS;
var sa = light.spotAngle;
var cs = Mathf.Cos(0.5f * sa * Mathf.Deg2Rad);
var si = Mathf.Sin(0.5f * sa * Mathf.Deg2Rad);
if (gpuLightType == GPULightType.ProjectorPyramid)
{
Vector3 lightPosToProjWindowCorner = (0.5f * lightDimensions.x) * vx + (0.5f * lightDimensions.y) * vy + 1.0f * vz;
cs = Vector3.Dot(vz, Vector3.Normalize(lightPosToProjWindowCorner));
si = Mathf.Sqrt(1.0f - cs * cs);
}
const float FltMax = 3.402823466e+38F;
var ta = cs > 0.0f ? (si / cs) : FltMax;
var cota = si > 0.0f ? (cs / si) : FltMax;
//const float cotasa = l.GetCotanHalfSpotAngle();
// apply nonuniform scale to OBB of spot light
var squeeze = true;//sa < 0.7f * 90.0f; // arb heuristic
var fS = squeeze ? ta : si;
bound.center = worldToView.MultiplyPoint(positionWS + ((0.5f * range) * lightDir)); // use mid point of the spot as the center of the bounding volume for building screen-space AABB for tiled lighting.
// scale axis to match box or base of pyramid
bound.boxAxisX = (fS * range) * vx;
bound.boxAxisY = (fS * range) * vy;
bound.boxAxisZ = (0.5f * range) * vz;
// generate bounding sphere radius
var fAltDx = si;
var fAltDy = cs;
fAltDy = fAltDy - 0.5f;
//if(fAltDy<0) fAltDy=-fAltDy;
fAltDx *= range; fAltDy *= range;
// Handle case of pyramid with this select (currently unused)
var altDist = Mathf.Sqrt(fAltDy * fAltDy + (true ? 1.0f : 2.0f) * fAltDx * fAltDx);
bound.radius = altDist > (0.5f * range) ? altDist : (0.5f * range); // will always pick fAltDist
bound.scaleXY = squeeze ? 0.01f : 1.0f;
lightVolumeData.lightAxisX = vx;
lightVolumeData.lightAxisY = vy;
lightVolumeData.lightAxisZ = vz;
lightVolumeData.lightPos = positionVS;
lightVolumeData.radiusSq = range * range;
lightVolumeData.cotan = cota;
lightVolumeData.featureFlags = (uint)LightFeatureFlags.Punctual;
}
else if (gpuLightType == GPULightType.Point)
{
// Construct a view-space axis-aligned bounding cube around the bounding sphere.
// This allows us to utilize the same polygon clipping technique for all lights.
// Non-axis-aligned vectors may result in a larger screen-space AABB.
Vector3 vx = new Vector3(1, 0, 0);
Vector3 vy = new Vector3(0, 1, 0);
Vector3 vz = new Vector3(0, 0, 1);
bound.center = positionVS;
bound.boxAxisX = vx * range;
bound.boxAxisY = vy * range;
bound.boxAxisZ = vz * range;
bound.scaleXY = 1.0f;
bound.radius = range;
// fill up ldata
lightVolumeData.lightAxisX = vx;
lightVolumeData.lightAxisY = vy;
lightVolumeData.lightAxisZ = vz;
lightVolumeData.lightPos = bound.center;
lightVolumeData.radiusSq = range * range;
lightVolumeData.featureFlags = (uint)LightFeatureFlags.Punctual;
}
else if (gpuLightType == GPULightType.Tube)
{
Vector3 dimensions = new Vector3(lightDimensions.x + 2 * range, 2 * range, 2 * range); // Omni-directional
Vector3 extents = 0.5f * dimensions;
Vector3 centerVS = positionVS;
bound.center = centerVS;
bound.boxAxisX = extents.x * xAxisVS;
bound.boxAxisY = extents.y * yAxisVS;
bound.boxAxisZ = extents.z * zAxisVS;
bound.radius = extents.x;
bound.scaleXY = 1.0f;
lightVolumeData.lightPos = centerVS;
lightVolumeData.lightAxisX = xAxisVS;
lightVolumeData.lightAxisY = yAxisVS;
lightVolumeData.lightAxisZ = zAxisVS;
lightVolumeData.boxInvRange.Set(1.0f / extents.x, 1.0f / extents.y, 1.0f / extents.z);
lightVolumeData.featureFlags = (uint)LightFeatureFlags.Area;
}
else if (gpuLightType == GPULightType.Rectangle)
{
Vector3 dimensions = new Vector3(lightDimensions.x + 2 * range, lightDimensions.y + 2 * range, range); // One-sided
Vector3 extents = 0.5f * dimensions;
Vector3 centerVS = positionVS + extents.z * zAxisVS;
float d = range + 0.5f * Mathf.Sqrt(lightDimensions.x * lightDimensions.x + lightDimensions.y * lightDimensions.y);
bound.center = centerVS;
bound.boxAxisX = extents.x * xAxisVS;
bound.boxAxisY = extents.y * yAxisVS;
bound.boxAxisZ = extents.z * zAxisVS;
bound.radius = Mathf.Sqrt(d * d + (0.5f * range) * (0.5f * range));
bound.scaleXY = 1.0f;
lightVolumeData.lightPos = centerVS;
lightVolumeData.lightAxisX = xAxisVS;
lightVolumeData.lightAxisY = yAxisVS;
lightVolumeData.lightAxisZ = zAxisVS;
lightVolumeData.boxInvRange.Set(1.0f / extents.x, 1.0f / extents.y, 1.0f / extents.z);
lightVolumeData.featureFlags = (uint)LightFeatureFlags.Area;
}
else if (gpuLightType == GPULightType.ProjectorBox)
{
Vector3 dimensions = new Vector3(lightDimensions.x, lightDimensions.y, range); // One-sided
Vector3 extents = 0.5f * dimensions;
Vector3 centerVS = positionVS + extents.z * zAxisVS;
bound.center = centerVS;
bound.boxAxisX = extents.x * xAxisVS;
bound.boxAxisY = extents.y * yAxisVS;
bound.boxAxisZ = extents.z * zAxisVS;
bound.radius = extents.magnitude;
bound.scaleXY = 1.0f;
lightVolumeData.lightPos = centerVS;
lightVolumeData.lightAxisX = xAxisVS;
lightVolumeData.lightAxisY = yAxisVS;
lightVolumeData.lightAxisZ = zAxisVS;
lightVolumeData.boxInvRange.Set(1.0f / extents.x, 1.0f / extents.y, 1.0f / extents.z);
lightVolumeData.featureFlags = (uint)LightFeatureFlags.Punctual;
}
else if (gpuLightType == GPULightType.Disc)
{
//not supported at real time at the moment
}
else
{
Debug.Assert(false, "TODO: encountered an unknown GPULightType.");
}
#if DEBUG
if (outputIndex < 0 || outputIndex >= outputLightBoundsCount)
throw new Exception("Trying to access an output index out of bounds. Output index is " + outputIndex + "and max length is " + outputLightBoundsCount);
#endif
lightBounds[outputIndex] = bound;
lightVolumes[outputIndex] = lightVolumeData;
}
#if DEBUG
[IgnoreWarning(1370)] //Ignore throwing exception warning.
#endif
private void ConvertDirectionalLightToGPUFormat(
int outputIndex, int lightIndex, LightCategory lightCategory, GPULightType gpuLightType, LightVolumeType lightVolumeType)
{
var light = visibleLights[lightIndex];
var processedEntity = processedEntities[lightIndex];
int dataIndex = processedEntity.dataIndex;
var lightData = new DirectionalLightData();
ref HDLightRenderData lightRenderData = ref GetLightData(dataIndex);
lightData.lightLayers = GetLightLayer(globalConfig.lightLayersEnabled, lightRenderData);
// Light direction for directional is opposite to the forward direction
lightData.forward = light.GetForward();
lightData.color = GetLightColor(light);
// Caution: This is bad but if additionalData == HDUtils.s_DefaultHDAdditionalLightData it mean we are trying to promote legacy lights, which is the case for the preview for example, so we need to multiply by PI as legacy Unity do implicit divide by PI for direct intensity.
// So we expect that all light with additionalData == HDUtils.s_DefaultHDAdditionalLightData are currently the one from the preview, light in scene MUST have additionalData
lightData.color *= (defaultDataIndex == dataIndex) ? Mathf.PI : 1.0f;
lightData.lightDimmer = lightRenderData.lightDimmer;
lightData.diffuseDimmer = lightRenderData.affectDiffuse ? lightData.lightDimmer : 0;
lightData.specularDimmer = lightRenderData.affectSpecular ? lightData.lightDimmer * globalConfig.specularGlobalDimmer : 0;
lightData.volumetricLightDimmer = (lightRenderData.affectVolumetric ? lightRenderData.volumetricDimmer : 0.0f);
lightData.shadowIndex = -1;
lightData.screenSpaceShadowIndex = globalConfig.invalidScreenSpaceShadowIndex;
lightData.isRayTracedContactShadow = 0.0f;
// Rescale for cookies and windowing.
lightData.right = light.GetRight() * 2 / Mathf.Max(lightRenderData.shapeWidth, 0.001f);
lightData.up = light.GetUp() * 2 / Mathf.Max(lightRenderData.shapeHeight, 0.001f);
lightData.positionRWS = light.GetPosition();
lightData.shadowDimmer = lightRenderData.shadowDimmer;
var volumetricShadowDimmerVal = lightRenderData.affectVolumetric ? lightRenderData.volumetricShadowDimmer : 0.0f;
lightData.volumetricShadowDimmer = volumetricShadowDimmerVal;
// We want to have a colored penumbra if the flag is on and the color is not gray
var shadowTintValue = lightRenderData.shadowTint;
bool penumbraTintValue = lightRenderData.penumbraTint && ((shadowTintValue.r != shadowTintValue.g) || (shadowTintValue.g != shadowTintValue.b));
lightData.penumbraTint = penumbraTintValue ? 1.0f : 0.0f;
if (penumbraTintValue)
lightData.shadowTint = new Vector3(shadowTintValue.r * shadowTintValue.r, shadowTintValue.g * shadowTintValue.g, shadowTintValue.b * shadowTintValue.b);
else
lightData.shadowTint = new Vector3(shadowTintValue.r, shadowTintValue.g, shadowTintValue.b);
//Value of max smoothness is derived from AngularDiameter. Formula results from eyeballing. Angular diameter of 0 results in 1 and angular diameter of 80 results in 0.
float maxSmoothness = Mathf.Clamp01(1.35f / (1.0f + Mathf.Pow(1.15f * (0.0315f * lightRenderData.angularDiameter + 0.4f), 2f)) - 0.11f);
// Value of max smoothness is from artists point of view, need to convert from perceptual smoothness to roughness
lightData.minRoughness = (1.0f - maxSmoothness) * (1.0f - maxSmoothness);
lightData.shadowMaskSelector = Vector4.zero;
if (processedEntity.isBakedShadowMask)
{
var bakingOutput = visibleLightBakingOutput[lightIndex];
lightData.shadowMaskSelector[bakingOutput.occlusionMaskChannel] = 1.0f;
lightData.nonLightMappedOnly = visibleLightShadowCasterMode[lightIndex] == LightShadowCasterMode.NonLightmappedOnly ? 1 : 0;
}
else
{
// use -1 to say that we don't use shadow mask
lightData.shadowMaskSelector.x = -1.0f;
lightData.nonLightMappedOnly = 0;
}
lightData.angularDiameter = lightRenderData.angularDiameter * Mathf.Deg2Rad;
lightData.distanceFromCamera = (isPbrSkyActive && lightRenderData.interactsWithSky) ? lightRenderData.distance : -1;
if (useCameraRelativePosition)
lightData.positionRWS -= cameraPos;
IncrementCounter(HDGpuLightsBuilder.GPULightTypeCountSlots.Directional);
#if DEBUG
if (outputIndex < 0 || outputIndex >= outputDirectionalLightCounts)
throw new Exception("Trying to access an output index out of bounds. Output index is " + outputIndex + "and max length is " + outputLightCounts);
#endif
directionalLights[outputIndex] = lightData;
}
public void Execute(int index)
{
var sortKey = sortKeys[index];
HDGpuLightsBuilder.UnpackLightSortKey(sortKey, out var lightCategory, out var gpuLightType, out var lightVolumeType, out var lightIndex, out var offscreen);
if (gpuLightType == GPULightType.Directional)
{
int outputIndex = index;
ConvertDirectionalLightToGPUFormat(outputIndex, lightIndex, lightCategory, gpuLightType, lightVolumeType);
}
else
{
int outputIndex = index - directionalSortedLightCounts;
StoreAndConvertLightToGPUFormat(outputIndex, lightIndex, lightCategory, gpuLightType, lightVolumeType, offscreen);
}
}
}
public void StartCreateGpuLightDataJob(
HDCamera hdCamera,
in CullingResults cullingResult,
HDShadowSettings hdShadowSettings,
HDProcessedVisibleLightsBuilder visibleLights,
HDLightRenderDatabase lightEntities)
{
var visualEnvironment = hdCamera.volumeStack.GetComponent<VisualEnvironment>();
var shadowSettings = hdCamera.volumeStack.GetComponent<HDShadowSettings>();
Debug.Assert(visualEnvironment != null);
bool isPbrSkyActive = visualEnvironment.skyType.value == (int)SkyType.PhysicallyBased;
var createGpuLightDataJob = new CreateGpuLightDataJob()
{
//Parameters
totalLightCounts = lightEntities.lightCount,
outputLightCounts = m_LightCount,
outputDirectionalLightCounts = m_DirectionalLightCount,
outputLightBoundsCount = m_LightBoundsCount,
globalConfig = CreateGpuLightDataJobGlobalConfig.Create(hdCamera, hdShadowSettings),
cameraPos = hdCamera.mainViewConstants.worldSpaceCameraPos,
directionalSortedLightCounts = visibleLights.sortedDirectionalLightCounts,
isPbrSkyActive = isPbrSkyActive,
defaultDataIndex = lightEntities.GetEntityDataIndex(lightEntities.GetDefaultLightEntity()),
viewCounts = hdCamera.viewCount,
useCameraRelativePosition = ShaderConfig.s_CameraRelativeRendering != 0,
maxShadowDistance = shadowSettings.maxShadowDistance.value,
// light entity data
lightRenderDataArray = lightEntities.lightData,
//visible lights processed
sortKeys = visibleLights.sortKeys,
processedEntities = visibleLights.processedEntities,
visibleLights = cullingResult.visibleLights,
visibleLightBakingOutput = visibleLights.visibleLightBakingOutput,
visibleLightShadowCasterMode = visibleLights.visibleLightShadowCasterMode,
//outputs
gpuLightCounters = m_LightTypeCounters,
lights = m_Lights,
directionalLights = m_DirectionalLights,
lightsPerView = m_LightsPerView,
lightBounds = m_LightBounds,
lightVolumes = m_LightVolumes
};
m_CreateGpuLightDataJobHandle = createGpuLightDataJob.Schedule(visibleLights.sortedLightCounts, 32);
}
public void CompleteGpuLightDataJob()
{
m_CreateGpuLightDataJobHandle.Complete();
}
}
}