您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
255 行
11 KiB
255 行
11 KiB
using UnityEngine;
|
|
using UnityEngine.Experimental.Rendering;
|
|
using UnityEngine.Experimental.ScriptableRenderLoop;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.ScriptableRenderPipeline;
|
|
|
|
#region RenderPipelineInstance
|
|
public class LowEndRenderPipelineInstance : RenderPipeline
|
|
{
|
|
private readonly LowEndRenderPipeline m_Asset;
|
|
|
|
ShadowRenderPass m_ShadowPass;
|
|
ShadowSettings m_ShadowSettings = ShadowSettings.Default;
|
|
|
|
public LowEndRenderPipelineInstance(LowEndRenderPipeline asset)
|
|
{
|
|
m_Asset = asset;
|
|
|
|
BuildShadowSettings();
|
|
m_ShadowPass = new ShadowRenderPass(m_ShadowSettings);
|
|
}
|
|
|
|
public override void Render(ScriptableRenderContext context, Camera[] cameras)
|
|
{
|
|
base.Render(context, cameras);
|
|
|
|
foreach (Camera camera in cameras)
|
|
{
|
|
CullingParameters cullingParameters;
|
|
camera.farClipPlane = 1000.0f;
|
|
if (!CullResults.GetCullingParameters(camera, out cullingParameters))
|
|
continue;
|
|
|
|
cullingParameters.shadowDistance = QualitySettings.shadowDistance;
|
|
CullResults cull = CullResults.Cull(ref cullingParameters, context);
|
|
|
|
var cmd = new CommandBuffer() { name = "Clear" };
|
|
cmd.ClearRenderTarget(true, false, Color.black);
|
|
context.ExecuteCommandBuffer(cmd);
|
|
cmd.Dispose();
|
|
|
|
ShadowOutput shadowOutput;
|
|
m_ShadowPass.Render(context, cull, out shadowOutput);
|
|
SetupShadowShaderVariables(shadowOutput, context, camera.nearClipPlane, cullingParameters.shadowDistance);
|
|
|
|
context.SetupCameraProperties(camera);
|
|
|
|
SetupLightShaderVariables(cull.visibleLights, context);
|
|
var settings = new DrawRendererSettings(cull, camera, new ShaderPassName("LowEndForwardBase"));
|
|
settings.sorting.flags = SortFlags.CommonOpaque;
|
|
settings.inputFilter.SetQueuesOpaque();
|
|
|
|
#if UNITY_EDITOR
|
|
if (UnityEditor.Lightmapping.bakedGI)
|
|
settings.rendererConfiguration = settings.rendererConfiguration | RendererConfiguration.PerObjectLightmaps;
|
|
#endif
|
|
|
|
context.DrawRenderers(ref settings);
|
|
context.DrawSkybox(camera);
|
|
|
|
settings.sorting.flags = SortFlags.CommonTransparent;
|
|
settings.inputFilter.SetQueuesTransparent();
|
|
context.DrawRenderers(ref settings);
|
|
}
|
|
|
|
context.Submit();
|
|
}
|
|
|
|
private void BuildShadowSettings()
|
|
{
|
|
m_ShadowSettings = ShadowSettings.Default;
|
|
m_ShadowSettings.directionalLightCascadeCount = QualitySettings.shadowCascades;
|
|
m_ShadowSettings.shadowAtlasWidth = m_Asset.ShadowAtlasWidth;
|
|
m_ShadowSettings.shadowAtlasHeight = m_Asset.ShadowAtlasHeight;
|
|
m_ShadowSettings.maxShadowDistance = QualitySettings.shadowDistance;
|
|
m_ShadowSettings.maxShadowLightsSupported = 1;
|
|
m_ShadowSettings.shadowType = ShadowSettings.ShadowType.LIGHTSPACE;
|
|
m_ShadowSettings.renderTextureFormat = RenderTextureFormat.Depth;
|
|
|
|
switch (m_ShadowSettings.directionalLightCascadeCount)
|
|
{
|
|
case 1:
|
|
m_ShadowSettings.directionalLightCascades = new Vector3(1.0f, 0.0f, 0.0f);
|
|
break;
|
|
|
|
case 2:
|
|
m_ShadowSettings.directionalLightCascades = new Vector3(QualitySettings.shadowCascade2Split, 1.0f, 0.0f);
|
|
break;
|
|
|
|
case 4:
|
|
m_ShadowSettings.directionalLightCascades = QualitySettings.shadowCascade4Split;
|
|
break;
|
|
|
|
default:
|
|
Debug.LogError("Invalid Shadow Cascade Settings");
|
|
m_ShadowSettings = ShadowSettings.Default;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#region HelperMethods
|
|
private void SetupLightShaderVariables(VisibleLight[] lights, ScriptableRenderContext context)
|
|
{
|
|
if (lights.Length <= 0)
|
|
return;
|
|
|
|
const int kMaxLights = 8;
|
|
Vector4[] lightPositions = new Vector4[kMaxLights];
|
|
Vector4[] lightColors = new Vector4[kMaxLights];
|
|
Vector4[] lightAttenuations = new Vector4[kMaxLights];
|
|
Vector4[] lightSpotDirections = new Vector4[kMaxLights];
|
|
|
|
int pixelLightCount = Mathf.Min(lights.Length, QualitySettings.pixelLightCount);
|
|
int vertexLightCount = (m_Asset.SupportsVertexLight) ? Mathf.Min(lights.Length - pixelLightCount, kMaxLights) : 0;
|
|
int totalLightCount = pixelLightCount + vertexLightCount;
|
|
|
|
for (int i = 0; i < totalLightCount; ++i)
|
|
{
|
|
VisibleLight currLight = lights[i];
|
|
if (currLight.lightType == LightType.Directional)
|
|
{
|
|
Vector4 dir = -currLight.localToWorld.GetColumn(2);
|
|
lightPositions[i] = new Vector4(dir.x, dir.y, dir.z, 0.0f);
|
|
}
|
|
else
|
|
{
|
|
Vector4 pos = currLight.localToWorld.GetColumn(3);
|
|
lightPositions[i] = new Vector4(pos.x, pos.y, pos.z, 1.0f);
|
|
}
|
|
|
|
lightColors[i] = currLight.finalColor;
|
|
|
|
float rangeSq = currLight.range * currLight.range;
|
|
float quadAtten = (currLight.lightType == LightType.Directional) ? 0.0f : 25.0f / rangeSq;
|
|
|
|
if (currLight.lightType == LightType.Spot)
|
|
{
|
|
Vector4 dir = currLight.localToWorld.GetColumn(2);
|
|
lightSpotDirections[i] = new Vector4(-dir.x, -dir.y, -dir.z, 0.0f);
|
|
|
|
float spotAngle = Mathf.Deg2Rad * currLight.spotAngle;
|
|
float cosOuterAngle = Mathf.Cos(spotAngle * 0.5f);
|
|
float cosInneAngle = Mathf.Cos(spotAngle * 0.25f);
|
|
float angleRange = cosInneAngle - cosOuterAngle;
|
|
lightAttenuations[i] = new Vector4(cosOuterAngle,
|
|
Mathf.Approximately(angleRange, 0.0f) ? 1.0f : angleRange, quadAtten, rangeSq);
|
|
}
|
|
else
|
|
{
|
|
lightSpotDirections[i] = new Vector4(0.0f, 0.0f, 1.0f, 0.0f);
|
|
lightAttenuations[i] = new Vector4(-1.0f, 1.0f, quadAtten, rangeSq);
|
|
}
|
|
}
|
|
|
|
// ambient lighting spherical harmonics values
|
|
const int kSHCoefficients = 7;
|
|
Vector4[] shConstants = new Vector4[kSHCoefficients];
|
|
SphericalHarmonicsL2 ambientSH = RenderSettings.ambientProbe * RenderSettings.ambientIntensity;
|
|
GetShaderConstantsFromNormalizedSH(ref ambientSH, shConstants);
|
|
|
|
CommandBuffer cmd = new CommandBuffer() { name = "SetupShadowShaderConstants" };
|
|
cmd.SetGlobalVectorArray("globalLightPos", lightPositions);
|
|
cmd.SetGlobalVectorArray("globalLightColor", lightColors);
|
|
cmd.SetGlobalVectorArray("globalLightAtten", lightAttenuations);
|
|
cmd.SetGlobalVectorArray("globalLightSpotDir", lightSpotDirections);
|
|
cmd.SetGlobalVector("globalLightCount", new Vector4(pixelLightCount, totalLightCount, 0.0f, 0.0f));
|
|
cmd.SetGlobalVectorArray("globalSH", shConstants);
|
|
context.ExecuteCommandBuffer(cmd);
|
|
cmd.Dispose();
|
|
}
|
|
|
|
void SetupShadowShaderVariables(ShadowOutput shadowOutput, ScriptableRenderContext context, float shadowNear, float shadowFar)
|
|
{
|
|
// PSSM distance settings
|
|
float shadowFrustumDepth = shadowFar - shadowNear;
|
|
Vector3 shadowSplitRatio = m_ShadowSettings.directionalLightCascades;
|
|
|
|
// We set PSSMDistance to infinity for non active cascades so the comparison test always fails for unavailable cascades
|
|
Vector4 PSSMDistances = new Vector4(
|
|
shadowNear + shadowSplitRatio.x * shadowFrustumDepth,
|
|
(shadowSplitRatio.y > 0.0f) ? shadowNear + shadowSplitRatio.y * shadowFrustumDepth : Mathf.Infinity,
|
|
(shadowSplitRatio.z > 0.0f) ? shadowNear + shadowSplitRatio.z * shadowFrustumDepth : Mathf.Infinity,
|
|
Mathf.Infinity);
|
|
|
|
ShadowSliceData[] shadowSlices = shadowOutput.shadowSlices;
|
|
int shadowSliceCount = shadowSlices.Length;
|
|
|
|
const int maxShadowCascades = 4;
|
|
Matrix4x4[] shadowMatrices = new Matrix4x4[maxShadowCascades];
|
|
for (int i = 0; i < shadowSliceCount; ++i)
|
|
shadowMatrices[i] = (shadowSliceCount >= i) ? shadowSlices[i].shadowTransform : Matrix4x4.identity;
|
|
|
|
var setupShadow = new CommandBuffer() { name = "SetupShadowShaderConstants" };
|
|
setupShadow.SetGlobalMatrixArray("_WorldToShadow", shadowMatrices);
|
|
setupShadow.SetGlobalVector("_PSSMDistances", PSSMDistances);
|
|
context.ExecuteCommandBuffer(setupShadow);
|
|
setupShadow.Dispose();
|
|
}
|
|
|
|
private void GetShaderConstantsFromNormalizedSH(ref SphericalHarmonicsL2 ambientProbe, Vector4[] outCoefficients)
|
|
{
|
|
for (int channelIdx = 0; channelIdx < 3; ++channelIdx)
|
|
{
|
|
// Constant + Linear
|
|
// In the shader we multiply the normal is not swizzled, so it's normal.xyz.
|
|
// Swizzle the coefficients to be in { x, y, z, DC } order.
|
|
outCoefficients[channelIdx].x = ambientProbe[channelIdx, 3];
|
|
outCoefficients[channelIdx].y = ambientProbe[channelIdx, 1];
|
|
outCoefficients[channelIdx].z = ambientProbe[channelIdx, 2];
|
|
outCoefficients[channelIdx].w = ambientProbe[channelIdx, 0] - ambientProbe[channelIdx, 6];
|
|
// Quadratic polynomials
|
|
outCoefficients[channelIdx + 3].x = ambientProbe[channelIdx, 4];
|
|
outCoefficients[channelIdx + 3].y = ambientProbe[channelIdx, 5];
|
|
outCoefficients[channelIdx + 3].z = ambientProbe[channelIdx, 6] * 3.0f;
|
|
outCoefficients[channelIdx + 3].w = ambientProbe[channelIdx, 7];
|
|
}
|
|
// Final quadratic polynomial
|
|
outCoefficients[6].x = ambientProbe[0, 8];
|
|
outCoefficients[6].y = ambientProbe[1, 8];
|
|
outCoefficients[6].z = ambientProbe[2, 8];
|
|
outCoefficients[6].w = 1.0f;
|
|
}
|
|
#endregion
|
|
}
|
|
#endregion
|
|
|
|
public class LowEndRenderPipeline : RenderPipelineAsset
|
|
{
|
|
#region AssetAndPipelineCreation
|
|
#if UNITY_EDITOR
|
|
[UnityEditor.MenuItem("Renderloop/Create Low End Pipeline")]
|
|
static void CreateLowEndPipeline()
|
|
{
|
|
var instance = ScriptableObject.CreateInstance<LowEndRenderPipeline>();
|
|
UnityEditor.AssetDatabase.CreateAsset(instance, "Assets/LowEndRenderLoop/LowEndPipeline.asset");
|
|
}
|
|
#endif
|
|
|
|
protected override IRenderPipeline InternalCreatePipeline()
|
|
{
|
|
return new LowEndRenderPipelineInstance(this);
|
|
}
|
|
#endregion
|
|
|
|
#region PipelineAssetSettings
|
|
public bool m_SupportsVertexLight = true;
|
|
public int m_ShadowAtlasWidth = 1024;
|
|
public int m_ShadowAtlasHeight = 1024;
|
|
|
|
public bool SupportsVertexLight { get { return m_SupportsVertexLight;} private set { m_SupportsVertexLight = value; } }
|
|
|
|
public int ShadowAtlasWidth { get { return m_ShadowAtlasWidth; } private set { m_ShadowAtlasWidth = value; } }
|
|
public int ShadowAtlasHeight { get { return m_ShadowAtlasHeight; } private set { m_ShadowAtlasHeight = value; } }
|
|
#endregion
|
|
}
|