Merge pull request #5 from DeMuenu/shadow-plane

Shadow plane added
This commit is contained in:
DeMuenu
2025-10-02 16:04:11 +02:00
committed by GitHub
18 changed files with 696 additions and 68 deletions

View File

@@ -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);

View File

@@ -0,0 +1,152 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[InitializeOnLoad]
public static class ShadowcasterUpdaterEditorLoop
{
private const double Interval = 0.1;
private const float Eps = 1e-6f;
private static double _lastUpdateTime;
private static ShadowcasterUpdater[] _cached;
private static readonly MaterialPropertyBlock _mpb = new MaterialPropertyBlock();
static ShadowcasterUpdaterEditorLoop()
{
_lastUpdateTime = EditorApplication.timeSinceStartup;
EditorApplication.update += Update;
EditorApplication.hierarchyChanged += RefreshCache;
AssemblyReloadEvents.afterAssemblyReload += RefreshCache;
RefreshCache();
}
private static void RefreshCache()
{
_cached = FindSceneUpdaters();
}
private static ShadowcasterUpdater[] FindSceneUpdaters()
{
#if UNITY_2020_1_OR_NEWER
return Object.FindObjectsOfType<ShadowcasterUpdater>(true);
#else
// Unity 2019-compatible path: include inactive, filter out assets/prefabs not in a scene
var all = Resources.FindObjectsOfTypeAll(typeof(ShadowcasterUpdater));
var list = new List<ShadowcasterUpdater>(all.Length);
foreach (var o in all)
{
var c = o as ShadowcasterUpdater;
if (c == null) continue;
if (EditorUtility.IsPersistent(c)) continue; // skip assets
if (!c.gameObject.scene.IsValid()) continue;
list.Add(c);
}
return list.ToArray();
#endif
}
private static void Update()
{
if (EditorApplication.isPlayingOrWillChangePlaymode) return;
if (EditorApplication.isCompiling) return;
var now = EditorApplication.timeSinceStartup;
if (now - _lastUpdateTime < Interval) return;
_lastUpdateTime = now;
var arr = _cached;
if (arr == null || arr.Length == 0) return;
for (int i = 0; i < arr.Length; i++)
{
var u = arr[i];
if (u == null) continue;
TryUpdate(u);
}
// Make changes visible in Scene/Game view without wiggling the mouse
SceneView.RepaintAll();
}
private static void TryUpdate(ShadowcasterUpdater u)
{
// If inspector values changed, make sure textures/colors/min brightness are pushed
try
{
u.ApplyTextureData();
}
catch
{
// UdonSharp can be touchy during certain editor states. Ignore and continue.
}
var targets = u.rendererTargets;
if (targets == null || targets.Length == 0) return;
// Match the runtime script's plane definition (0.5 half-size before scaling)
const float quadHalfWidth = 0.5f;
const float quadHalfHeight = 0.5f;
Transform t = u.transform;
// World-space basis from transform
Vector3 Udir = t.rotation * Vector3.right; // local +X
Vector3 Vdir = t.rotation * Vector3.up; // local +Y
// Half extents after non-uniform scaling
float halfW = Mathf.Max(quadHalfWidth * t.lossyScale.x, Eps);
float halfH = Mathf.Max(quadHalfHeight * t.lossyScale.y, Eps);
// Reciprocal axes so dot(r, Uinv/Vinv) -> [-0.5, 0.5]
Vector3 Uinv = Udir / (2.0f * halfW);
Vector3 Vinv = Vdir / (2.0f * halfH);
// Unit normal
Vector3 N = Vector3.Normalize(Vector3.Cross(Udir, Vdir));
int idx = Mathf.Max(0, u.shadowcasterIndex);
string suf = "_" + idx.ToString();
int idShadowTex = Shader.PropertyToID("_Udon_shadowCasterTex" + suf);
int idShadowColor = Shader.PropertyToID("_Udon_shadowCasterColor" + suf);
int idOutsideColor = Shader.PropertyToID("_Udon_OutSideColor" + suf);
int idMinBrightness = Shader.PropertyToID("_Udon_MinBrightnessShadow" + suf);
int idPlaneOrigin = Shader.PropertyToID("_Udon_Plane_Origin_" + idx.ToString());
int idPlaneUinv = Shader.PropertyToID("_Udon_Plane_Uinv_" + idx.ToString());
int idPlaneVinv = Shader.PropertyToID("_Udon_Plane_Vinv_" + idx.ToString());
int idPlaneNormal = Shader.PropertyToID("_Udon_Plane_Normal_" + idx.ToString());
Vector4 origin = new Vector4(t.position.x, t.position.y, t.position.z, 0);
Vector4 uinv4 = new Vector4(Uinv.x, Uinv.y, Uinv.z, 0);
Vector4 vinv4 = new Vector4(Vinv.x, Vinv.y, Vinv.z, 0);
Vector4 n4 = new Vector4(N.x, N.y, N.z, 0);
for (int r = 0; r < targets.Length; r++)
{
var ren = targets[r];
if (ren == null) continue;
ren.GetPropertyBlock(_mpb);
// Also mirror texture/color in case ApplyTextureData couldn't run
if (u.ShadowcasterTexture != null) _mpb.SetTexture(idShadowTex, u.ShadowcasterTexture);
_mpb.SetColor(idShadowColor, u.TextureColor);
_mpb.SetColor(idOutsideColor, u.OutsideColor);
_mpb.SetFloat(idMinBrightness, u.MinBrightness);
// Plane data
_mpb.SetVector(idPlaneOrigin, origin);
_mpb.SetVector(idPlaneUinv, uinv4);
_mpb.SetVector(idPlaneVinv, vinv4);
_mpb.SetVector(idPlaneNormal, n4);
ren.SetPropertyBlock(_mpb);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0491ab6ce27c5ba449e98288f0e3d8ed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -9,44 +9,52 @@ public partial class PlayerPositionsToShader
out Vector4[] colors,
out Vector4[] directions,
out float[] types,
out float[] shadowMapIndices,
out int count)
{
int max = Mathf.Max(1, maxLights);
positions = new Vector4[max];
colors = new Vector4[max];
directions = new Vector4[max];
types = new float[max];
count = 0;
positions = new Vector4[max];
colors = new Vector4[max];
directions = new Vector4[max];
types = new float[max];
shadowMapIndices = new float[max];
count = 0;
// ✅ Avoid Array.Empty<T>(); just guard the loop
if (otherLightSources != null)
if (otherLightSources == null) return;
for (int i = 0; i < otherLightSources.Length && count < max; i++)
{
for (int i = 0; i < otherLightSources.Length && count < max; i++)
{
Transform t = otherLightSources[i];
if (t == null || !t.gameObject.activeInHierarchy) continue;
Transform t = otherLightSources[i];
if (t == null || !t.gameObject.activeInHierarchy) continue;
LightdataStorage data = t.GetComponent<LightdataStorage>();
LightdataStorage data = t.GetComponent<LightdataStorage>();
Vector3 pos = t.position;
float range = (data != null) ? data.range * t.localScale.x : t.localScale.x;
Vector4 col = (data != null) ? data.GetFinalColor() : new Vector4(1f, 1f, 1f, 1f);
float intens = (data != null) ? data.intensity * t.localScale.x : 1f;
float cosHalf = (data != null) ? data.GetCosHalfAngle() : 1f;
int typeId = (data != null) ? data.GetTypeId() : 0;
Vector3 pos = t.position;
float range = (data != null) ? data.range * t.localScale.x : t.localScale.x;
// rgb = color, a = intensity (packed to match runtime/shader)
Vector4 col = (data != null) ? data.GetFinalColor() : new Vector4(1f, 1f, 1f, 1f);
float intensity = (data != null) ? data.intensity * t.localScale.x : 1f;
Quaternion rot = t.rotation;
Vector3 fwd = rot * Vector3.down;
// w = cosHalfAngle (0 for omni)
float cosHalf = (data != null) ? data.GetCosHalfAngle() : 0f;
positions[count] = new Vector4(pos.x, pos.y, pos.z, range);
colors[count] = new Vector4(col.x, col.y, col.z, intens);
directions[count] = new Vector4(fwd.x, fwd.y, fwd.z, data.spotAngleDeg);
types[count] = (float)typeId;
// 0=Omni, 1=Spot, 2=Directional (your custom enum)
int typeId = (data != null) ? data.GetTypeId() : 0;
count++;
}
float shIndex = (data != null) ? data.shadowMapIndex : 0f;
Quaternion rot = t.rotation;
Vector3 fwd = rot * Vector3.down;
positions[count] = new Vector4(pos.x, pos.y, pos.z, range);
colors[count] = new Vector4(col.x, col.y, col.z, intensity);
directions[count] = new Vector4(fwd.x, fwd.y, fwd.z, cosHalf);
types[count] = (float)typeId;
shadowMapIndices[count] = shIndex;
count++;
}
}
}

8
Mesh.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2d573c2258a40a04f9932e8910763931
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

BIN
Mesh/Plane.fbx Normal file

Binary file not shown.

109
Mesh/Plane.fbx.meta Normal file
View File

@@ -0,0 +1,109 @@
fileFormatVersion: 2
guid: 26a14a3ac3d4388458abdbca51f4efc8
ModelImporter:
serializedVersion: 22200
internalIDToNameTable: []
externalObjects: {}
materials:
materialImportMode: 2
materialName: 0
materialSearch: 1
materialLocation: 1
animations:
legacyGenerateAnimations: 4
bakeSimulation: 0
resampleCurves: 1
optimizeGameObjects: 0
removeConstantScaleCurves: 0
motionNodeName:
rigImportErrors:
rigImportWarnings:
animationImportErrors:
animationImportWarnings:
animationRetargetingWarnings:
animationDoRetargetingWarnings: 0
importAnimatedCustomProperties: 0
importConstraints: 0
animationCompression: 1
animationRotationError: 0.5
animationPositionError: 0.5
animationScaleError: 0.5
animationWrapMode: 0
extraExposedTransformPaths: []
extraUserProperties: []
clipAnimations: []
isReadable: 0
meshes:
lODScreenPercentages: []
globalScale: 1
meshCompression: 0
addColliders: 0
useSRGBMaterialColor: 1
sortHierarchyByName: 1
importPhysicalCameras: 1
importVisibility: 1
importBlendShapes: 1
importCameras: 1
importLights: 1
nodeNameCollisionStrategy: 1
fileIdsGeneration: 2
swapUVChannels: 0
generateSecondaryUV: 0
useFileUnits: 1
keepQuads: 0
weldVertices: 1
bakeAxisConversion: 0
preserveHierarchy: 0
skinWeightsMode: 0
maxBonesPerVertex: 4
minBoneWeight: 0.001
optimizeBones: 1
meshOptimizationFlags: -1
indexFormat: 0
secondaryUVAngleDistortion: 8
secondaryUVAreaDistortion: 15.000001
secondaryUVHardAngle: 88
secondaryUVMarginMethod: 1
secondaryUVMinLightmapResolution: 40
secondaryUVMinObjectScale: 1
secondaryUVPackMargin: 4
useFileScale: 1
strictVertexDataChecks: 0
tangentSpace:
normalSmoothAngle: 60
normalImportMode: 0
tangentImportMode: 3
normalCalculationMode: 4
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
blendShapeNormalImportMode: 1
normalSmoothingSource: 0
referencedClips: []
importAnimation: 1
humanDescription:
serializedVersion: 3
human: []
skeleton: []
armTwist: 0.5
foreArmTwist: 0.5
upperLegTwist: 0.5
legTwist: 0.5
armStretch: 0.05
legStretch: 0.05
feetSpacing: 0
globalScale: 1
rootMotionBoneName:
hasTranslationDoF: 0
hasExtraRoot: 0
skeletonHasParents: 1
lastHumanDescriptionAvatarSource: {instanceID: 0}
autoGenerateAvatarMappingIfUnspecified: 1
animationType: 2
humanoidOversampling: 1
avatarSetup: 0
addHumanoidExtraRootOnlyWhenUsingAvatar: 1
importBlendShapeDeformPercent: 1
remapMaterialsIfMaterialImportModeIsNone: 0
additionalBone: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -32,6 +32,10 @@ public class LightdataStorage : UdonSharpBehaviour
[Tooltip("0 = omni (no cone)")]
public float spotAngleDeg = 0f;
[Header("Shadow Settings")]
[Tooltip("0 = no shadows, 1-4 = shadow map index")]
public float shadowMapIndex = 0f; // 0 = no shadows, 1-4 = shadow map index
// Convert to a Vector4 for your shader upload
public Vector4 GetFinalColor()
{

View File

@@ -24,6 +24,9 @@ public partial class PlayerPositionsToShader : UdonSharpBehaviour
public float playerLightIntensity = 5f;
public float remoteLightIntensity = 2f;
[Tooltip("0 = no shadows, 1-4 = shadow map index")]
public float PlayerShadowMapIndex = 0f; // 0 = no shadows, 1-4 = shadow map index
[Header("Shader property names (advanced users)")]
[Tooltip("Vector4 array: xyz = position, w = range")]
@@ -41,6 +44,9 @@ public partial class PlayerPositionsToShader : UdonSharpBehaviour
[Tooltip("float array: light type (1=area, 2=cone, etc)")]
public string typeProperty = "_Udon_LightType";
[Tooltip("float array: shadow map index (0=none, 1-4=shadow map index)")]
public string shadowMapIndexProperty = "_Udon_ShadowMapIndex";
[Header("Max Lights (advanced users)")]
[Tooltip("Hard cap / array size. 80 = default cap")]
public int maxLights = 80;
@@ -57,9 +63,11 @@ public partial class PlayerPositionsToShader : UdonSharpBehaviour
private float[] _TypeArray;
private bool _TypeArray_isDirty = false;
private float[] _ShadowMapArray;
private bool _ShadowMap_isDirty = false;
private VRCPlayerApi[] _players;
private MaterialPropertyBlock _mpb;
public int currentCount { get; private set; }
@@ -70,6 +78,7 @@ public partial class PlayerPositionsToShader : UdonSharpBehaviour
private int UdonID_LightColors;
private int UdonID_LightDirections;
private int UdonID_LightType;
private int UdonID_ShadowMapIndex;
void Start()
{
@@ -79,15 +88,16 @@ public partial class PlayerPositionsToShader : UdonSharpBehaviour
_lightColors = new Vector4[maxLights];
_directions = new Vector4[maxLights];
_TypeArray = new float[maxLights];
_ShadowMapArray = new float[maxLights];
_players = new VRCPlayerApi[maxLights];
_mpb = new MaterialPropertyBlock();
UdonID_PlayerPositions = VRCShader.PropertyToID(positionsProperty);
UdonID_LightCount = VRCShader.PropertyToID(countProperty);
UdonID_LightColors = VRCShader.PropertyToID(colorProperty);
UdonID_LightDirections = VRCShader.PropertyToID(directionsProperty);
UdonID_LightType = VRCShader.PropertyToID(typeProperty);
UdonID_ShadowMapIndex = VRCShader.PropertyToID(shadowMapIndexProperty);
UpdateData();
@@ -150,6 +160,11 @@ public partial class PlayerPositionsToShader : UdonSharpBehaviour
_TypeArray[i] = 0f;
_TypeArray_isDirty = true;
}
if (_ShadowMapArray[i] != PlayerShadowMapIndex)
{
_ShadowMapArray[i] = PlayerShadowMapIndex;
_ShadowMap_isDirty = true;
}
}
@@ -175,6 +190,11 @@ public partial class PlayerPositionsToShader : UdonSharpBehaviour
_TypeArray[i] = 0f;
_TypeArray_isDirty = true;
}
if (_ShadowMapArray[i] != 0f)
{
_ShadowMapArray[i] = 0f;
_ShadowMap_isDirty = true;
}
}
}
@@ -230,6 +250,13 @@ public partial class PlayerPositionsToShader : UdonSharpBehaviour
_TypeArray_isDirty = true;
}
float shadowMapIndex = (data != null) ? data.shadowMapIndex : 0f;
if (_ShadowMapArray[currentCount] != shadowMapIndex)
{
_ShadowMapArray[currentCount] = shadowMapIndex;
_ShadowMap_isDirty = true;
}
currentCount++;
}
}
@@ -259,6 +286,12 @@ public partial class PlayerPositionsToShader : UdonSharpBehaviour
_TypeArray[i] = 0f;
_TypeArray_isDirty = true;
}
if (_ShadowMapArray[i] != 0f)
{
_ShadowMapArray[i] = 0f;
_ShadowMap_isDirty = true;
}
}
}
@@ -267,23 +300,26 @@ public partial class PlayerPositionsToShader : UdonSharpBehaviour
// Snapshot which things are dirty this frame
bool pushPositions = _positons_isDirty;
bool pushColors = _lightColors_isDirty;
bool pushDirs = _directions_isDirty;
bool pushTypes = _TypeArray_isDirty && !string.IsNullOrEmpty(typeProperty);
bool pushColors = _lightColors_isDirty;
bool pushDirs = _directions_isDirty;
bool pushTypes = _TypeArray_isDirty && !string.IsNullOrEmpty(typeProperty);
bool pushShadowMap = _ShadowMap_isDirty;
if (pushPositions) VRCShader.SetGlobalVectorArray(UdonID_PlayerPositions, _positions);
if (pushColors) VRCShader.SetGlobalVectorArray(UdonID_LightColors, _lightColors);
if (pushDirs) VRCShader.SetGlobalVectorArray(UdonID_LightDirections, _directions);
if (pushTypes) _mpb.SetFloatArray(UdonID_LightType, _TypeArray);
if (pushColors) VRCShader.SetGlobalVectorArray(UdonID_LightColors, _lightColors);
if (pushDirs) VRCShader.SetGlobalVectorArray(UdonID_LightDirections, _directions);
if (pushTypes) VRCShader.SetGlobalFloatArray(UdonID_LightType, _TypeArray);
if (pushShadowMap) VRCShader.SetGlobalFloatArray(UdonID_ShadowMapIndex, _ShadowMapArray);
VRCShader.SetGlobalFloat(UdonID_LightCount, currentCount);
Debug.Log($"[MoonlightVRC] Pushed {currentCount} lights to shader.");
// Only now mark them clean
if (pushPositions) { _positons_isDirty = false;}
if (pushColors) { _lightColors_isDirty = false;}
if (pushDirs) { _directions_isDirty = false;}
if (pushTypes) { _TypeArray_isDirty = false;}
if (pushPositions) { _positons_isDirty = false; }
if (pushColors) { _lightColors_isDirty = false; }
if (pushDirs) { _directions_isDirty = false; }
if (pushTypes) { _TypeArray_isDirty = false; }
if (pushShadowMap) { _ShadowMap_isDirty = false; }
}
}

View File

@@ -0,0 +1,76 @@

using System.Security.Permissions;
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class ShadowcasterUpdater : UdonSharpBehaviour
{
public Renderer[] rendererTargets;
public Texture2D ShadowcasterTexture;
public Color OutsideColor = Color.white;
public Color TextureColor = Color.white;
public float MinBrightness = 0.0f;
public int shadowcasterIndex = 1;
private MaterialPropertyBlock _mpb;
void Start()
{
_mpb = new MaterialPropertyBlock();
ApplyTextureData();
}
public void ApplyTextureData()
{
foreach (Renderer mat in rendererTargets)
{
if (mat == null) continue;
mat.GetPropertyBlock(_mpb);
_mpb.SetTexture("_Udon_shadowCasterTex" + "_" + shadowcasterIndex.ToString(), ShadowcasterTexture);
_mpb.SetColor("_Udon_shadowCasterColor" + "_" + shadowcasterIndex.ToString(), TextureColor);
_mpb.SetColor("_Udon_OutSideColor" + "_" + shadowcasterIndex.ToString(), OutsideColor);
_mpb.SetFloat("_Udon_MinBrightnessShadow" + "_" + shadowcasterIndex.ToString(), MinBrightness);
mat.SetPropertyBlock(_mpb);
}
}
void LateUpdate()
{
foreach (Renderer mat in rendererTargets)
{
if (mat == null) continue;
mat.GetPropertyBlock(_mpb);
float quadHalfWidth = 0.5f;
float quadHalfHeight = 0.5f;
// World-space basis directions from transform
Vector3 Udir = transform.rotation * Vector3.right; // plane local +X
Vector3 Vdir = transform.rotation * Vector3.up; // plane local +Y
// World-space half extents after non-uniform scaling
float halfW = quadHalfWidth * transform.lossyScale.x;
float halfH = quadHalfHeight * transform.lossyScale.y;
// Reciprocal axes so dot(r, Uinv/Vinv) -> [-0.5, 0.5]
Vector3 Uinv = Udir / (2.0f * Mathf.Max(halfW, 1e-6f));
Vector3 Vinv = Vdir / (2.0f * Mathf.Max(halfH, 1e-6f));
// Unit normal
Vector3 N = Vector3.Normalize(Vector3.Cross(Udir, Vdir));
_mpb.SetVector("_Udon_Plane_Origin_" + shadowcasterIndex.ToString(), new Vector4(transform.position.x, transform.position.y, transform.position.z, 0));
_mpb.SetVector("_Udon_Plane_Uinv_" + shadowcasterIndex.ToString(), new Vector4(Uinv.x, Uinv.y, Uinv.z, 0));
_mpb.SetVector("_Udon_Plane_Vinv_" + shadowcasterIndex.ToString(), new Vector4(Vinv.x, Vinv.y, Vinv.z, 0));
_mpb.SetVector("_Udon_Plane_Normal_" + shadowcasterIndex.ToString(), new Vector4(N.x, N.y, N.z, 0));
mat.SetPropertyBlock(_mpb);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 36c63f8382c2aad48b73fe4628db0f38
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -15,8 +15,13 @@ Shader "DeMuenu/World/Hoppou/RevealStandart"
//Moonlight
_InverseSqareMultiplier ("Inverse Square Multiplier", Float) = 1
_LightCutoffDistance ("Light Cutoff Distance", Float) = 100
_EnableShadowCasting ("Enable Shadowcasting", Float) = 0
_BlurPixels ("Shadowcaster Blur Pixels", Float) = 0
//Moonlight END
[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull Mode", Float) = 2
}
@@ -24,6 +29,7 @@ Shader "DeMuenu/World/Hoppou/RevealStandart"
{
Tags { "RenderType"="Opaque" }
LOD 100
Cull[_Cull]
Pass
{
@@ -36,6 +42,7 @@ Shader "DeMuenu/World/Hoppou/RevealStandart"
#include "Includes/Lambert.hlsl"
#include "Includes/DefaultSetup.hlsl"
#include "Includes/Variables.hlsl"
#include "Includes/Shadowcaster.cginc"
//Moonlight Defines
#define MAX_LIGHTS 80 // >= maxPlayers in script
@@ -85,6 +92,32 @@ Shader "DeMuenu/World/Hoppou/RevealStandart"
MoonlightGlobalVariables
float4 _Udon_Plane_Origin_1; // xyz = origin (world), w unused
float4 _Udon_Plane_Uinv_1; // xyz = Udir / (2*halfWidth)
float4 _Udon_Plane_Vinv_1; // xyz = Vdir / (2*halfHeight)
float4 _Udon_Plane_Normal_1; // xyz = unit normal
sampler2D _Udon_shadowCasterTex_1;
float4 _Udon_shadowCasterColor_1;
float4 _Udon_OutSideColor_1;
float _Udon_MinBrightnessShadow_1;
float4 _Udon_Plane_Origin_2;
float4 _Udon_Plane_Uinv_2;
float4 _Udon_Plane_Vinv_2;
float4 _Udon_Plane_Normal_2;
sampler2D _Udon_shadowCasterTex_2;
float4 _Udon_shadowCasterColor_2;
float4 _Udon_OutSideColor_2;
float _Udon_MinBrightnessShadow_2;
float _BlurPixels;
float4 _Udon_shadowCasterTex_1_TexelSize; // xy = 1/width, 1/height
float4 _Udon_shadowCasterTex_2_TexelSize;
bool _EnableShadowCasting;
v2f vert (appdata v)
{
v2f o;
@@ -139,8 +172,28 @@ Shader "DeMuenu/World/Hoppou/RevealStandart"
Lambert(_Udon_LightPositions[LightCounter].xyz ,i, N); //defines NdotL
LightTypeCalculations(_Udon_LightColors, LightCounter, i, NdotL, dIntensity, _Udon_LightPositions[LightCounter].a, _Udon_LightPositions[LightCounter].xyz);
float4 ShadowCasterMult_1 = 1;
float4 ShadowCasterMult_2 = 1;
dmax = dmax + contrib * float4(LightColor, 1) * NdotL; // accumulate light contributions
if (((_Udon_ShadowMapIndex[LightCounter] > 0.5) && (_Udon_ShadowMapIndex[LightCounter] < 1.5) && (_EnableShadowCasting > 0.5)) || (_Udon_ShadowMapIndex[LightCounter] > 2.5 && _EnableShadowCasting))
{
float4 sc1 = SampleShadowcasterPlaneWS_Basis(
_Udon_LightPositions[LightCounter].xyz, i.worldPos,
_Udon_Plane_Origin_1.xyz, _Udon_Plane_Uinv_1.xyz, _Udon_Plane_Vinv_1.xyz, _Udon_Plane_Normal_1.xyz,
_Udon_shadowCasterTex_1, _Udon_OutSideColor_1, _Udon_shadowCasterColor_1, _BlurPixels, _Udon_shadowCasterTex_1_TexelSize.xy);
ShadowCasterMult_1 = max(sc1, _Udon_MinBrightnessShadow_1);
}
if (_Udon_ShadowMapIndex[LightCounter] > 1.5 && (_EnableShadowCasting > 0.5))
{
float4 sc2 = SampleShadowcasterPlaneWS_Basis(
_Udon_LightPositions[LightCounter].xyz, i.worldPos,
_Udon_Plane_Origin_2.xyz, _Udon_Plane_Uinv_2.xyz, _Udon_Plane_Vinv_2.xyz, _Udon_Plane_Normal_2.xyz,
_Udon_shadowCasterTex_2, _Udon_OutSideColor_2, _Udon_shadowCasterColor_2, _BlurPixels, _Udon_shadowCasterTex_2_TexelSize.xy);
ShadowCasterMult_2 = max(sc2, _Udon_MinBrightnessShadow_2);
}
dmax = dmax + contrib * float4(LightColor, 1) * NdotL * ShadowCasterMult_1 * ShadowCasterMult_2;
}

View File

@@ -6,7 +6,7 @@
{ \
contrib = _Udon_LightColors[LightCounter].a / max(1e-4, max(0, max(1, distanceFromLight - radius) * invSqMul) * max(0, max(1, distanceFromLight - radius) * invSqMul)); \
\
dIntensity += contrib * NdotL; \
dIntensity += contrib; \
} \
else if (_Udon_LightType[LightCounter] == 1) \
{ \
@@ -17,7 +17,7 @@
contrib= 1 - step(threshold, contrib); \
\
contrib = contrib * invSq; \
dIntensity += contrib * NdotL; \
dIntensity += contrib; \
} \
float3 LightColor = _Udon_LightColors[LightCounter].xyz; \

View File

@@ -0,0 +1,46 @@
#ifndef SHADOWCASTER_PLANE
#define SHADOWCASTER_PLANE
#include "UnityCG.cginc" // tex2D, tex2Dgrad
static const float WS_EPS = 1e-5;
inline float4 SampleShadowcasterPlaneWS_Basis(
float3 A, float3 B,
float3 P0, float3 Uinv, float3 Vinv, float3 N,
sampler2D tex, float4 OutsideColor, float4 ShadowColor,
float blurPixels, float2 texelSize)
{
float3 d = B - A;
float dn = dot(N, d);
if (abs(dn) < WS_EPS) return OutsideColor;
float t = dot(N, P0 - A) / dn;
if (t < 0.0 || t > 1.0) return OutsideColor;
float3 hit = A + d * t;
float3 r = hit - P0;
// u,v in [-0.5, 0.5] if inside quad
float u = dot(r, Uinv);
float v = dot(r, Vinv);
if (abs(u) > 0.5 || abs(v) > 0.5) return OutsideColor;
float2 uv = float2(u + 0.5, v + 0.5);
// If blur is tiny, do the normal one-tap
if (blurPixels <= 0.001)
{
float4 col = tex2D(tex, uv) * ShadowColor;
return float4(col.rgb * (1 - col.a), 1);
}
// Inflate gradients so the sampler picks a higher mip (cheap blur).
float2 g = texelSize * blurPixels;
float4 blurred = tex2Dgrad(tex, uv, float2(g.x, 0), float2(0, g.y));
float4 outCol = blurred * ShadowColor;
return float4(outCol.rgb * (1 - outCol.a), 1);
}
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 407fd8c92bce1a84ab69e3abad2320b0
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -8,6 +8,7 @@
float4 _Udon_LightColors[MAX_LIGHTS]; /* xyz = position */ \
float4 _Udon_LightDirections[MAX_LIGHTS]; /* xyz = direction, w = cos(halfAngle) */ \
float _Udon_LightType[MAX_LIGHTS]; /* 0 = sphere, 1 = cone */ \
float _Udon_ShadowMapIndex[MAX_LIGHTS];\
float _Udon_PlayerCount; /* set via SetFloat */ \
#endif

View File

@@ -9,6 +9,9 @@ Shader "DeMuenu/World/Hoppou/Particles/LitParticles"
//Moonlight
_InverseSqareMultiplier ("Inverse Square Multiplier", Float) = 1
_LightCutoffDistance ("Light Cutoff Distance", Float) = 100
_EnableShadowCasting ("Enable Shadowcasting", Float) = 0
_BlurPixels ("Shadowcaster Blur Pixels", Float) = 0
//Moonlight END
@@ -36,6 +39,7 @@ Shader "DeMuenu/World/Hoppou/Particles/LitParticles"
#include "Includes/Lambert.hlsl"
#include "Includes/DefaultSetup.hlsl"
#include "Includes/Variables.hlsl"
#include "Includes/Shadowcaster.cginc"
@@ -78,6 +82,31 @@ Shader "DeMuenu/World/Hoppou/Particles/LitParticles"
MoonlightGlobalVariables
float4 _Udon_Plane_Origin_1; // xyz = origin (world), w unused
float4 _Udon_Plane_Uinv_1; // xyz = Udir / (2*halfWidth)
float4 _Udon_Plane_Vinv_1; // xyz = Vdir / (2*halfHeight)
float4 _Udon_Plane_Normal_1; // xyz = unit normal
sampler2D _Udon_shadowCasterTex_1;
float4 _Udon_shadowCasterColor_1;
float4 _Udon_OutSideColor_1;
float _Udon_MinBrightnessShadow_1;
float4 _Udon_Plane_Origin_2;
float4 _Udon_Plane_Uinv_2;
float4 _Udon_Plane_Vinv_2;
float4 _Udon_Plane_Normal_2;
sampler2D _Udon_shadowCasterTex_2;
float4 _Udon_shadowCasterColor_2;
float4 _Udon_OutSideColor_2;
float _Udon_MinBrightnessShadow_2;
float _BlurPixels;
float4 _Udon_shadowCasterTex_1_TexelSize; // xy = 1/width, 1/height
float4 _Udon_shadowCasterTex_2_TexelSize;
bool _EnableShadowCasting;
v2f vert (appdata v)
{
@@ -114,11 +143,29 @@ Shader "DeMuenu/World/Hoppou/Particles/LitParticles"
InLoopSetup(_Udon_LightPositions, LightCounter, count, i); //defines distanceFromLight, contrib
Lambert(_Udon_LightPositions[LightCounter].xyz ,i, N); //defines NdotL
LightTypeCalculations(_Udon_LightColors, LightCounter, i, NdotL, dIntensity, _Udon_LightPositions[LightCounter].a, _Udon_LightPositions[LightCounter].xyz);
LightTypeCalculations(_Udon_LightColors, LightCounter, i, 1, dIntensity, _Udon_LightPositions[LightCounter].a, _Udon_LightPositions[LightCounter].xyz);
dmax = dmax + contrib * float4(LightColor, 1) * NdotL; // accumulate light contributions
float4 ShadowCasterMult_1 = 1;
float4 ShadowCasterMult_2 = 1;
if (((_Udon_ShadowMapIndex[LightCounter] > 0.5) && (_Udon_ShadowMapIndex[LightCounter] < 1.5) && (_EnableShadowCasting > 0.5)) || (_Udon_ShadowMapIndex[LightCounter] > 2.5 && _EnableShadowCasting))
{
float4 sc1 = SampleShadowcasterPlaneWS_Basis(
_Udon_LightPositions[LightCounter].xyz, i.worldPos,
_Udon_Plane_Origin_1.xyz, _Udon_Plane_Uinv_1.xyz, _Udon_Plane_Vinv_1.xyz, _Udon_Plane_Normal_1.xyz,
_Udon_shadowCasterTex_1, _Udon_OutSideColor_1, _Udon_shadowCasterColor_1, _BlurPixels, _Udon_shadowCasterTex_1_TexelSize.xy);
ShadowCasterMult_1 = max(sc1, _Udon_MinBrightnessShadow_1);
}
if (_Udon_ShadowMapIndex[LightCounter] > 1.5 && (_EnableShadowCasting > 0.5)) {
float4 sc2 = SampleShadowcasterPlaneWS_Basis(
_Udon_LightPositions[LightCounter].xyz, i.worldPos,
_Udon_Plane_Origin_2.xyz, _Udon_Plane_Uinv_2.xyz, _Udon_Plane_Vinv_2.xyz, _Udon_Plane_Normal_2.xyz,
_Udon_shadowCasterTex_2, _Udon_OutSideColor_2, _Udon_shadowCasterColor_2, _BlurPixels, _Udon_shadowCasterTex_2_TexelSize.xy);
ShadowCasterMult_2 = max(sc2, _Udon_MinBrightnessShadow_2);
}
dmax = dmax + contrib * float4(LightColor, 1) * ShadowCasterMult_1 * ShadowCasterMult_2;
}

View File

@@ -17,6 +17,10 @@ Shader "DeMuenu/World/Hoppou/Water"
_InverseSqareMultiplier ("Inverse Square Multiplier", Float) = 1
_LightCutoffDistance ("Light Cutoff Distance", Float) = 100
_EnableShadowCasting ("Enable Shadowcasting", Float) = 0
_BlurPixels ("Shadowcaster Blur Pixels", Float) = 0
//Moonlight END
_SpecPower ("Spec Power", Range(4,256)) = 64
_SpecIntensity ("Spec Intensity", Range(0,10)) = 1
_AmbientFloor ("Ambient Floor", Range(0,1)) = 0.08
@@ -24,7 +28,6 @@ Shader "DeMuenu/World/Hoppou/Water"
_F0 ("F0", Range(0,1)) = 0.02
_FresnelPower ("Fresnel Power", Range(1,8)) = 5
_ReflectionStrength ("Reflection Strength", Range(0,1)) = 0.7
//Moonlight END
_WaveInput ("Wave Input", 2D) = "black" {}
_CameraScale ("Camera Scale", Float) = 15
@@ -51,6 +54,7 @@ Shader "DeMuenu/World/Hoppou/Water"
#include "Includes/Lambert.hlsl"
#include "Includes/DefaultSetup.hlsl"
#include "Includes/Variables.hlsl"
#include "Includes/Shadowcaster.cginc"
//Moonlight Defines
#define MAX_LIGHTS 80 // >= maxPlayers in script
@@ -94,6 +98,33 @@ Shader "DeMuenu/World/Hoppou/Water"
MoonlightGlobalVariables
float4 _Udon_Plane_Origin_1; // xyz = origin (world), w unused
float4 _Udon_Plane_Uinv_1; // xyz = Udir / (2*halfWidth)
float4 _Udon_Plane_Vinv_1; // xyz = Vdir / (2*halfHeight)
float4 _Udon_Plane_Normal_1; // xyz = unit normal
sampler2D _Udon_shadowCasterTex_1;
float4 _Udon_shadowCasterColor_1;
float4 _Udon_OutSideColor_1;
float _Udon_MinBrightnessShadow_1;
float4 _Udon_Plane_Origin_2;
float4 _Udon_Plane_Uinv_2;
float4 _Udon_Plane_Vinv_2;
float4 _Udon_Plane_Normal_2;
sampler2D _Udon_shadowCasterTex_2;
float4 _Udon_shadowCasterColor_2;
float4 _Udon_OutSideColor_2;
float _Udon_MinBrightnessShadow_2;
float _BlurPixels;
float4 _Udon_shadowCasterTex_1_TexelSize; // xy = 1/width, 1/height
float4 _Udon_shadowCasterTex_2_TexelSize;
bool _EnableShadowCasting;
//Watershader specific
float _SpecPower, _SpecIntensity;
float3 _AmbientFloor;
@@ -185,13 +216,30 @@ Shader "DeMuenu/World/Hoppou/Water"
LightTypeCalculations(_Udon_LightColors, LightCounter, i, 1, dIntensity, _Udon_LightPositions[LightCounter].a, _Udon_LightPositions[LightCounter].xyz);
float4 ShadowCasterMult_1 = 1;
float4 ShadowCasterMult_2 = 1;
if (((_Udon_ShadowMapIndex[LightCounter] > 0.5) && (_Udon_ShadowMapIndex[LightCounter] < 1.5) && (_EnableShadowCasting > 0.5)) || (_Udon_ShadowMapIndex[LightCounter] > 2.5 && _EnableShadowCasting))
{
float4 sc1 = SampleShadowcasterPlaneWS_Basis(
_Udon_LightPositions[LightCounter].xyz, i.worldPos,
_Udon_Plane_Origin_1.xyz, _Udon_Plane_Uinv_1.xyz, _Udon_Plane_Vinv_1.xyz, _Udon_Plane_Normal_1.xyz,
_Udon_shadowCasterTex_1, _Udon_OutSideColor_1, _Udon_shadowCasterColor_1, _BlurPixels, _Udon_shadowCasterTex_1_TexelSize.xy);
ShadowCasterMult_1 = max(sc1, _Udon_MinBrightnessShadow_1);
}
if (_Udon_ShadowMapIndex[LightCounter] > 1.5 && (_EnableShadowCasting > 0.5)) {
float4 sc2 = SampleShadowcasterPlaneWS_Basis(
_Udon_LightPositions[LightCounter].xyz, i.worldPos,
_Udon_Plane_Origin_2.xyz, _Udon_Plane_Uinv_2.xyz, _Udon_Plane_Vinv_2.xyz, _Udon_Plane_Normal_2.xyz,
_Udon_shadowCasterTex_2, _Udon_OutSideColor_2, _Udon_shadowCasterColor_2, _BlurPixels, _Udon_shadowCasterTex_2_TexelSize.xy);
ShadowCasterMult_2 = max(sc2, _Udon_MinBrightnessShadow_2);
}
//Watershader specific
//float fres = Schlick(saturate(dot(N, V)), _F0, _FresnelPower);
float spec = pow(saturate(dot(R, L)), _SpecPower);
//return float4(spec, spec, spec,1);
dmax.rgb += _Udon_LightColors[LightCounter].rgb * contrib + _Udon_LightColors[LightCounter].rgb * _SpecIntensity * spec * contrib;
dmax.rgb += _Udon_LightColors[LightCounter].rgb * contrib * ShadowCasterMult_1 * ShadowCasterMult_2 + _Udon_LightColors[LightCounter].rgb * _SpecIntensity * spec * contrib * ShadowCasterMult_1 * ShadowCasterMult_2;
dmax.a -= _SpecIntensity * spec;
//dmax = dmax + contrib * float4(LightColor, 1); // accumulate light contributions