您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

501 行
24 KiB

using System;
using System.Collections.Generic;
using Unity.Collections;
#if UNITY_EDITOR
using UnityEditor.Experimental.Rendering.LightweightPipeline;
#endif
using UnityEngine.Rendering;
using UnityEngine.Rendering.PostProcessing;
using UnityEngine.Experimental.GlobalIllumination;
namespace UnityEngine.Experimental.Rendering.LightweightPipeline
{
public partial class LightweightPipeline : RenderPipeline
{
private static class PerFrameBuffer
{
public static int _GlossyEnvironmentColor;
public static int _SubtractiveShadowColor;
}
public LightweightPipelineAsset pipelineAsset { get; private set; }
private IRendererSetup m_DefaultRendererSetup;
private IRendererSetup defaultRendererSetup
{
get
{
if (m_DefaultRendererSetup == null)
m_DefaultRendererSetup = new DefaultRendererSetup();
return m_DefaultRendererSetup;
}
}
CameraComparer m_CameraComparer = new CameraComparer();
LightweightForwardRenderer m_Renderer;
CullResults m_CullResults;
List<int> m_LocalLightIndices = new List<int>();
bool m_IsCameraRendering;
public LightweightPipeline(LightweightPipelineAsset asset)
{
pipelineAsset = asset;
SetSupportedRenderingFeatures();
SetPipelineCapabilities(asset);
PerFrameBuffer._GlossyEnvironmentColor = Shader.PropertyToID("_GlossyEnvironmentColor");
PerFrameBuffer._SubtractiveShadowColor = Shader.PropertyToID("_SubtractiveShadowColor");
SetupLightweightConstanstPass.PerCameraBuffer._ScaledScreenParams = Shader.PropertyToID("_ScaledScreenParams");
m_Renderer = new LightweightForwardRenderer(asset);
// Let engine know we have MSAA on for cases where we support MSAA backbuffer
if (QualitySettings.antiAliasing != pipelineAsset.msaaSampleCount)
QualitySettings.antiAliasing = pipelineAsset.msaaSampleCount;
Shader.globalRenderPipeline = "LightweightPipeline";
m_IsCameraRendering = false;
Lightmapping.SetDelegate(lightsDelegate);
}
public override void Dispose()
{
base.Dispose();
Shader.globalRenderPipeline = "";
SupportedRenderingFeatures.active = new SupportedRenderingFeatures();
#if UNITY_EDITOR
SceneViewDrawMode.ResetDrawMode();
#endif
m_Renderer.Dispose();
Lightmapping.ResetDelegate();
}
public override void Render(ScriptableRenderContext context, Camera[] cameras)
{
if (m_IsCameraRendering)
{
Debug.LogWarning("Nested camera rendering is forbidden. If you are calling camera.Render inside OnWillRenderObject callback, use BeginCameraRender callback instead.");
return;
}
pipelineAsset.savedXRGraphicsConfig.renderScale = pipelineAsset.renderScale;
pipelineAsset.savedXRGraphicsConfig.viewportScale = 1.0f; // Placeholder until viewportScale is all hooked up
// Apply any changes to XRGConfig prior to this point
pipelineAsset.savedXRGraphicsConfig.SetConfig();
base.Render(context, cameras);
BeginFrameRendering(cameras);
GraphicsSettings.lightsUseLinearIntensity = true;
SetupPerFrameShaderConstants();
// Sort cameras array by camera depth
Array.Sort(cameras, m_CameraComparer);
foreach (Camera camera in cameras)
{
BeginCameraRendering(camera);
string renderCameraTag = "Render " + camera.name;
CommandBuffer cmd = CommandBufferPool.Get(renderCameraTag);
using (new ProfilingSample(cmd, renderCameraTag))
{
CameraData cameraData;
InitializeCameraData(camera, out cameraData);
SetupPerCameraShaderConstants(cameraData);
ScriptableCullingParameters cullingParameters;
if (!CullResults.GetCullingParameters(camera, cameraData.isStereoEnabled, out cullingParameters))
{
CommandBufferPool.Release(cmd);
continue;
}
cullingParameters.shadowDistance = Mathf.Min(cameraData.maxShadowDistance, camera.farClipPlane);
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
#if UNITY_EDITOR
try
#endif
{
m_IsCameraRendering = true;
#if UNITY_EDITOR
// Emit scene view UI
if (cameraData.isSceneViewCamera)
ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
#endif
CullResults.Cull(ref cullingParameters, context, ref m_CullResults);
List<VisibleLight> visibleLights = m_CullResults.visibleLights;
RenderingData renderingData;
InitializeRenderingData(ref cameraData, visibleLights,
m_Renderer.maxSupportedLocalLightsPerPass, m_Renderer.maxSupportedVertexLights,
out renderingData);
var setup = cameraData.camera.GetComponent<IRendererSetup>();
if (setup == null)
setup = defaultRendererSetup;
setup.Setup(m_Renderer, ref context, ref m_CullResults, ref renderingData);
m_Renderer.Execute(ref context, ref m_CullResults, ref renderingData);
}
#if UNITY_EDITOR
catch (Exception)
{
CommandBufferPool.Release(cmd);
throw;
}
finally
#endif
{
m_IsCameraRendering = false;
}
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
context.Submit();
}
}
void SetSupportedRenderingFeatures()
{
#if UNITY_EDITOR
SupportedRenderingFeatures.active = new SupportedRenderingFeatures()
{
reflectionProbeSupportFlags = SupportedRenderingFeatures.ReflectionProbeSupportFlags.None,
defaultMixedLightingMode = SupportedRenderingFeatures.LightmapMixedBakeMode.Subtractive,
supportedMixedLightingModes = SupportedRenderingFeatures.LightmapMixedBakeMode.Subtractive,
supportedLightmapBakeTypes = LightmapBakeType.Baked | LightmapBakeType.Mixed,
supportedLightmapsModes = LightmapsMode.CombinedDirectional | LightmapsMode.NonDirectional,
rendererSupportsLightProbeProxyVolumes = false,
rendererSupportsMotionVectors = false,
rendererSupportsReceiveShadows = false,
rendererSupportsReflectionProbes = true
};
SceneViewDrawMode.SetupDrawMode();
#endif
}
void InitializeCameraData(Camera camera, out CameraData cameraData)
{
const float kRenderScaleThreshold = 0.05f;
cameraData.camera = camera;
bool msaaEnabled = camera.allowMSAA && pipelineAsset.msaaSampleCount > 1;
if (msaaEnabled)
cameraData.msaaSamples = (camera.targetTexture != null) ? camera.targetTexture.antiAliasing : pipelineAsset.msaaSampleCount;
else
cameraData.msaaSamples = 1;
cameraData.isSceneViewCamera = camera.cameraType == CameraType.SceneView;
cameraData.isOffscreenRender = camera.targetTexture != null && !cameraData.isSceneViewCamera;
cameraData.isStereoEnabled = IsStereoEnabled(camera);
// TODO: There's currently an issue in engine side that breaks MSAA with texture2DArray.
// for now we force msaa disabled when using texture2DArray. This fixes VR multiple and single pass instanced modes.
if (cameraData.isStereoEnabled && XRGraphicsConfig.eyeTextureDesc.dimension == TextureDimension.Tex2DArray)
cameraData.msaaSamples = 1;
cameraData.isHdrEnabled = camera.allowHDR && pipelineAsset.supportsHDR;
cameraData.postProcessLayer = camera.GetComponent<PostProcessLayer>();
cameraData.postProcessEnabled = cameraData.postProcessLayer != null && cameraData.postProcessLayer.isActiveAndEnabled;
Rect cameraRect = camera.rect;
cameraData.isDefaultViewport = (!(Math.Abs(cameraRect.x) > 0.0f || Math.Abs(cameraRect.y) > 0.0f ||
Math.Abs(cameraRect.width) < 1.0f || Math.Abs(cameraRect.height) < 1.0f));
// If XR is enabled, use XR renderScale.
// Discard variations lesser than kRenderScaleThreshold.
// Scale is only enabled for gameview.
float usedRenderScale = XRGraphicsConfig.enabled ? pipelineAsset.savedXRGraphicsConfig.renderScale : pipelineAsset.renderScale;
cameraData.renderScale = (Mathf.Abs(1.0f - usedRenderScale) < kRenderScaleThreshold) ? 1.0f : usedRenderScale;
cameraData.renderScale = (camera.cameraType == CameraType.Game) ? cameraData.renderScale : 1.0f;
cameraData.requiresDepthTexture = pipelineAsset.supportsCameraDepthTexture || cameraData.isSceneViewCamera;
cameraData.requiresSoftParticles = pipelineAsset.supportsSoftParticles;
cameraData.requiresOpaqueTexture = pipelineAsset.supportsCameraOpaqueTexture;
cameraData.opaqueTextureDownsampling = pipelineAsset.opaqueDownsampling;
bool anyShadowsEnabled = pipelineAsset.supportsDirectionalShadows || pipelineAsset.supportsLocalShadows;
cameraData.maxShadowDistance = (anyShadowsEnabled) ? pipelineAsset.shadowDistance : 0.0f;
LightweightAdditionalCameraData additionalCameraData = camera.gameObject.GetComponent<LightweightAdditionalCameraData>();
if (additionalCameraData != null)
{
cameraData.maxShadowDistance = (additionalCameraData.renderShadows) ? cameraData.maxShadowDistance : 0.0f;
cameraData.requiresDepthTexture &= additionalCameraData.requiresDepthTexture;
cameraData.requiresOpaqueTexture &= additionalCameraData.requiresColorTexture;
}
else if (!cameraData.isSceneViewCamera && camera.cameraType != CameraType.Reflection && camera.cameraType != CameraType.Preview)
{
cameraData.requiresDepthTexture = false;
cameraData.requiresOpaqueTexture = false;
}
cameraData.requiresDepthTexture |= cameraData.postProcessEnabled;
}
void InitializeRenderingData(ref CameraData cameraData, List<VisibleLight> visibleLights, int maxSupportedLocalLightsPerPass, int maxSupportedVertexLights, out RenderingData renderingData)
{
m_LocalLightIndices.Clear();
bool hasDirectionalShadowCastingLight = false;
bool hasLocalShadowCastingLight = false;
if (cameraData.maxShadowDistance > 0.0f)
{
for (int i = 0; i < visibleLights.Count; ++i)
{
Light light = visibleLights[i].light;
bool castShadows = light != null && light.shadows != LightShadows.None;
if (visibleLights[i].lightType == LightType.Directional)
{
hasDirectionalShadowCastingLight |= castShadows;
}
else
{
hasLocalShadowCastingLight |= castShadows;
m_LocalLightIndices.Add(i);
}
}
}
renderingData.cameraData = cameraData;
InitializeLightData(visibleLights, maxSupportedLocalLightsPerPass, maxSupportedVertexLights, out renderingData.lightData);
InitializeShadowData(hasDirectionalShadowCastingLight, hasLocalShadowCastingLight, out renderingData.shadowData);
renderingData.supportsDynamicBatching = pipelineAsset.supportsDynamicBatching;
}
void InitializeShadowData(bool hasDirectionalShadowCastingLight, bool hasLocalShadowCastingLight, out ShadowData shadowData)
{
// Until we can have keyword stripping forcing single cascade hard shadows on gles2
bool supportsScreenSpaceShadows = SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES2;
shadowData.renderDirectionalShadows = pipelineAsset.supportsDirectionalShadows && hasDirectionalShadowCastingLight;
// we resolve shadows in screenspace when cascades are enabled to save ALU as computing cascade index + shadowCoord on fragment is expensive
shadowData.requiresScreenSpaceShadowResolve = shadowData.renderDirectionalShadows && supportsScreenSpaceShadows && pipelineAsset.cascadeCount > 1;
shadowData.directionalLightCascadeCount = (shadowData.requiresScreenSpaceShadowResolve) ? pipelineAsset.cascadeCount : 1;
shadowData.directionalShadowAtlasWidth = pipelineAsset.directionalShadowAtlasResolution;
shadowData.directionalShadowAtlasHeight = pipelineAsset.directionalShadowAtlasResolution;
switch (shadowData.directionalLightCascadeCount)
{
case 1:
shadowData.directionalLightCascades = new Vector3(1.0f, 0.0f, 0.0f);
break;
case 2:
shadowData.directionalLightCascades = new Vector3(pipelineAsset.cascade2Split, 1.0f, 0.0f);
break;
default:
shadowData.directionalLightCascades = pipelineAsset.cascade4Split;
break;
}
shadowData.renderLocalShadows = pipelineAsset.supportsLocalShadows && hasLocalShadowCastingLight;
shadowData.localShadowAtlasWidth = shadowData.localShadowAtlasHeight = pipelineAsset.localShadowAtlasResolution;
shadowData.supportsSoftShadows = pipelineAsset.supportsSoftShadows;
shadowData.bufferBitCount = 16;
shadowData.renderedDirectionalShadowQuality = LightShadows.None;
shadowData.renderedLocalShadowQuality = LightShadows.None;
}
void InitializeLightData(List<VisibleLight> visibleLights, int maxSupportedLocalLightsPerPass, int maxSupportedVertexLights, out LightData lightData)
{
int visibleLightsCount = Math.Min(visibleLights.Count, pipelineAsset.maxPixelLights);
lightData.mainLightIndex = GetMainLight(visibleLights);
// If we have a main light we don't shade it in the per-object light loop. We also remove it from the per-object cull list
int mainLightPresent = (lightData.mainLightIndex >= 0) ? 1 : 0;
int additionalPixelLightsCount = Math.Min(visibleLightsCount - mainLightPresent, maxSupportedLocalLightsPerPass);
int vertexLightCount = (pipelineAsset.supportsVertexLight) ? Math.Min(visibleLights.Count, maxSupportedLocalLightsPerPass) - additionalPixelLightsCount : 0;
vertexLightCount = Math.Min(vertexLightCount, maxSupportedVertexLights);
lightData.pixelAdditionalLightsCount = additionalPixelLightsCount;
lightData.totalAdditionalLightsCount = additionalPixelLightsCount + vertexLightCount;
lightData.visibleLights = visibleLights;
lightData.visibleLocalLightIndices = m_LocalLightIndices;
}
// Main Light is always a directional light
int GetMainLight(List<VisibleLight> visibleLights)
{
int totalVisibleLights = visibleLights.Count;
if (totalVisibleLights == 0 || pipelineAsset.maxPixelLights == 0)
return -1;
for (int i = 0; i < totalVisibleLights; ++i)
{
VisibleLight currLight = visibleLights[i];
// Particle system lights have the light property as null. We sort lights so all particles lights
// come last. Therefore, if first light is particle light then all lights are particle lights.
// In this case we either have no main light or already found it.
if (currLight.light == null)
break;
// In case no shadow light is present we will return the brightest directional light
if (currLight.lightType == LightType.Directional)
return i;
}
return -1;
}
void SetupPerFrameShaderConstants()
{
// When glossy reflections are OFF in the shader we set a constant color to use as indirect specular
SphericalHarmonicsL2 ambientSH = RenderSettings.ambientProbe;
Color linearGlossyEnvColor = new Color(ambientSH[0, 0], ambientSH[1, 0], ambientSH[2, 0]) * RenderSettings.reflectionIntensity;
Color glossyEnvColor = CoreUtils.ConvertLinearToActiveColorSpace(linearGlossyEnvColor);
Shader.SetGlobalVector(PerFrameBuffer._GlossyEnvironmentColor, glossyEnvColor);
// Used when subtractive mode is selected
Shader.SetGlobalVector(PerFrameBuffer._SubtractiveShadowColor, CoreUtils.ConvertSRGBToActiveColorSpace(RenderSettings.subtractiveShadowColor));
}
void SetupPerCameraShaderConstants(CameraData cameraData)
{
float cameraWidth = (float)cameraData.camera.pixelWidth * cameraData.renderScale;
float cameraHeight = (float)cameraData.camera.pixelHeight * cameraData.renderScale;
Shader.SetGlobalVector(SetupLightweightConstanstPass.PerCameraBuffer._ScaledScreenParams, new Vector4(cameraWidth, cameraHeight, 1.0f + 1.0f / cameraWidth, 1.0f + 1.0f / cameraHeight));
}
public static bool LightDataGIExtract(Light light, ref LightDataGI lightData)
{
// TODO: Only take into account the light dimmer when we have real time GI.
lightData.instanceID = light.GetInstanceID();
lightData.color = LinearColor.Convert(light.color, light.intensity);
lightData.indirectColor = LightmapperUtils.ExtractIndirect(light);
// The difference is that `light.lightmapBakeType` is the intent, e.g.you want a mixed light with shadowmask. But then the overlap test might detect more than 4 overlapping volumes and force a light to fallback to baked.
// In that case `light.bakingOutput.lightmapBakeType` would be baked, instead of mixed, whereas `light.lightmapBakeType` would still be mixed. But this difference is only relevant in editor builds
#if UNITY_EDITOR
lightData.mode = LightmapperUtils.Extract(light.lightmapBakeType);
#else
lightData.mode = LightmapperUtils.Extract(light.bakingOutput.lightmapBakeType);
#endif
lightData.shadow = (byte)(light.shadows != LightShadows.None ? 1 : 0);
if (true)
{
// For LWRP we need to divide the analytic light color by PI (HDRP do explicit PI division for Lambert, but built in Unity and the GI don't for punctual lights)
// We apply it on both direct and indirect are they are separated, seems that direct is not used if we used mixed mode with indirect or shadowmask bake.
switch (light.type)
{
case LightType.Directional:
lightData.orientation.SetLookRotation(light.transform.forward, Vector3.up);
lightData.position = Vector3.zero;
lightData.range = 0.0f;
lightData.coneAngle = 0.0f;
lightData.innerConeAngle = 0.0f;
#if UNITY_EDITOR
lightData.shape0 = light.shadows != LightShadows.None ? (Mathf.Deg2Rad * light.shadowAngle) : 0.0f;
#else
lightData.shape0 = 0.0f;
#endif
lightData.shape1 = 0.0f;
lightData.type = UnityEngine.Experimental.GlobalIllumination.LightType.Directional;
lightData.falloff = FalloffType.Undefined;
break;
case LightType.Spot:
lightData.orientation = light.transform.rotation;
lightData.position = light.transform.position;
lightData.range = light.range;
lightData.coneAngle = light.spotAngle * Mathf.Deg2Rad; // coneAngle is the full angle
lightData.innerConeAngle = LightmapperUtils.ExtractInnerCone(light) * 0.5f;
#if UNITY_EDITOR
lightData.shape0 = light.shadows != LightShadows.None ? light.shadowRadius : 0.0f;
#else
lightData.shape0 = 0.0f;
#endif
lightData.shape1 = 0.0f;
lightData.type = UnityEngine.Experimental.GlobalIllumination.LightType.Spot;
lightData.falloff = FalloffType.InverseSquared;
break;
case LightType.Point:
lightData.orientation = Quaternion.identity;
lightData.position = light.transform.position;
lightData.range = light.range;
lightData.coneAngle = 0.0f;
lightData.innerConeAngle = 0.0f;
#if UNITY_EDITOR
lightData.shape0 = light.shadows != LightShadows.None ? light.shadowRadius : 0.0f;
#else
lightData.shape0 = 0.0f;
#endif
lightData.shape1 = 0.0f;
lightData.type = UnityEngine.Experimental.GlobalIllumination.LightType.Point;
lightData.falloff = true ? FalloffType.InverseSquared : FalloffType.InverseSquaredNoRangeAttenuation;
break;
// Note: We don't support this type in HDRP, but ini just in case
case LightType.Area:
lightData.orientation = light.transform.rotation;
lightData.position = light.transform.position;
lightData.range = light.range;
lightData.coneAngle = 0.0f;
lightData.innerConeAngle = 0.0f;
#if UNITY_EDITOR
lightData.shape0 = light.areaSize.x;
lightData.shape1 = light.areaSize.y;
#else
lightData.shape0 = 0.0f;
lightData.shape1 = 0.0f;
#endif
lightData.type = UnityEngine.Experimental.GlobalIllumination.LightType.Rectangle;
lightData.falloff = FalloffType.Undefined;
break;
default:
Debug.Assert(false, "Encountered an unknown LightType.");
break;
}
}
return true;
}
public static Lightmapping.RequestLightsDelegate lightsDelegate = (Light[] requests, NativeArray<LightDataGI> lightsOutput) =>
{
// Get all lights in the scene
LightDataGI lightData = new LightDataGI();
for (int i = 0; i < requests.Length; i++)
{
Light light = requests[i];
#if UNITY_EDITOR
if (LightmapperUtils.Extract(light.lightmapBakeType) == LightMode.Realtime)
lightData.InitNoBake(light.GetInstanceID());
else
LightDataGIExtract(light, ref lightData);
#else
ld.InitNoBake(l.GetInstanceID());
#endif
lightsOutput[i] = lightData;
}
};
}
}