这一节讲阴影,看的我都有阴影了!
言归正传:这节内容比较多,问题也比较多,下面列出调好的全部代码,注释以后慢慢加。
具体理论看原文吧:中文翻译,英语原文 源码
Runtime:
CustomRenderPipelineAsset:
using UnityEngine;
using UnityEngine.Rendering;
[CreateAssetMenu(menuName = "Rendering/Custom Render Pipeline")]
public class CustomRenderPipelineAsset : RenderPipelineAsset
{
[SerializeField]
bool useDynamicBatching = true, useGPUInstancing = true, useSRPBatcher = true;
[SerializeField]
ShadowSettings shadows = default;
//系统调用:获取自定义渲染管线,调用其Render
protected override RenderPipeline CreatePipeline()
{
return new CustomRenderPipeline(useDynamicBatching, useGPUInstancing, useSRPBatcher, shadows);
}
}
CustomRenderPipeline:
using UnityEngine;
using UnityEngine.Rendering;
public class CustomRenderPipeline : RenderPipeline
{
CameraRender renderer = new CameraRender();
bool useDynamicBatching, useGPUInstancing;
ShadowSettings shadowSettings;
private CustomRenderPipeline() { }
public CustomRenderPipeline(bool useDynamicBatching, bool useGPUInstancing, bool useSRPBatcher, ShadowSettings shadows)
{
this.shadowSettings = shadows;
this.useDynamicBatching = useDynamicBatching;
this.useGPUInstancing = useGPUInstancing;
GraphicsSettings.useScriptableRenderPipelineBatching = useSRPBatcher;
GraphicsSettings.lightsUseLinearIntensity = true;
}
//每帧调用
protected override void Render(ScriptableRenderContext context, Camera[] cameras)
{
//遍历所有相机
foreach (Camera camera in cameras)
{
//渲染器用指定的参数对每个相机进行渲染
renderer.Render(context, camera, useDynamicBatching, useGPUInstancing, shadowSettings);
}
}
}
CameraRenderer
using UnityEngine;
using UnityEngine.Rendering;
public partial class CameraRender
{
private ScriptableRenderContext context;
private Camera camera;
private Lighting lighting = new Lighting();
private const string bufferName = "Render Camera";
private CommandBuffer buffer = new CommandBuffer()
{
name = bufferName
};
private CullingResults cullingResults;
private static ShaderTagId unlitShaderTagId = new ShaderTagId("SRPDefaultUnlit");
private static ShaderTagId litShaderTagId = new ShaderTagId("CustomLit");
public void Render(ScriptableRenderContext context, Camera camera,
bool useDynamicBatching, bool useGPUInstancing, ShadowSettings shadowSettings)
{
this.context = context;
this.camera = camera;
//处理编辑器字符串GC问题,打包没有这个调用(分部方法)
PrepareBuffer();
//同步Scene窗口显示
PrepareForSceneWindow();
//1.剔除:使用Unity提供的方法
if (!Cull(shadowSettings.maxDistance))
{
return;
}
//FrameDebug调试用
buffer.BeginSample(SampleName);
ExecuteBuffer();
//2.灯光设置,阴影设置,阴影渲染:过程比较复杂,自行查看
lighting.Setup(context, cullingResults, shadowSettings);
//FrameDebug调试用
buffer.EndSample(SampleName);
//3.渲染器设置
Setup();
//4.画可见的几何形状
DrawVisibleGeometry(useDynamicBatching, useGPUInstancing);
//5.提示不支持的Shader:编辑器用
DrawUnsupportedShaders();
//6.画Gizmos:编辑器用
DrawGizmos();
//7.灯光清除操作:主要是释放阴影图集空间
lighting.Cleanup();
//8.提交命令执行
Submit();
}
private void Setup()
{
context.SetupCameraProperties(camera);
CameraClearFlags flags = camera.clearFlags;
buffer.ClearRenderTarget(
flags <= CameraClearFlags.Depth,
flags == CameraClearFlags.Color,
flags == CameraClearFlags.Color ? camera.backgroundColor.linear : Color.clear);
buffer.BeginSample(SampleName);
ExecuteBuffer();
}
private void DrawVisibleGeometry(bool useDynamicBatching, bool useGPUInstancing)
{
//画不透明物体
SortingSettings sortingSettings = new SortingSettings(camera)
{
criteria = SortingCriteria.CommonOpaque
};
DrawingSettings drawingSettings = new DrawingSettings(unlitShaderTagId, sortingSettings)
{
enableDynamicBatching = useDynamicBatching,
enableInstancing = useGPUInstancing
};
//设置Lit的ShaderTagId
drawingSettings.SetShaderPassName(1, litShaderTagId);
FilteringSettings filteringSettings = new FilteringSettings(RenderQueueRange.opaque);
context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);
context.DrawSkybox(camera);
//画透明物体
sortingSettings = new SortingSettings(camera)
{
criteria = SortingCriteria.CommonTransparent
};
drawingSettings = new DrawingSettings(unlitShaderTagId, sortingSettings);
//这里还得设置一次
drawingSettings.SetShaderPassName(1, litShaderTagId);
filteringSettings = new FilteringSettings(RenderQueueRange.transparent);
context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);
}
private void Submit()
{
buffer.EndSample(SampleName);
ExecuteBuffer();
context.Submit();
}
private void ExecuteBuffer()
{
context.ExecuteCommandBuffer(buffer);
buffer.Clear();
}
private bool Cull(float maxShadowDistance)
{
if (camera.TryGetCullingParameters(out ScriptableCullingParameters p))
{
p.shadowDistance = Mathf.Min(maxShadowDistance, camera.farClipPlane);
cullingResults = context.Cull(ref p);
return true;
}
return false;
}
}
CameraRenderer.Editor
using UnityEditor;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
public partial class CameraRender
{
partial void DrawUnsupportedShaders();
partial void DrawGizmos();
partial void PrepareForSceneWindow();
partial void PrepareBuffer();
#if UNITY_EDITOR
private static ShaderTagId[] legacyShaderTagIds = {
new ShaderTagId("Always"),
new ShaderTagId("ForwardBase"),
new ShaderTagId("PrepassBase"),
new ShaderTagId("Vertex"),
new ShaderTagId("VertexLMRGBM"),
new ShaderTagId("VertexLM")
};
private static Material errorMaterial;
private string SampleName { get; set; }
partial void DrawUnsupportedShaders()
{
if (errorMaterial == null)
{
errorMaterial = new Material(Shader.Find("Hidden/InternalErrorShader"));
}
DrawingSettings drawingSettings = new DrawingSettings(legacyShaderTagIds[0], new SortingSettings(camera))
{
overrideMaterial = errorMaterial
};
for (int i = 1; i < legacyShaderTagIds.Length; i++)
{
drawingSettings.SetShaderPassName(i, legacyShaderTagIds[i]);
}
FilteringSettings filteringSettings = FilteringSettings.defaultValue;
context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);
}
partial void DrawGizmos()
{
if (Handles.ShouldRenderGizmos())
{
context.DrawGizmos(camera, GizmoSubset.PreImageEffects);
context.DrawGizmos(camera, GizmoSubset.PostImageEffects);
}
}
partial void PrepareForSceneWindow()
{
if (camera.cameraType == CameraType.SceneView)
{
//场景窗口显示
ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
}
}
partial void PrepareBuffer()
{
Profiler.BeginSample("Editor Only");
buffer.name = SampleName = camera.name;//会产生GC
Profiler.EndSample();
}
#else
const string SampleName = bufferName;//运行时用常量
#endif
}
Lighting:设置Shader里用到的灯光数据
using System;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Rendering;
public class Lighting
{
const string bufferName = "Lighting";
CommandBuffer buffer = new CommandBuffer()
{
name = bufferName
};
const int maxDirLightCount = 4;
static int
//dirLightColorId = Shader.PropertyToID("_DirectionalLightColor"),
//dirLightDirectionId = Shader.PropertyToID("_DirectionalLightDirection");
dirLightCountId = Shader.PropertyToID("_DirectionalLightCount"),
dirLightColorsId = Shader.PropertyToID("_DirectionalLightColors"),
dirLightDirectionsId = Shader.PropertyToID("_DirectionalLightDirections"),
dirLightShadowDataId = Shader.PropertyToID("_DirectionalLightShadowData");
static Vector4[]
dirLightColors = new Vector4[maxDirLightCount],
dirLightDirections = new Vector4[maxDirLightCount],
dirLightShadowData = new Vector4[maxDirLightCount];
CullingResults cullingResults;
Shadows shadows = new Shadows();
public void Setup(ScriptableRenderContext context, CullingResults cullingResults, ShadowSettings shadowSettings)
{
this.cullingResults = cullingResults;
buffer.BeginSample(bufferName);
shadows.Setup(context, cullingResults, shadowSettings);
SetupLights();
shadows.Render();
buffer.EndSample(bufferName);
context.ExecuteCommandBuffer(buffer);
buffer.Clear();
}
private void SetupLights()
{
NativeArray<VisibleLight> visibleLights = cullingResults.visibleLights;
int dirLightCount = 0;
for (int i = 0; i < visibleLights.Length; i++)
{
VisibleLight visibleLight = visibleLights[i];
if (visibleLight.lightType == LightType.Directional)
{
SetupDirectionalLight(dirLightCount++, ref visibleLight);
if (dirLightCount > maxDirLightCount)
{
break;
}
}
buffer.SetGlobalInt(dirLightCountId, visibleLights.Length);
buffer.SetGlobalVectorArray(dirLightColorsId, dirLightColors);
buffer.SetGlobalVectorArray(dirLightDirectionsId, dirLightDirections);
}
buffer.SetGlobalVectorArray(dirLightShadowDataId, dirLightShadowData);
}
private void SetupDirectionalLight(int index, ref VisibleLight visibleLight)
{
dirLightColors[index] = visibleLight.finalColor;
dirLightDirections[index] = -visibleLight.localToWorldMatrix.GetColumn(2);
dirLightShadowData[index] = shadows.ReserverDirectionalShadows(visibleLight.light, index);
}
public void Cleanup()
{
shadows.Cleanup();
}
}
Shadows
using System;
using UnityEngine;
using UnityEngine.Rendering;
public class Shadows
{
//产生阴影的方向光
struct ShadowedDirectionalLight
{
public int visibleLightIndex;
public float slopeScaleBias;
public float nearPlaneOffset;
}
ShadowedDirectionalLight[] shadowedDirectionalLights = new ShadowedDirectionalLight[maxShadowedDirectionalLightCount];
int shadowedDirectionalLightCount;
const int maxShadowedDirectionalLightCount = 4, maxCascades = 4;
static int dirShadowAtlasId = Shader.PropertyToID("_DirectionalShadowAtlas");
static int dirShadowMatricesId = Shader.PropertyToID("_DirectionalShadowMatrices");
static int cascadeCountId = Shader.PropertyToID("_CascadeCount");
static int cascadeCullingSphereId = Shader.PropertyToID("_CascadeCullingSpheres");
static int cascadeDataId = Shader.PropertyToID("_CascadeData");
static int shadowAtlasSizeId = Shader.PropertyToID("_ShadowAtlasSize");
//static int shadowDistanceId = Shader.PropertyToID("_ShadowDistance");
static int shadowDistanceFadeId = Shader.PropertyToID("_ShadowDistanceFade");
static string[] directionalFilterKeywords = {
"_DIRECTIONAL_PCF3",
"_DIRECTIONAL_PCF5",
"_DIRECTIONAL_PCF7",
};
static string[] cascadeBlendKeywords = {
"_CASCADE_BLEND_SOFT",
"_CASCADE_BLEND_DITHER"
};
static Vector4[] cascadeCullingSpheres = new Vector4[maxCascades];
static Vector4[] cascadeData = new Vector4[maxCascades];
static Matrix4x4[] dirShadowMatrixces = new Matrix4x4[maxShadowedDirectionalLightCount * maxCascades];
const string bufferName = "Shadows";
CommandBuffer buffer = new CommandBuffer
{
name = bufferName
};
ScriptableRenderContext context;
CullingResults cullingResults;
ShadowSettings settings;
public void Setup(ScriptableRenderContext context, CullingResults cullingResults, ShadowSettings settings)
{
this.context = context;
this.cullingResults = cullingResults;
this.settings = settings;
shadowedDirectionalLightCount = 0;
}
private void ExecuteBuffer()
{
context.ExecuteCommandBuffer(buffer);
buffer.Clear();
}
//为灯光的阴影贴图保留空间,存储信息
public Vector3 ReserverDirectionalShadows(Light light, int visibleLightIndex)
{
if (shadowedDirectionalLightCount < maxShadowedDirectionalLightCount &&
light.shadows != LightShadows.None && light.shadowStrength > 0f &&
cullingResults.GetShadowCasterBounds(visibleLightIndex, out Bounds b))
{
shadowedDirectionalLights[shadowedDirectionalLightCount] =
new ShadowedDirectionalLight
{
visibleLightIndex = visibleLightIndex,
slopeScaleBias = light.shadowBias,
nearPlaneOffset = light.shadowNearPlane
};
return new Vector3(light.shadowStrength, settings.directional.cascadeCount * shadowedDirectionalLightCount++,
light.shadowNormalBias);
}
return Vector3.zero;
}
public void Render()
{
if (shadowedDirectionalLightCount > 0)
{
RenderDirectionalShadows();
}
}
private void RenderDirectionalShadows()
{
int atlasSize = (int)settings.directional.atlasSize;
//开辟空间
buffer.GetTemporaryRT(dirShadowAtlasId, atlasSize, atlasSize, 32, FilterMode.Bilinear, RenderTextureFormat.Shadowmap);
//设置渲染目标
buffer.SetRenderTarget(dirShadowAtlasId, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store);
//只关心深度,清除深度
buffer.ClearRenderTarget(true, false, Color.clear);
buffer.BeginSample(bufferName);
ExecuteBuffer();
//支持多个光阴影,对阴影贴图切块(目前:要么1整块,要么四个块)
//增加阴影级联
int tiles = shadowedDirectionalLightCount * settings.directional.cascadeCount;
int split = tiles <= 1 ? 1 : tiles <= 4 ? 2 : 4;
int tileSize = atlasSize / split;
for (int i = 0; i < shadowedDirectionalLightCount; i++)
{
RenderDirectionalShadows(i, split, tileSize);
}
buffer.SetGlobalInt(cascadeCountId, settings.directional.cascadeCount);
buffer.SetGlobalVectorArray(cascadeCullingSphereId, cascadeCullingSpheres);
buffer.SetGlobalVectorArray(cascadeDataId, cascadeData);
//将矩阵发送到GPU
buffer.SetGlobalMatrixArray(dirShadowMatricesId, dirShadowMatrixces);
//buffer.SetGlobalFloat(shadowDistanceId, settings.maxDistance);
float f = 1f - settings.directional.cascadeFade;//第三个值用于最后级联阴影的渐隐
buffer.SetGlobalVector(shadowDistanceFadeId, new Vector4(1f / settings.maxDistance, 1f / settings.distanceFade, 1f / (1f - f * f)));
SetKeywords(directionalFilterKeywords, (int)settings.directional.filter - 1);
SetKeywords(cascadeBlendKeywords, (int)settings.directional.cascadeBlend - 1);
//设置图集大小和纹理像素大小
buffer.SetGlobalVector(shadowAtlasSizeId, new Vector4(atlasSize, 1f / atlasSize));
buffer.EndSample(bufferName);
ExecuteBuffer();
}
private void SetKeywords(string[] keywords, int enabledIndex)
{
//int enabledIndex = (int)settings.directional.filter - 1;
for (int i = 0; i < keywords.Length; i++)
{
if (i == enabledIndex)
{
buffer.EnableShaderKeyword(keywords[i]);
}
else
{
buffer.DisableShaderKeyword(keywords[i]);
}
}
}
private void RenderDirectionalShadows(int index, int split, int tileSize)
{
ShadowedDirectionalLight light = shadowedDirectionalLights[index];
//渲染阴影设置
var shadowSettings = new ShadowDrawingSettings(cullingResults, light.visibleLightIndex);
int cascadeCount = settings.directional.cascadeCount;
int tileOffset = index * cascadeCount;
Vector3 ratios = settings.directional.CascadeRatios;
float cullingFactor = 1f - settings.directional.cascadeFade;
for (int i = 0; i < cascadeCount; i++)
{
//计算渲染阴影需要的数据:如,投影矩阵,视角矩阵。。。
cullingResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(
light.visibleLightIndex, i, cascadeCount, ratios, tileSize,
light.nearPlaneOffset, out Matrix4x4 viewMatrix,
out Matrix4x4 projectionMatrix, out ShadowSplitData splitData);
//尝试从较大的级联中剔除某些阴影投射器
splitData.shadowCascadeBlendCullingFactor = cullingFactor;
shadowSettings.splitData = splitData;
if (index == 0)
{
SetCascadeData(i, splitData.cullingSphere, tileSize);
}
int tileIndex = tileOffset + i;
Vector2 offset = SetTileViewport(tileIndex, split, tileSize);
//设置阴影投影矩阵:从世界空间到灯光空间
//dirShadowMatrixces[index] = projectionMatrix * viewMatrix;
dirShadowMatrixces[tileIndex] = ConvertToAtlasMatrix(projectionMatrix * viewMatrix, offset, split);
//设置矩阵
buffer.SetViewProjectionMatrices(viewMatrix, projectionMatrix);
buffer.SetGlobalDepthBias(0, light.slopeScaleBias);
ExecuteBuffer();
//渲染阴影:需要Shader里有LightMode=ShadowCaster的Pass
context.DrawShadows(ref shadowSettings);
buffer.SetGlobalDepthBias(0f, 0f);
}
}
private void SetCascadeData(int index, Vector4 cullingSphere, float tileSize)
{
float texelSize = 2f * cullingSphere.w / tileSize;
float filterSize = texelSize * ((float)settings.directional.filter + 1f);
cullingSphere.w -= filterSize;
//cascadeData[index].x = 1f / cullingSphere.w;
//此处计算平方,着色器里就不用:距离球体中心的平方距离和半径比较
cullingSphere.w *= cullingSphere.w;
cascadeCullingSpheres[index] = cullingSphere;
cascadeData[index] = new Vector4(1f / cullingSphere.w, filterSize * 1.4142136f);
}
private Vector2 SetTileViewport(int index, int split, float tileSize)
{
Vector2 offset = new Vector2(index % split, index / split);
buffer.SetViewport(new Rect(offset.x * tileSize, offset.y * tileSize, tileSize, tileSize));
return offset;
}
//从世界空间到阴影图块空间到矩阵
private Matrix4x4 ConvertToAtlasMatrix(Matrix4x4 m, Vector2 offset, int split)
{
if (SystemInfo.usesReversedZBuffer)
{
m.m20 = -m.m20;
m.m21 = -m.m21;
m.m22 = -m.m22;
m.m23 = -m.m23;
}
float scale = 1f / split;
m.m00 = (0.5f * (m.m00 + m.m30) + offset.x * m.m30) * scale;
m.m01 = (0.5f * (m.m01 + m.m31) + offset.x * m.m31) * scale;
m.m02 = (0.5f * (m.m02 + m.m32) + offset.x * m.m32) * scale;
m.m03 = (0.5f * (m.m03 + m.m33) + offset.x * m.m33) * scale;
m.m10 = (0.5f * (m.m10 + m.m30) + offset.y * m.m30) * scale;
m.m11 = (0.5f * (m.m11 + m.m31) + offset.y * m.m31) * scale;
m.m12 = (0.5f * (m.m12 + m.m32) + offset.y * m.m32) * scale;
m.m13 = (0.5f * (m.m13 + m.m33) + offset.y * m.m33) * scale;
m.m20 = 0.5f * (m.m20 + m.m30);
m.m21 = 0.5f * (m.m21 + m.m31);
m.m22 = 0.5f * (m.m22 + m.m32);
m.m23 = 0.5f * (m.m23 + m.m33);
return m;
}
public void Cleanup()
{
if (shadowedDirectionalLightCount > 0)
{
buffer.ReleaseTemporaryRT(dirShadowAtlasId);
ExecuteBuffer();
}
}
}
ShadowSettings
using UnityEngine;
[System.Serializable]
public class ShadowSettings
{
public enum TextureSize
{
_256 = 256,
_512 = 512,
_1024 = 1024,
_2048 = 2048,
_4096 = 4096,
_8192 = 8192
}
public enum FilterMode
{
PCF2x2, PCF3x3, PCF5x5, PCF7x7
}
//支持方向光
[System.Serializable]
public struct Directional
{
public enum CascadeBlendMode
{
Hard, Soft, Dither
}
public TextureSize atlasSize;
public FilterMode filter;
[Range(1, 4)]
public int cascadeCount;
[Range(0f, 1f)]
public float cascadeRatio1, cascadeRatio2, cascadeRatio3;
public Vector3 CascadeRatios => new Vector3(cascadeRatio1, cascadeRatio2, cascadeRatio3);
[Range(0.001f, 1f)]
public float cascadeFade;
public CascadeBlendMode cascadeBlend;
}
[Min(0.001f)]
public float maxDistance = 100f;
[Range(0.001f, 1f)]
public float distanceFade = 0.1f;
//设置方向光默认贴图大小
public Directional directional = new Directional
{
atlasSize = TextureSize._1024,
filter = FilterMode.PCF2x2,
cascadeCount = 4,
cascadeRatio1 = 0.1f,
cascadeRatio2 = 0.25f,
cascadeRatio3 = 0.5f,
cascadeFade = 0.1f,
cascadeBlend = Directional.CascadeBlendMode.Hard
};
}
ShaderLibrary
UnityInput
#ifndef CUSTOM_UNITY_INPUT_INCLUDE
#define CUSTOM_UNITY_INPUT_INCLUDE
CBUFFER_START(UnityPerDraw)
float4x4 unity_ObjectToWorld;
float4x4 unity_WorldToObject;
float4 unity_LODFade;//需要添加
real4 unity_WorldTransformParams;
CBUFFER_END
float3 _WorldSpaceCameraPos;
float4x4 unity_MatrixVP;
float4x4 unity_MatrixV;
float4x4 glstate_matrix_projection;
#endif
Common
#ifndef CUSTOM_COMMON_INCLUDE
#define CUSTOM_COMMON_INCLUDE
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
#include "UnityInput.hlsl"
#define UNITY_MATRIX_M unity_ObjectToWorld
#define UNITY_MATRIX_I_M unity_WorldToObject
#define UNITY_MATRIX_V unity_MatrixV
#define UNITY_MATRIX_VP unity_MatrixVP
#define UNITY_MATRIX_P glstate_matrix_projection
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"
float Square(float v)
{
return v * v;
}
float DistanceSquared(float3 pA, float3 pB)
{
return dot(pA - pB, pA - pB);
}
#endif
Light
#ifndef CUSTOM_LIGHT_INCLUDE
#define CUSTOM_LIGHT_INCLUDE
#define MAX_DIRECTIONAL_LIGHT_COUNT 4
CBUFFER_START(_CustomLight)
//float3 _DirectionalLightColor;
//float3 _DirectionalLightDirection;
int _DirectionalLightCount;
float4 _DirectionalLightColors[MAX_DIRECTIONAL_LIGHT_COUNT];
float4 _DirectionalLightDirections[MAX_DIRECTIONAL_LIGHT_COUNT];
float4 _DirectionalLightShadowData[MAX_DIRECTIONAL_LIGHT_COUNT];
CBUFFER_END
struct Light
{
float3 color;
float3 direction;
float attenuation;
};
int GetDirectionalLightCount()
{
return _DirectionalLightCount;
}
DirectionalShadowData GetDirectionalShadowData(int lightIndex, ShadowData shadowData)
{
DirectionalShadowData data;
data.strength = _DirectionalLightShadowData[lightIndex].x * shadowData.strength;
data.tileIndex = _DirectionalLightShadowData[lightIndex].y + shadowData.cascadeIndex;
data.normalBias = _DirectionalLightShadowData[lightIndex].z;
return data;
}
Light GetDirectionalLight(int index, Surface surfaceWS, ShadowData shadowData)
{
Light light;
light.color = _DirectionalLightColors[index].rgb;
light.direction = _DirectionalLightDirections[index].xyz;
DirectionalShadowData dirShadowData = GetDirectionalShadowData(index, shadowData);
light.attenuation = GetDirectionalShadowAttenuation(dirShadowData, shadowData, surfaceWS);
//测试:*0.25降低强度
//light.attenuation = shadowData.cascadeIndex * 0.25;
return light;
}
#endif
Lighting
#ifndef CUSTOM_LIGHTING_INCLUDE
#define CUSTOM_LIGHTING_INCLUDE
//计算入射光颜色
float3 IncomingLight(Surface surface, Light light)
{
return saturate(dot(surface.normal, light.direction) * light.attenuation) * light.color;
}
//计算表面和灯光的最终照明
float3 GetLighting(Surface surface, BRDF brdf, Light light)
{
//return IncomingLight(surface, light) * surface.color;
//return IncomingLight(surface, light) * brdf.diffuse;
return IncomingLight(surface, light) * DirectBRDF(surface, brdf, light);
}
//计算表面和灯光颜色
float3 GetLighting(Surface surfaceWS, BRDF brdf)
{
ShadowData shadowData = GetShadowData(surfaceWS);
float3 color = 0.0;
for (int i = 0; i < GetDirectionalLightCount(); i++)
{
Light light = GetDirectionalLight(i, surfaceWS, shadowData);
color += GetLighting(surfaceWS, brdf, light);
}
return color;
}
#endif
Surface
#ifndef CUSTOM_SURFACE_INCLUDE
#define CUSTOM_SURFACE_INCLUDE
struct Surface
{
float3 position;
float3 normal;
float3 viewDirection;
float depth;
float3 color;
float alpha;
float metallic;
float smoothness;
float dither;//抖动值
};
#endif
BRDF
#ifndef CUSTOM_BRDF_INCLUDE
#define CUSTOM_BRDF_INCLUDE
struct BRDF
{
float3 diffuse;
float3 specular;
float roughness;
};
#define MIN_REFLECTIVITY 0.04
float OneMinusReflectivity(float metallic)
{
float range = 1.0 - MIN_REFLECTIVITY;
return range - metallic * range;
}
BRDF GetBRDF(Surface surface,bool applyAlphaToDiffuse=false)
{
BRDF brdf;
float oneMinusReflectivity = OneMinusReflectivity(surface.metallic);
brdf.diffuse = surface.color * oneMinusReflectivity;
if (applyAlphaToDiffuse)
{
brdf.diffuse *= surface.alpha;
}
brdf.specular = lerp(MIN_REFLECTIVITY,surface.color,surface.metallic);
float perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(surface.smoothness);
brdf.roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
return brdf;
}
//高光强度公式:同URP
float SpecularStrength(Surface surface, BRDF brdf, Light light)
{
float3 h = SafeNormalize(light.direction + surface.viewDirection);
float nh2 = Square(saturate(dot(surface.normal, h)));
float lh2 = Square(saturate(dot(light.direction, h)));
float r2 = Square(brdf.roughness);
float d2 = Square(nh2 * (r2 - 1.0) + 1.0001);
float normalization = brdf.roughness * 4.0 + 2.0;
return r2 / (d2 * max(0.1, lh2) * normalization);
}
//直接照明灯颜色:经过高光强度调制的镜面颜色加上漫反射颜色
float3 DirectBRDF(Surface surface, BRDF brdf, Light light)
{
return SpecularStrength(surface, brdf, light) * brdf.specular + brdf.diffuse;
}
#endif
Shadow
//阴影采样
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Shadow/ShadowSamplingTent.hlsl"
#if defined(_DIRECTIONAL_PCF3)
#define DIRECTIONAL_FILTER_SAMPLES 4
#define DIRECTIONAL_FILTER_SETUP SampleShadow_ComputeSamples_Tent_3x3
#elif defined(_DIRECTIONAL_PCF5)
#define DIRECTIONAL_FILTER_SAMPLES 9
#define DIRECTIONAL_FILTER_SETUP SampleShadow_ComputeSamples_Tent_5x5
#elif defined(_DIRECTIONAL_PCF7)
#define DIRECTIONAL_FILTER_SAMPLES 16
#define DIRECTIONAL_FILTER_SETUP SampleShadow_ComputeSamples_Tent_7x7
#endif
#ifndef CUSTOM_SHADOW_INCLUDE
#define CUSTOM_SHADOW_INCLUDE
#define MAX_SHADOW_DIRECTIONAL_LIGHT_COUNT 4
#define MAX_CASCADE_COUNT 4
struct DirectionalShadowData
{
float strength;
int tileIndex;
float normalBias;
};
struct ShadowData
{
int cascadeIndex;
float cascadeBlend;
float strength;
};
TEXTURE2D_SHADOW(_DirectionalShadowAtlas);
#define SHADOW_SAMPLER sampler_linear_clamp_compare
SAMPLER_CMP(SHADOW_SAMPLER);
CBUFFER_START(_CustomShadows)
int _CascadeCount;
float4 _CascadeCullingSpheres[MAX_CASCADE_COUNT];
float4 _CascadeData[MAX_CASCADE_COUNT];
float4x4 _DirectionalShadowMatrices[MAX_SHADOW_DIRECTIONAL_LIGHT_COUNT * MAX_CASCADE_COUNT];
//float _ShadowDistance;
float4 _ShadowDistanceFade;
float4 _ShadowAtlasSize;
CBUFFER_END
float FadedShadowStrength(float distance, float scale, float fade)
{
//淡化阴影边缘公式:具体查看原文:这里采用乘法,传过来的值已经除过,优化
return saturate((1.0 - distance * scale) * fade);
}
ShadowData GetShadowData(Surface surfaceWS)
{
ShadowData data;
data.cascadeBlend = 1.0;
//data.strength = surfaceWS.depth < _ShadowDistance ? 1.0 : 0.0;
data.strength = FadedShadowStrength(surfaceWS.depth, _ShadowDistanceFade.x, _ShadowDistanceFade.y);
int i;
for (i = 0; i < _CascadeCount; i++)
{
float4 sphere = _CascadeCullingSpheres[i];
float distanceSqr = DistanceSquared(surfaceWS.position, sphere.xyz);
if (distanceSqr < sphere.w)
{
float fade = FadedShadowStrength(distanceSqr, _CascadeData[i].x, _ShadowDistanceFade.z);
//最后一个级联阴影渐隐:也是公式,和上面的类似,已经在C#端做过处理
if (i == _CascadeCount - 1)
{
//_CascadeData[i].x = 1.0 / sphere.w
//data.strength *= FadedShadowStrength(distanceSqr, 1.0 / sphere.w, _ShadowDistanceFade.z);
data.strength *= fade;
}
else
{
data.cascadeBlend *= fade;
}
break;
}
}
if (i == _CascadeCount)
{
data.strength = 0.0;
}
#if defined(_CASCADE_BLEND_DITHER)
else if (data.cascadeBlend < surfaceWS.dither)
{
i += 1;
}
#endif
#if !defined(_CASCADE_BLEND_SOFT)
data.cascadeBlend = 1.0;
#endif
data.cascadeIndex = i;
return data;
}
float SampleDirectionalShadowAtlas(float3 positionSTS)
{
return SAMPLE_TEXTURE2D_SHADOW(_DirectionalShadowAtlas, SHADOW_SAMPLER, positionSTS);
}
float FilterDirectionalShadow(float3 positionSTS)
{
#if defined(DIRECTIONAL_FILTER_SETUP)
real weights[DIRECTIONAL_FILTER_SAMPLES];
real2 positions[DIRECTIONAL_FILTER_SAMPLES];
float4 size = _ShadowAtlasSize.yyxx;
DIRECTIONAL_FILTER_SETUP(size, positionSTS.xy, weights, positions);
float shadow = 0;
for (int i = 0; i < DIRECTIONAL_FILTER_SAMPLES; i++)
{
shadow += weights[i] * SampleDirectionalShadowAtlas(float3(positions[i].xy, positionSTS.z));
}
return shadow;
#else
return SampleDirectionalShadowAtlas(positionSTS);
#endif
}
//获取方向光阴影衰减:1表示无衰减,显示原颜色,0代表全衰减,黑色
float GetDirectionalShadowAttenuation(DirectionalShadowData directional, ShadowData global, Surface surfaceWS)
{
#if !defined(_RECEIVE_SHADOWS)
return 1.0;
#endif
//灯光阴影强度<=0,不需要采样,直接返回1
if (directional.strength <= 0.0)
{
return 1.0;
}
float3 normalBias = surfaceWS.normal * (directional.normalBias * _CascadeData[global.cascadeIndex].y);
//获取阴影空间坐标:此处是data.tileIndex
float3 positionSTS = mul(_DirectionalShadowMatrices[directional.tileIndex], float4(surfaceWS.position + normalBias, 1.0)).xyz;
//采样
//float shadow = SampleDirectionalShadowAtlas(positionSTS);
float shadow = FilterDirectionalShadow(positionSTS);
//级联阴影之间混合
if (global.cascadeBlend < 1.0)
{
normalBias = surfaceWS.normal * (directional.normalBias * _CascadeData[global.cascadeIndex + 1].y);
positionSTS = mul(_DirectionalShadowMatrices[directional.tileIndex + 1], float4(surfaceWS.position + normalBias, 1.0)).xyz;
shadow = lerp(FilterDirectionalShadow(positionSTS), shadow, global.cascadeBlend);
}
return lerp(1.0, shadow, directional.strength);
}
#endif
Shader
Unlit
Shader "Custom RP/Unlit"
{
Properties
{
_BaseMap("Texture",2D) = "white"{}
_BaseColor("Color",Color) = (1.0,1.0,1.0,1.0)
_Cutoff("Alpha Cutoff",Range(0.0,1.0)) = 0.5
[Toggle(_CLIPPING)] _Clipping("Alpha Clipping",Float)=0
[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend("Scr Blend",Float) = 1
[Enum(UnityEngine.Rendering.BlendMode)] _DstBlend("Dst Blend",Float) = 0
[Enum(Off,0,On,1)] _ZWrite("Z Write",Float) = 1
}
SubShader
{
Pass
{
Blend[_SrcBlend][_DstBlend]
ZWrite[_ZWrite]
HLSLPROGRAM
#pragma target 3.5
#pragma shader_feature _CLIPPING
#pragma multi_compile_instancing
#pragma vertex UnlitPassVertex
#pragma fragment UnlitPassFragment
#include "UnlitPass.hlsl"
ENDHLSL
}
Pass
{
Tags{"LightMode" = "ShadowCaster"}
ColorMask 0
HLSLPROGRAM
#pragma target 3.5
//#pragma shader_feature _CLIPPING
//在阴影投射Pass定义
#pragma shader_feature _ _SHADOWS_CLIP _SHADOWS_DITHER
#pragma multi_compile_instancing
#pragma vertex ShadowCasterPassVertex
#pragma fragment ShadowCasterPassFragment
#include "ShadowCasterPass.hlsl"
ENDHLSL
}
}
CustomEditor "CustomShaderGUI"
}
Lit
Shader "Custom RP/Lit"
{
Properties
{
_BaseMap("Texture",2D) = "white"{}
_BaseColor("Color",Color) = (0.5,0.5,0.5,1.0)
_Cutoff("Alpha Cutoff",Range(0.0,1.0)) = 0.5
[Toggle(_CLIPPING)] _Clipping("Alpha Clipping",Float)=0
[Toggle(_RECEIVE_SHADOWS)] _ReceiveShadows("Receive Shadows",Float)=1
[KeywordEnum(On,Clip,Dither,Off)] _Shadows("Shadow",Float)=0
_Metallic("Metallic",Range(0,1))=0
_Smoothness("Smoothness",Range(0,1))=0.5
[Toggle(_PREMULTIPLY_ALPHA)] _PremulAlpha("Premultiply Alpha",Float) = 0
[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend("Scr Blend",Float) = 1
[Enum(UnityEngine.Rendering.BlendMode)] _DstBlend("Dst Blend",Float) = 0
[Enum(Off,0,On,1)] _ZWrite("Z Write",Float) = 1
}
SubShader
{
Pass
{
Tags{"LightMode" = "CustomLit"}
Blend[_SrcBlend][_DstBlend]
ZWrite[_ZWrite]
HLSLPROGRAM
#pragma target 3.5
#pragma shader_feature _RECEIVE_SHADOWS
#pragma shader_feature _CLIPPING
#pragma shader_feature _PREMULTIPLY_ALPHA
#pragma multi_compile _ _DIRECTIONAL_PCF3 _DIRECTIONAL_PCF5 _DIRECTIONAL_PCF7
#pragma multi_compile _ _CASCADE_BLEND_SOFT _CASCADE_BLEND_DITHER
#pragma multi_compile_instancing
#pragma vertex LitPassVertex
#pragma fragment LitPassFragment
#include "LitPass.hlsl"
ENDHLSL
}
Pass
{
Tags{"LightMode"="ShadowCaster"}
ColorMask 0
HLSLPROGRAM
#pragma target 3.5
//#pragma shader_feature _CLIPPING
//在阴影投射Pass定义
#pragma shader_feature _ _SHADOWS_CLIP _SHADOWS_DITHER
#pragma multi_compile_instancing
#pragma vertex ShadowCasterPassVertex
#pragma fragment ShadowCasterPassFragment
#include "ShadowCasterPass.hlsl"
ENDHLSL
}
}
CustomEditor "CustomShaderGUI"
}
UnlitPass
#ifndef CUSTOM_UNLIT_PASS_INCLUDE
#define CUSTOM_UNLIT_PASS_INCLUDE
#include "../ShaderLibrary/Common.hlsl"
//srp Batching
//CBUFFER_START(UnityPerMaterial)
// float4 _BaseColor;
//CBUFFER_END
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
//GPU instancing
UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseMap_ST)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
UNITY_DEFINE_INSTANCED_PROP(float,_Cutoff)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)
struct Attributes
{
float3 positionOS:POSITION;
float2 baseUV:TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS:SV_POSITION;
float2 baseUV:VAR_BASE_UV;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
Varyings UnlitPassVertex(Attributes input)
{
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
float3 positionWS = TransformObjectToWorld(input.positionOS);
output.positionCS = TransformWorldToHClip(positionWS);
float4 baseST = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseMap_ST);
output.baseUV = input.baseUV * baseST.xy + baseST.zw;
return output;
}
float4 UnlitPassFragment(Varyings input) :SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.baseUV);
float4 baseColor = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);
float4 base = baseMap * baseColor;
#if defined(_CLIPPING)
clip(base.a - UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Cutoff));
#endif
return base;
}
#endif
LitPass
#ifndef CUSTOM_LIT_PASS_INCLUDE
#define CUSTOM_LIT_PASS_INCLUDE
#include "../ShaderLibrary/Common.hlsl"
#include "../ShaderLibrary/Surface.hlsl"
#include "../ShaderLibrary/Shadow.hlsl"
#include "../ShaderLibrary/Light.hlsl"
#include "../ShaderLibrary/BRDF.hlsl"
#include "../ShaderLibrary/Lighting.hlsl"
//srp Batching
//CBUFFER_START(UnityPerMaterial)
// float4 _BaseColor;
//CBUFFER_END
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
//GPU instancing
UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseMap_ST)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
UNITY_DEFINE_INSTANCED_PROP(float, _Cutoff)
UNITY_DEFINE_INSTANCED_PROP(float, _Metallic)
UNITY_DEFINE_INSTANCED_PROP(float, _Smoothness)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)
struct Attributes
{
float3 positionOS:POSITION;
float3 normalOS:NORMAL;
float2 baseUV:TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS:SV_POSITION;
float3 positionWS:VAR_POSITION;
float3 normalWS:VAR_NORMAL;
float2 baseUV:VAR_BASE_UV;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
Varyings LitPassVertex(Attributes input)
{
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
output.positionWS = TransformObjectToWorld(input.positionOS);
output.positionCS = TransformWorldToHClip(output.positionWS);
output.normalWS = TransformObjectToWorldNormal(input.normalOS);
float4 baseST = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseMap_ST);
output.baseUV = input.baseUV * baseST.xy + baseST.zw;
return output;
}
float4 LitPassFragment(Varyings input) :SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.baseUV);
float4 baseColor = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);
float4 base = baseMap * baseColor;
#if defined(_CLIPPING)
clip(base.a - UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Cutoff));
#endif
//可视化法线
//base.rgb = normalize(input.normalWS);
Surface surface;
surface.position = input.positionWS;
surface.normal = normalize(input.normalWS);
surface.viewDirection = normalize(_WorldSpaceCameraPos - input.positionWS);
surface.depth = -TransformWorldToView(input.positionWS).z;
surface.color = base.rgb;
surface.alpha = base.a;
surface.metallic = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Metallic);
surface.smoothness = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Smoothness);
surface.dither = InterleavedGradientNoise(input.positionCS.xy, 0);
#if defined(_PREMULTIPLY_ALPHA)
BRDF brdf = GetBRDF(surface,true);
#else
BRDF brdf = GetBRDF(surface);
#endif
float3 color = GetLighting(surface,brdf);
return float4(color,surface.alpha);
}
#endif
ShadowCasterPass
#ifndef CUSTOM_SHADOW_CASTER_PASS_INCLUDE
#define CUSTOM_SHADOW_CASTER_PASS_INCLUDE
#include "../ShaderLibrary/Common.hlsl"
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
//GPU instancing
UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseMap_ST)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
UNITY_DEFINE_INSTANCED_PROP(float, _Cutoff)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)
struct Attributes
{
float3 positionOS:POSITION;
float2 baseUV:TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS:SV_POSITION;
float2 baseUV:VAR_BASE_UV;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
Varyings ShadowCasterPassVertex(Attributes input)
{
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
float3 positionWS = TransformObjectToWorld(input.positionOS);
output.positionCS = TransformWorldToHClip(positionWS);
#if UNITY_REVERSED_Z
output.positionCS.z = min(output.positionCS.z, output.positionCS.w * UNITY_NEAR_CLIP_VALUE);
#else
output.positionCS.z = max(output.positionCS.z, output.positionCS.w * UNITY_NEAR_CLIP_VALUE);
#endif
float4 baseST = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseMap_ST);
output.baseUV = input.baseUV * baseST.xy + baseST.zw;
return output;
}
void ShadowCasterPassFragment(Varyings input)
{
UNITY_SETUP_INSTANCE_ID(input);
float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.baseUV);
float4 baseColor = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);
float4 base = baseMap * baseColor;
#if defined(_SHADOWS_CLIP)
clip(base.a - UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Cutoff));
#elif defined(_SHADOWS_DITHER)
float dither = InterleavedGradientNoise(input.positionCS.xy, 0);
clip(base.a - dither);
#endif
}
#endif
自定义ShaderGUI
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
public class CustomShaderGUI : ShaderGUI
{
enum ShadowMode
{
On, Clipped, Dithered, Off
}
MaterialEditor editor;
Object[] materials;
MaterialProperty[] properties;
bool showPresets;
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
EditorGUI.BeginChangeCheck();
base.OnGUI(materialEditor, properties);
editor = materialEditor;
materials = materialEditor.targets;
this.properties = properties;
EditorGUILayout.Space();
showPresets = EditorGUILayout.Foldout(showPresets, "Presets", true);
if (showPresets)
{
OpaquePreset();
ClipPreset();
FadePreset();
TransparentPreset();
}
if (EditorGUI.EndChangeCheck())
{
//设置阴影Pass是否开启
SetShadowCasterPass();
}
}
private bool SetProperty(string name, float value)
{
var property = FindProperty(name, properties, false);
if (property != null)
{
property.floatValue = value;
return true;
}
return false;
}
private void SetKeyword(string keyword, bool enabled)
{
if (enabled)
{
foreach (Material m in materials)
{
m.EnableKeyword(keyword);
}
}
else
{
foreach (Material m in materials)
{
m.DisableKeyword(keyword);
}
}
}
private void SetProperty(string name, string keyword, bool value)
{
if (SetProperty(name, value ? 1f : 0f))
{
SetKeyword(keyword, value);
}
}
bool Clipping
{
set
{
SetProperty("_Clipping", "_CLIPPING", value);
}
}
bool PremultiplyAlpha
{
set
{
SetProperty("_PremulAlpha", "_PREMULTIPLY_ALPHA", value);
}
}
BlendMode SrcBlend
{
set
{
SetProperty("_SrcBlend", (float)value);
}
}
BlendMode DstBlend
{
set
{
SetProperty("_DstBlend", (float)value);
}
}
bool ZWrite
{
set
{
SetProperty("_ZWrite", value ? 1f : 0f);
}
}
RenderQueue RenderQueue
{
set
{
foreach (Material m in materials)
{
m.renderQueue = (int)value;
}
}
}
ShadowMode Shadows
{
set
{
if (SetProperty("_Shadows", (float)value))
{
SetKeyword("_SHADOWS_CLIP", value == ShadowMode.Clipped);
SetKeyword("_SHADOWS_DITHER", value == ShadowMode.Dithered);
}
}
}
bool PresetButton(string name)
{
if (GUILayout.Button(name))
{
editor.RegisterPropertyChangeUndo(name);
return true;
}
return false;
}
void OpaquePreset()
{
if (PresetButton("Opaque"))
{
Clipping = false;
PremultiplyAlpha = false;
SrcBlend = BlendMode.One;
DstBlend = BlendMode.Zero;
ZWrite = true;
RenderQueue = RenderQueue.Geometry;
Shadows = ShadowMode.On;
}
}
void ClipPreset()
{
if (PresetButton("Clip"))
{
Clipping = true;
PremultiplyAlpha = false;
SrcBlend = BlendMode.One;
DstBlend = BlendMode.Zero;
ZWrite = true;
RenderQueue = RenderQueue.AlphaTest;
Shadows = ShadowMode.Clipped;
}
}
void FadePreset()
{
if (PresetButton("Fade"))
{
Clipping = false;
PremultiplyAlpha = false;
SrcBlend = BlendMode.SrcAlpha;
DstBlend = BlendMode.OneMinusSrcAlpha;
ZWrite = false;
RenderQueue = RenderQueue.Transparent;
Shadows = ShadowMode.Dithered;
}
}
void TransparentPreset()
{
if (HasPremultiplyAlpha && PresetButton("Transparent"))
{
Clipping = false;
PremultiplyAlpha = true;
SrcBlend = BlendMode.One;
DstBlend = BlendMode.OneMinusSrcAlpha;
ZWrite = false;
RenderQueue = RenderQueue.Transparent;
Shadows = ShadowMode.Dithered;
}
}
bool HasProperty(string name) => FindProperty(name, properties, false) != null;
bool HasPremultiplyAlpha => HasProperty("_PremulAlpha");
void SetShadowCasterPass()
{
MaterialProperty shadows = FindProperty("_Shadows", properties, false);
if (shadows == null || shadows.hasMixedValue)
{
return;
}
bool enabled = shadows.floatValue < (float)ShadowMode.Off;
foreach (Material material in materials)
{
Debug.Log("启用:" + material.name);
material.SetShaderPassEnabled("ShadowCaster", enabled);
}
}
}
Example里的代码和上节一样