mirror of
https://github.com/DeMuenu/MoonlightVRC.git
synced 2025-12-13 19:33:56 +00:00
Add shadowcaster support to lighting and shaders
Introduces shadowcaster support by adding shadow map index fields to light data, updating PlayerPositionsToShader and LightdataStorage to handle shadow map indices, and extending the shader and its includes to sample shadowcaster planes. Adds ShadowcasterUpdater script and editor preview for updating world-to-local matrices, and updates relevant arrays and property handling throughout the codebase. Also adds a sample plane mesh for shadowcasting.
This commit is contained in:
@@ -17,7 +17,8 @@ public static class PlayerPositionsToShaderPreview
|
||||
public Vector4[] colors;
|
||||
public Vector4[] directions;
|
||||
public float[] types;
|
||||
public int size;
|
||||
public float[] shadowMapIndices;
|
||||
public int size;
|
||||
}
|
||||
|
||||
static PlayerPositionsToShaderPreview()
|
||||
@@ -66,16 +67,17 @@ public static class PlayerPositionsToShaderPreview
|
||||
static void EnsureArrays(PlayerPositionsToShader src, int required)
|
||||
{
|
||||
if (!_cache.TryGetValue(src, out var c) ||
|
||||
c.positions == null || c.colors == null || c.directions == null || c.types == null ||
|
||||
c.positions == null || c.colors == null || c.directions == null || c.types == null || c.shadowMapIndices == null ||
|
||||
c.size != required)
|
||||
{
|
||||
c = new Cache
|
||||
{
|
||||
positions = new Vector4[required],
|
||||
colors = new Vector4[required],
|
||||
directions = new Vector4[required],
|
||||
types = new float[required],
|
||||
size = required
|
||||
positions = new Vector4[required],
|
||||
colors = new Vector4[required],
|
||||
directions = new Vector4[required],
|
||||
types = new float[required],
|
||||
shadowMapIndices = new float[required],
|
||||
size = required
|
||||
};
|
||||
_cache[src] = c;
|
||||
}
|
||||
@@ -87,25 +89,27 @@ public static class PlayerPositionsToShaderPreview
|
||||
EnsureArrays(src, max);
|
||||
|
||||
var c = _cache[src];
|
||||
var positions = c.positions;
|
||||
var colors = c.colors;
|
||||
var directions = c.directions;
|
||||
var types = c.types;
|
||||
var positions = c.positions;
|
||||
var colors = c.colors;
|
||||
var directions = c.directions;
|
||||
var types = c.types;
|
||||
var shadowMapIndices = c.shadowMapIndices;
|
||||
|
||||
// Clear arrays to safe defaults
|
||||
for (int i = 0; i < max; i++)
|
||||
{
|
||||
positions[i] = Vector4.zero;
|
||||
colors[i] = Vector4.zero;
|
||||
directions[i] = Vector4.zero;
|
||||
types[i] = 0f;
|
||||
positions[i] = Vector4.zero;
|
||||
colors[i] = Vector4.zero;
|
||||
directions[i] = Vector4.zero;
|
||||
types[i] = 0f;
|
||||
shadowMapIndices[i] = 0f;
|
||||
}
|
||||
|
||||
// Use the Editor-side function defined on the partial class
|
||||
int count = 0;
|
||||
try
|
||||
{
|
||||
src.Editor_BuildPreview(out positions, out colors, out directions, out types, out count);
|
||||
src.Editor_BuildPreview(out positions, out colors, out directions, out types, out shadowMapIndices, out count);
|
||||
|
||||
// replace cache arrays if sizes changed
|
||||
if (positions.Length != c.size)
|
||||
@@ -113,11 +117,12 @@ public static class PlayerPositionsToShaderPreview
|
||||
|
||||
_cache[src] = new Cache
|
||||
{
|
||||
positions = positions,
|
||||
colors = colors,
|
||||
directions = directions,
|
||||
types = types,
|
||||
size = positions.Length
|
||||
positions = positions,
|
||||
colors = colors,
|
||||
directions = directions,
|
||||
types = types,
|
||||
shadowMapIndices = shadowMapIndices,
|
||||
size = positions.Length
|
||||
};
|
||||
}
|
||||
catch
|
||||
@@ -152,6 +157,12 @@ public static class PlayerPositionsToShaderPreview
|
||||
Shader.SetGlobalFloatArray(id, types);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(src.shadowMapIndexProperty))
|
||||
{
|
||||
int id = Shader.PropertyToID(src.shadowMapIndexProperty);
|
||||
Shader.SetGlobalFloatArray(id, shadowMapIndices);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(src.countProperty))
|
||||
{
|
||||
int id = Shader.PropertyToID(src.countProperty);
|
||||
|
||||
142
EditorPreview/Editor/ShadowcasterUpdaterPreview.cs
Normal file
142
EditorPreview/Editor/ShadowcasterUpdaterPreview.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[InitializeOnLoad]
|
||||
public static class ShadowcasterUpdaterPreview
|
||||
{
|
||||
const double kTickInterval = 0.1; // seconds
|
||||
static double _nextTick;
|
||||
|
||||
// One shared MPB to avoid allocations each frame
|
||||
static readonly MaterialPropertyBlock sMPB = new MaterialPropertyBlock();
|
||||
|
||||
// Cache: component -> property ID, and renderer -> last applied matrix
|
||||
static readonly Dictionary<ShadowcasterUpdater, int> _propId = new Dictionary<ShadowcasterUpdater, int>();
|
||||
static readonly Dictionary<Renderer, Matrix4x4> _lastW2L = new Dictionary<Renderer, Matrix4x4>();
|
||||
static readonly List<Renderer> _toRemove = new List<Renderer>(32);
|
||||
|
||||
static ShadowcasterUpdaterPreview()
|
||||
{
|
||||
EditorApplication.update += Update;
|
||||
EditorApplication.hierarchyChanged += ForceTick;
|
||||
Undo.undoRedoPerformed += ForceTick;
|
||||
Selection.selectionChanged += ForceTick;
|
||||
EditorApplication.playModeStateChanged += _ => ForceTick();
|
||||
}
|
||||
|
||||
public static void ForceTick() => _nextTick = 0;
|
||||
|
||||
static void Update()
|
||||
{
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
if (EditorApplication.isPlayingOrWillChangePlaymode) return;
|
||||
#else
|
||||
if (EditorApplication.isPlaying) return;
|
||||
#endif
|
||||
double now = EditorApplication.timeSinceStartup;
|
||||
if (now < _nextTick) return;
|
||||
_nextTick = now + kTickInterval;
|
||||
|
||||
CleanupNullRenderers();
|
||||
|
||||
var behaviours = FindAllInScene();
|
||||
foreach (var b in behaviours)
|
||||
{
|
||||
if (b == null || !b.isActiveAndEnabled) continue;
|
||||
if (EditorUtility.IsPersistent(b)) continue; // skip assets/prefabs
|
||||
ApplyToBehaviour(b);
|
||||
}
|
||||
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
static ShadowcasterUpdater[] FindAllInScene()
|
||||
{
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
return Object.FindObjectsByType<ShadowcasterUpdater>(FindObjectsInactive.Exclude, FindObjectsSortMode.None);
|
||||
#elif UNITY_2020_1_OR_NEWER
|
||||
return Object.FindObjectsOfType<ShadowcasterUpdater>(true);
|
||||
#else
|
||||
return Resources.FindObjectsOfTypeAll<ShadowcasterUpdater>();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void CleanupNullRenderers()
|
||||
{
|
||||
_toRemove.Clear();
|
||||
foreach (var kv in _lastW2L)
|
||||
if (kv.Key == null) _toRemove.Add(kv.Key);
|
||||
for (int i = 0; i < _toRemove.Count; i++)
|
||||
_lastW2L.Remove(_toRemove[i]);
|
||||
}
|
||||
|
||||
static int GetPropertyId(ShadowcasterUpdater b)
|
||||
{
|
||||
string name = string.IsNullOrEmpty(b.propertyName) ? "_Udon_WorldToLocal" : b.propertyName;
|
||||
|
||||
if (!_propId.TryGetValue(b, out int id))
|
||||
{
|
||||
id = Shader.PropertyToID(name);
|
||||
_propId[b] = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
// If user changed the property name in inspector, refresh the ID
|
||||
int newId = Shader.PropertyToID(name);
|
||||
if (newId != id)
|
||||
{
|
||||
_propId[b] = newId;
|
||||
id = newId;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
static void ApplyToBehaviour(ShadowcasterUpdater b)
|
||||
{
|
||||
var renderers = b.rendererTargets;
|
||||
if (renderers == null || renderers.Length == 0) return;
|
||||
|
||||
int id = GetPropertyId(b);
|
||||
Matrix4x4 w2l = b.transform.worldToLocalMatrix;
|
||||
|
||||
for (int i = 0; i < renderers.Length; i++)
|
||||
{
|
||||
Renderer r = renderers[i];
|
||||
if (r == null) continue;
|
||||
|
||||
if (_lastW2L.TryGetValue(r, out var last) && last == w2l)
|
||||
continue; // nothing changed for this renderer
|
||||
|
||||
r.GetPropertyBlock(sMPB);
|
||||
sMPB.SetMatrix(id, w2l);
|
||||
r.SetPropertyBlock(sMPB);
|
||||
|
||||
_lastW2L[r] = w2l;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CustomEditor(typeof(ShadowcasterUpdater))]
|
||||
public class ShadowcasterUpdaterInspector : Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawDefaultInspector();
|
||||
GUILayout.Space(6);
|
||||
using (new EditorGUI.DisabledScope(true))
|
||||
{
|
||||
EditorGUILayout.LabelField("Edit-Mode Preview", EditorStyles.boldLabel);
|
||||
EditorGUILayout.LabelField("Keeps the matrix property updated in the Scene View.");
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Refresh Now"))
|
||||
{
|
||||
ShadowcasterUpdaterPreview.ForceTick();
|
||||
EditorApplication.QueuePlayerLoopUpdate();
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
11
EditorPreview/Editor/ShadowcasterUpdaterPreview.cs.meta
Normal file
11
EditorPreview/Editor/ShadowcasterUpdaterPreview.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0491ab6ce27c5ba449e98288f0e3d8ed
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user