Last active
February 16, 2020 06:36
-
-
Save Thaina/79fd5bd25f47344aac04865b42a568fd to your computer and use it in GitHub Desktop.
Fix ambient light not set in the [email protected]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine.Experimental.GlobalIllumination; | |
using Unity.Collections; | |
namespace UnityEngine.Rendering.Universal.Internal | |
{ | |
/// <summary> | |
/// Computes and submits lighting data to the GPU. | |
/// </summary> | |
public class ForwardLights | |
{ | |
static class LightConstantBuffer | |
{ | |
#region AmbientFix | |
public static int _AmbientSky; | |
public static int _AmbientEquator; | |
public static int _AmbientGround; | |
#endregion | |
public static int _MainLightPosition; | |
public static int _MainLightColor; | |
public static int _AdditionalLightsCount; | |
public static int _AdditionalLightsPosition; | |
public static int _AdditionalLightsColor; | |
public static int _AdditionalLightsAttenuation; | |
public static int _AdditionalLightsSpotDir; | |
public static int _AdditionalLightOcclusionProbeChannel; | |
} | |
int m_AdditionalLightsBufferId; | |
int m_AdditionalLightsIndicesId; | |
const string k_SetupLightConstants = "Setup Light Constants"; | |
MixedLightingSetup m_MixedLightingSetup; | |
// Holds light direction for directional lights or position for punctual lights. | |
// When w is set to 1.0, it means it's a punctual light. | |
Vector4 k_DefaultLightPosition = new Vector4(0.0f, 0.0f, 1.0f, 0.0f); | |
Vector4 k_DefaultLightColor = Color.black; | |
// Default light attenuation is setup in a particular way that it causes | |
// directional lights to return 1.0 for both distance and angle attenuation | |
Vector4 k_DefaultLightAttenuation = new Vector4(0.0f, 1.0f, 0.0f, 1.0f); | |
Vector4 k_DefaultLightSpotDirection = new Vector4(0.0f, 0.0f, 1.0f, 0.0f); | |
Vector4 k_DefaultLightsProbeChannel = new Vector4(-1.0f, 1.0f, -1.0f, -1.0f); | |
Vector4[] m_AdditionalLightPositions; | |
Vector4[] m_AdditionalLightColors; | |
Vector4[] m_AdditionalLightAttenuations; | |
Vector4[] m_AdditionalLightSpotDirections; | |
Vector4[] m_AdditionalLightOcclusionProbeChannels; | |
bool m_UseStructuredBuffer; | |
public ForwardLights() | |
{ | |
m_UseStructuredBuffer = RenderingUtils.useStructuredBuffer; | |
#region AmbientFix | |
LightConstantBuffer._AmbientSky = Shader.PropertyToID("unity_AmbientSky"); | |
LightConstantBuffer._AmbientEquator = Shader.PropertyToID("unity_AmbientEquator"); | |
LightConstantBuffer._AmbientGround = Shader.PropertyToID("unity_AmbientGround"); | |
#endregion | |
LightConstantBuffer._MainLightPosition = Shader.PropertyToID("_MainLightPosition"); | |
LightConstantBuffer._MainLightColor = Shader.PropertyToID("_MainLightColor"); | |
LightConstantBuffer._AdditionalLightsCount = Shader.PropertyToID("_AdditionalLightsCount"); | |
if (m_UseStructuredBuffer) | |
{ | |
m_AdditionalLightsBufferId = Shader.PropertyToID("_AdditionalLightsBuffer"); | |
m_AdditionalLightsIndicesId = Shader.PropertyToID("_AdditionalLightsIndices"); | |
} | |
else | |
{ | |
LightConstantBuffer._AdditionalLightsPosition = Shader.PropertyToID("_AdditionalLightsPosition"); | |
LightConstantBuffer._AdditionalLightsColor = Shader.PropertyToID("_AdditionalLightsColor"); | |
LightConstantBuffer._AdditionalLightsAttenuation = Shader.PropertyToID("_AdditionalLightsAttenuation"); | |
LightConstantBuffer._AdditionalLightsSpotDir = Shader.PropertyToID("_AdditionalLightsSpotDir"); | |
LightConstantBuffer._AdditionalLightOcclusionProbeChannel = Shader.PropertyToID("_AdditionalLightsOcclusionProbes"); | |
int maxLights = UniversalRenderPipeline.maxVisibleAdditionalLights; | |
m_AdditionalLightPositions = new Vector4[maxLights]; | |
m_AdditionalLightColors = new Vector4[maxLights]; | |
m_AdditionalLightAttenuations = new Vector4[maxLights]; | |
m_AdditionalLightSpotDirections = new Vector4[maxLights]; | |
m_AdditionalLightOcclusionProbeChannels = new Vector4[maxLights]; | |
} | |
} | |
public void Setup(ScriptableRenderContext context, ref RenderingData renderingData) | |
{ | |
int additionalLightsCount = renderingData.lightData.additionalLightsCount; | |
bool additionalLightsPerVertex = renderingData.lightData.shadeAdditionalLightsPerVertex; | |
CommandBuffer cmd = CommandBufferPool.Get(k_SetupLightConstants); | |
SetupShaderLightConstants(cmd, ref renderingData); | |
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.AdditionalLightsVertex, | |
additionalLightsCount > 0 && additionalLightsPerVertex); | |
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.AdditionalLightsPixel, | |
additionalLightsCount > 0 && !additionalLightsPerVertex); | |
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MixedLightingSubtractive, | |
renderingData.lightData.supportsMixedLighting && | |
m_MixedLightingSetup == MixedLightingSetup.Subtractive); | |
context.ExecuteCommandBuffer(cmd); | |
CommandBufferPool.Release(cmd); | |
} | |
void InitializeLightConstants(NativeArray<VisibleLight> lights, int lightIndex, out Vector4 lightPos, out Vector4 lightColor, out Vector4 lightAttenuation, out Vector4 lightSpotDir, out Vector4 lightOcclusionProbeChannel) | |
{ | |
lightPos = k_DefaultLightPosition; | |
lightColor = k_DefaultLightColor; | |
lightAttenuation = k_DefaultLightAttenuation; | |
lightSpotDir = k_DefaultLightSpotDirection; | |
lightOcclusionProbeChannel = k_DefaultLightsProbeChannel; | |
// When no lights are visible, main light will be set to -1. | |
// In this case we initialize it to default values and return | |
if (lightIndex < 0) | |
return; | |
VisibleLight lightData = lights[lightIndex]; | |
if (lightData.lightType == LightType.Directional) | |
{ | |
Vector4 dir = -lightData.localToWorldMatrix.GetColumn(2); | |
lightPos = new Vector4(dir.x, dir.y, dir.z, 0.0f); | |
} | |
else | |
{ | |
Vector4 pos = lightData.localToWorldMatrix.GetColumn(3); | |
lightPos = new Vector4(pos.x, pos.y, pos.z, 1.0f); | |
} | |
// VisibleLight.finalColor already returns color in active color space | |
lightColor = lightData.finalColor; | |
// Directional Light attenuation is initialize so distance attenuation always be 1.0 | |
if (lightData.lightType != LightType.Directional) | |
{ | |
// Light attenuation in universal matches the unity vanilla one. | |
// attenuation = 1.0 / distanceToLightSqr | |
// We offer two different smoothing factors. | |
// The smoothing factors make sure that the light intensity is zero at the light range limit. | |
// The first smoothing factor is a linear fade starting at 80 % of the light range. | |
// smoothFactor = (lightRangeSqr - distanceToLightSqr) / (lightRangeSqr - fadeStartDistanceSqr) | |
// We rewrite smoothFactor to be able to pre compute the constant terms below and apply the smooth factor | |
// with one MAD instruction | |
// smoothFactor = distanceSqr * (1.0 / (fadeDistanceSqr - lightRangeSqr)) + (-lightRangeSqr / (fadeDistanceSqr - lightRangeSqr) | |
// distanceSqr * oneOverFadeRangeSqr + lightRangeSqrOverFadeRangeSqr | |
// The other smoothing factor matches the one used in the Unity lightmapper but is slower than the linear one. | |
// smoothFactor = (1.0 - saturate((distanceSqr * 1.0 / lightrangeSqr)^2))^2 | |
float lightRangeSqr = lightData.range * lightData.range; | |
float fadeStartDistanceSqr = 0.8f * 0.8f * lightRangeSqr; | |
float fadeRangeSqr = (fadeStartDistanceSqr - lightRangeSqr); | |
float oneOverFadeRangeSqr = 1.0f / fadeRangeSqr; | |
float lightRangeSqrOverFadeRangeSqr = -lightRangeSqr / fadeRangeSqr; | |
float oneOverLightRangeSqr = 1.0f / Mathf.Max(0.0001f, lightData.range * lightData.range); | |
// On mobile: Use the faster linear smoothing factor. | |
// On other devices: Use the smoothing factor that matches the GI. | |
lightAttenuation.x = Application.isMobilePlatform ? oneOverFadeRangeSqr : oneOverLightRangeSqr; | |
lightAttenuation.y = lightRangeSqrOverFadeRangeSqr; | |
} | |
if (lightData.lightType == LightType.Spot) | |
{ | |
Vector4 dir = lightData.localToWorldMatrix.GetColumn(2); | |
lightSpotDir = new Vector4(-dir.x, -dir.y, -dir.z, 0.0f); | |
// Spot Attenuation with a linear falloff can be defined as | |
// (SdotL - cosOuterAngle) / (cosInnerAngle - cosOuterAngle) | |
// This can be rewritten as | |
// invAngleRange = 1.0 / (cosInnerAngle - cosOuterAngle) | |
// SdotL * invAngleRange + (-cosOuterAngle * invAngleRange) | |
// If we precompute the terms in a MAD instruction | |
float cosOuterAngle = Mathf.Cos(Mathf.Deg2Rad * lightData.spotAngle * 0.5f); | |
// We neeed to do a null check for particle lights | |
// This should be changed in the future | |
// Particle lights will use an inline function | |
float cosInnerAngle; | |
if (lightData.light != null) | |
cosInnerAngle = Mathf.Cos(lightData.light.innerSpotAngle * Mathf.Deg2Rad * 0.5f); | |
else | |
cosInnerAngle = Mathf.Cos((2.0f * Mathf.Atan(Mathf.Tan(lightData.spotAngle * 0.5f * Mathf.Deg2Rad) * (64.0f - 18.0f) / 64.0f)) * 0.5f); | |
float smoothAngleRange = Mathf.Max(0.001f, cosInnerAngle - cosOuterAngle); | |
float invAngleRange = 1.0f / smoothAngleRange; | |
float add = -cosOuterAngle * invAngleRange; | |
lightAttenuation.z = invAngleRange; | |
lightAttenuation.w = add; | |
} | |
Light light = lightData.light; | |
// Set the occlusion probe channel. | |
int occlusionProbeChannel = light != null ? light.bakingOutput.occlusionMaskChannel : -1; | |
// If we have baked the light, the occlusion channel is the index we need to sample in 'unity_ProbesOcclusion' | |
// If we have not baked the light, the occlusion channel is -1. | |
// In case there is no occlusion channel is -1, we set it to zero, and then set the second value in the | |
// input to one. We then, in the shader max with the second value for non-occluded lights. | |
lightOcclusionProbeChannel.x = occlusionProbeChannel == -1 ? 0f : occlusionProbeChannel; | |
lightOcclusionProbeChannel.y = occlusionProbeChannel == -1 ? 1f : 0f; | |
// TODO: Add support to shadow mask | |
if (light != null && light.bakingOutput.mixedLightingMode == MixedLightingMode.Subtractive && light.bakingOutput.lightmapBakeType == LightmapBakeType.Mixed) | |
{ | |
if (m_MixedLightingSetup == MixedLightingSetup.None && lightData.light.shadows != LightShadows.None) | |
{ | |
m_MixedLightingSetup = MixedLightingSetup.Subtractive; | |
} | |
} | |
} | |
void SetupShaderLightConstants(CommandBuffer cmd, ref RenderingData renderingData) | |
{ | |
m_MixedLightingSetup = MixedLightingSetup.None; | |
// Main light has an optimized shader path for main light. This will benefit games that only care about a single light. | |
// Universal pipeline also supports only a single shadow light, if available it will be the main light. | |
SetupMainLightConstants(cmd, ref renderingData.lightData); | |
SetupAdditionalLightConstants(cmd, ref renderingData); | |
} | |
void SetupMainLightConstants(CommandBuffer cmd, ref LightData lightData) | |
{ | |
Vector4 lightPos, lightColor, lightAttenuation, lightSpotDir, lightOcclusionChannel; | |
InitializeLightConstants(lightData.visibleLights, lightData.mainLightIndex, out lightPos, out lightColor, out lightAttenuation, out lightSpotDir, out lightOcclusionChannel); | |
#region AmbientFix | |
if(RenderSettings.ambientMode == AmbientMode.Trilight) | |
{ | |
cmd.EnableShaderKeyword("TRI_COLOR_AMBIENT"); | |
cmd.SetGlobalVector(LightConstantBuffer._AmbientSky, RenderSettings.ambientSkyColor); | |
cmd.SetGlobalVector(LightConstantBuffer._AmbientEquator, RenderSettings.ambientEquatorColor); | |
cmd.SetGlobalVector(LightConstantBuffer._AmbientGround, RenderSettings.ambientGroundColor); | |
} | |
else | |
{ | |
cmd.DisableShaderKeyword("TRI_COLOR_AMBIENT"); | |
cmd.SetGlobalVector(LightConstantBuffer._AmbientSky, RenderSettings.ambientLight); | |
} | |
#endregion | |
cmd.SetGlobalVector(LightConstantBuffer._MainLightPosition, lightPos); | |
cmd.SetGlobalVector(LightConstantBuffer._MainLightColor, lightColor); | |
} | |
void SetupAdditionalLightConstants(CommandBuffer cmd, ref RenderingData renderingData) | |
{ | |
ref LightData lightData = ref renderingData.lightData; | |
var cullResults = renderingData.cullResults; | |
var lights = lightData.visibleLights; | |
int maxAdditionalLightsCount = UniversalRenderPipeline.maxVisibleAdditionalLights; | |
int additionalLightsCount = SetupPerObjectLightIndices(cullResults, ref lightData); | |
if (additionalLightsCount > 0) | |
{ | |
if (m_UseStructuredBuffer) | |
{ | |
NativeArray<ShaderInput.LightData> additionalLightsData = new NativeArray<ShaderInput.LightData>(additionalLightsCount, Allocator.Temp); | |
for (int i = 0, lightIter = 0; i < lights.Length && lightIter < maxAdditionalLightsCount; ++i) | |
{ | |
VisibleLight light = lights[i]; | |
if (lightData.mainLightIndex != i) | |
{ | |
ShaderInput.LightData data; | |
InitializeLightConstants(lights, i, | |
out data.position, out data.color, out data.attenuation, | |
out data.spotDirection, out data.occlusionProbeChannels); | |
additionalLightsData[lightIter] = data; | |
lightIter++; | |
} | |
} | |
var lightDataBuffer = ShaderData.instance.GetLightDataBuffer(additionalLightsCount); | |
lightDataBuffer.SetData(additionalLightsData); | |
int lightIndices = cullResults.lightAndReflectionProbeIndexCount; | |
var lightIndicesBuffer = ShaderData.instance.GetLightIndicesBuffer(lightIndices); | |
cmd.SetGlobalBuffer(m_AdditionalLightsBufferId, lightDataBuffer); | |
cmd.SetGlobalBuffer(m_AdditionalLightsIndicesId, lightIndicesBuffer); | |
additionalLightsData.Dispose(); | |
} | |
else | |
{ | |
for (int i = 0, lightIter = 0; i < lights.Length && lightIter < maxAdditionalLightsCount; ++i) | |
{ | |
VisibleLight light = lights[i]; | |
if (lightData.mainLightIndex != i) | |
{ | |
InitializeLightConstants(lights, i, out m_AdditionalLightPositions[lightIter], | |
out m_AdditionalLightColors[lightIter], | |
out m_AdditionalLightAttenuations[lightIter], | |
out m_AdditionalLightSpotDirections[lightIter], | |
out m_AdditionalLightOcclusionProbeChannels[lightIter]); | |
lightIter++; | |
} | |
} | |
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsPosition, m_AdditionalLightPositions); | |
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsColor, m_AdditionalLightColors); | |
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsAttenuation, m_AdditionalLightAttenuations); | |
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsSpotDir, m_AdditionalLightSpotDirections); | |
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightOcclusionProbeChannel, m_AdditionalLightOcclusionProbeChannels); | |
} | |
cmd.SetGlobalVector(LightConstantBuffer._AdditionalLightsCount, new Vector4(lightData.maxPerObjectAdditionalLightsCount, | |
0.0f, 0.0f, 0.0f)); | |
} | |
else | |
{ | |
cmd.SetGlobalVector(LightConstantBuffer._AdditionalLightsCount, Vector4.zero); | |
} | |
} | |
int SetupPerObjectLightIndices(CullingResults cullResults, ref LightData lightData) | |
{ | |
if (lightData.additionalLightsCount == 0) | |
return lightData.additionalLightsCount; | |
var visibleLights = lightData.visibleLights; | |
var perObjectLightIndexMap = cullResults.GetLightIndexMap(Allocator.Temp); | |
int globalDirectionalLightsCount = 0; | |
int additionalLightsCount = 0; | |
// Disable all directional lights from the perobject light indices | |
// Pipeline handles main light globally and there's no support for additional directional lights atm. | |
for (int i = 0; i < visibleLights.Length; ++i) | |
{ | |
if (additionalLightsCount >= UniversalRenderPipeline.maxVisibleAdditionalLights) | |
break; | |
VisibleLight light = visibleLights[i]; | |
if (i == lightData.mainLightIndex) | |
{ | |
perObjectLightIndexMap[i] = -1; | |
++globalDirectionalLightsCount; | |
} | |
else | |
{ | |
perObjectLightIndexMap[i] -= globalDirectionalLightsCount; | |
++additionalLightsCount; | |
} | |
} | |
// Disable all remaining lights we cannot fit into the global light buffer. | |
for (int i = globalDirectionalLightsCount + additionalLightsCount; i < perObjectLightIndexMap.Length; ++i) | |
perObjectLightIndexMap[i] = -1; | |
cullResults.SetLightIndexMap(perObjectLightIndexMap); | |
if (m_UseStructuredBuffer && additionalLightsCount > 0) | |
{ | |
int lightAndReflectionProbeIndices = cullResults.lightAndReflectionProbeIndexCount; | |
Assertions.Assert.IsTrue(lightAndReflectionProbeIndices > 0, "Pipelines configures additional lights but per-object light and probe indices count is zero."); | |
cullResults.FillLightAndReflectionProbeIndices(ShaderData.instance.GetLightIndicesBuffer(lightAndReflectionProbeIndices)); | |
} | |
perObjectLightIndexMap.Dispose(); | |
return additionalLightsCount; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment