在上一节的基础上添加方向光支持,把需要的数据分类:Surface,BRDF,Light,Lighting。
总结:
1.光照着色器:为什么要在片元阶段归一化法线(顶点插值后的不是归一化的)
2.支持多盏灯计算:采用BRDF、高光反射公式
3.透明材质、自定义ShaderGUI方便切换类型、设置属性和关键字
以下是到这一节的全部代码:原文 源码
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;
protected override RenderPipeline CreatePipeline()
{
return new CustomRenderPipeline(useDynamicBatching, useGPUInstancing, useSRPBatcher);
}
}
CustomRenderPipeline:
using UnityEngine;
using UnityEngine.Rendering;
public class CustomRenderPipeline : RenderPipeline
{
CameraRender renderer = new CameraRender();
bool useDynamicBatching, useGPUInstancing;
private CustomRenderPipeline() { }
public CustomRenderPipeline(bool useDynamicBatching, bool useGPUInstancing, bool useSRPBatcher)
{
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);
}
}
}
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)
{
this.context = context;
this.camera = camera;
PrepareBuffer();
PrepareForSceneWindow();
if (!Cull())
{
return;
}
Setup();
lighting.Setup(context, cullingResults);
DrawVisibleGeometry(useDynamicBatching, useGPUInstancing);
DrawUnsupportedShaders();
DrawGizmos();
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,设置Pass中自定义的LightMode
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()
{
if (camera.TryGetCullingParameters(out ScriptableCullingParameters p))
{
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");
static Vector4[]
dirLightColors = new Vector4[maxDirLightCount],
dirLightDirections = new Vector4[maxDirLightCount];
CullingResults cullingResults;
public void Setup(ScriptableRenderContext context, CullingResults cullingResults)
{
this.cullingResults = cullingResults;
buffer.BeginSample(bufferName);
//SetupDirectionalLight();
SetupLights();
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);
}
}
private void SetupDirectionalLight(int index, ref VisibleLight visibleLight)
{
dirLightColors[index] = visibleLight.finalColor;
dirLightDirections[index] = -visibleLight.localToWorldMatrix.GetColumn(2);
}
}
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;
}
#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];
CBUFFER_END
struct Light
{
float3 color;
float3 direction;
};
int GetDirectionalLightCount()
{
return _DirectionalLightCount;
}
Light GetDirectionalLight(int index)
{
Light light;
light.color = _DirectionalLightColors[index].rgb;
light.direction = _DirectionalLightDirections[index].xyz;
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.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 surface, BRDF brdf)
{
float3 color = 0.0;
for (int i = 0; i < GetDirectionalLightCount(); i++)
{
color += GetLighting(surface, brdf, GetDirectionalLight(i));
}
return color;
}
#endif
Surface
#ifndef CUSTOM_SURFACE_INCLUDE
#define CUSTOM_SURFACE_INCLUDE
struct Surface
{
float3 normal;
float3 viewDirection;
float3 color;
float alpha;
float metallic;
float smoothness;
};
#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
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
}
}
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
_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 _CLIPPING
#pragma shader_feature _PREMULTIPLY_ALPHA
#pragma multi_compile_instancing
#pragma vertex LitPassVertex
#pragma fragment LitPassFragment
#include "LitPass.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/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.normal = normalize(input.normalWS);
surface.viewDirection = normalize(_WorldSpaceCameraPos - input.positionWS);
surface.color = base.rgb;
surface.alpha = base.a;
surface.metallic = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Metallic);
surface.smoothness = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Smoothness);
#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
自定义ShaderGUI
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
public class CustomShaderGUI : ShaderGUI
{
MaterialEditor editor;
Object[] materials;
MaterialProperty[] properties;
bool showPresets;
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
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();
}
}
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;
}
}
}
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;
}
}
void ClipPreset()
{
if (PresetButton("Clip"))
{
Clipping = true;
PremultiplyAlpha = false;
SrcBlend = BlendMode.One;
DstBlend = BlendMode.Zero;
ZWrite = true;
RenderQueue = RenderQueue.AlphaTest;
}
}
void FadePreset()
{
if (PresetButton("Fade"))
{
Clipping = false;
PremultiplyAlpha = false;
SrcBlend = BlendMode.SrcAlpha;
DstBlend = BlendMode.OneMinusSrcAlpha;
ZWrite = false;
RenderQueue = RenderQueue.Transparent;
}
}
void TransparentPreset()
{
if (HasPremultiplyAlpha && PresetButton("Transparent"))
{
Clipping = false;
PremultiplyAlpha = true;
SrcBlend = BlendMode.One;
DstBlend = BlendMode.OneMinusSrcAlpha;
ZWrite = false;
RenderQueue = RenderQueue.Transparent;
}
}
bool HasProperty(string name) => FindProperty(name, properties, false) != null;
bool HasPremultiplyAlpha => HasProperty("_PremulAlpha");
}
Examples
PerObjectMaterialProperties
using UnityEngine;
[DisallowMultipleComponent]
public class PerObjectMaterialProperties : MonoBehaviour
{
static int
baseColorId = Shader.PropertyToID("_BaseColor"),
cutoffId = Shader.PropertyToID("_Cutoff"),
metallicId = Shader.PropertyToID("_Metallic"),
smoothnessId = Shader.PropertyToID("_Smoothness");
static MaterialPropertyBlock block;
[SerializeField]
Color baseColor = Color.white;
[SerializeField, Range(0f, 1f)]
float cutoff = 0.5f, metallic = 0f, smoothness = 0.5f;
private void Awake()
{
OnValidate();
}
private void OnValidate()
{
if (block == null)
{
block = new MaterialPropertyBlock();
}
block.SetColor(baseColorId, baseColor);
block.SetFloat(cutoffId, cutoff);
block.SetFloat(metallicId, metallic);
block.SetFloat(smoothnessId, smoothness);
GetComponent<Renderer>().SetPropertyBlock(block);
}
}
MeshBall
using UnityEngine;
public class MeshBall : MonoBehaviour
{
static int
baseColorId = Shader.PropertyToID("_BaseColor"),
metallicId = Shader.PropertyToID("_Metallic"),
smoothnessId = Shader.PropertyToID("_Smoothness");
[SerializeField]
Mesh mesh = default;
[SerializeField]
Material material = default;
Matrix4x4[] matrices = new Matrix4x4[1023];
Vector4[] baseColors = new Vector4[1023];
float[]
metallic = new float[1023],
smoothness = new float[1023];
MaterialPropertyBlock block;
private void Awake()
{
for (int i = 0; i < matrices.Length; i++)
{
matrices[i] = Matrix4x4.TRS(Random.insideUnitSphere * 10f,
Quaternion.Euler(Random.value * 360f, Random.value * 360f, Random.value * 360f),
Vector3.one * Random.Range(0.5f, 1.5f));
baseColors[i] = new Vector4(Random.value, Random.value, Random.value, Random.Range(0.5f, 1f));
metallic[i] = Random.value < 0.25f ? 1f : 0f;
smoothness[i] = Random.Range(0.05f, 0.95f);
}
}
private void Update()
{
if (block == null)
{
block = new MaterialPropertyBlock();
block.SetVectorArray(baseColorId, baseColors);
block.SetFloatArray(metallicId, metallic);
block.SetFloatArray(smoothnessId, smoothness);
}
Graphics.DrawMeshInstanced(mesh, 0, material, matrices, 1023, block);
}
}