Merge branch 'main' into shadow-plane

This commit is contained in:
DeMuenu
2025-10-01 02:16:22 +02:00
2 changed files with 37 additions and 10 deletions

View File

@@ -2,7 +2,7 @@
## Idea
When creating a World for VRChat that reveals items around the player as they walk up to them, I stumbled across the problem that Quest doesn't handle realtime lights well. As a result, I may have ended up spending tens of hours coding my own light system.
When creating a World for VRChat that reveals items around the player as they walk up to them, I stumbled across the problem that Quest doesn't handle real-time lights well. As a result, I may have ended up spending tens of hours coding my own light system.
What this includes:
- Point/spotlights editable at runtime.
@@ -13,16 +13,16 @@ Work in progress:
- Water shader
- Documentation
- More performance testing/improvements
- Shadow caster planes
Planned:
- Basic shadows via a shadow emitter map. The plan is to only sample the highest points of a map area and then calculate if the light ray is intersecting the object. This should provide basic shadow casting that is much more performant than raycasting.
- Support for addative baked light maps and ambient lighting.
- Support for additive baked light maps and ambient lighting in the standard shader.
---
## Performance
Early testing showed the Quest 3 dropping to around 30 FPS when having 100 spotlights active in a scene at once. This test was conducted with 5 material targets. Since then the shader has grown in size significantly and I have also included some optimizations in the code transferring light data to the objects. Reading the light data and transferring it to the shader seems to be a major bottleneck at the moment but there is room for improvements.
Early testing showed the Quest 3 dropping to around 30 FPS when having 100 spotlights active in a scene at once. This test was conducted with 5 material targets. Since then the shader has grown in size significantly there has been optimisation
On PC, I haven't encountered any frame drops in the editor at all, even with 400 concurrent lights.
@@ -31,19 +31,20 @@ On PC, I haven't encountered any frame drops in the editor at all, even with 400
1. Clone the code into your project.
2. Add the `PlayerPositionsToShader` component to a GameObject in your scene:
- Inspect the script in the inspector and assign `targets` (Objects that use a compatible shader) and optional `otherLightSources` (Transforms as described in step 3).
- Tweak strength/intensity of the local and remote player if you want them to have an attached light.
3. For lights, attach `LightdataStorage` to a Transform and configure:
- `range`, `type`, `color`, `intensity`, and `spotAngleDeg`.
4. Use one of the premade shaders on your objects. Or if you feel like it, use the provided .hlsl in your own shader. You just need to copy everything surrounded by Moonlight comments, and applying it at the end of your shader.
4. Add the light transform to your `PlayerPositionsToShader` component's `otherLightSources` array.
5. Use one of the premade shaders on your material. Or, if you feel like it, use the provided .hlsl/.cginc in your own shader. You just need to copy everything surrounded by Moonlight comments, and apply it at the end of your shader.
---
## Editor preview
- While not in Play mode, the editor helper `PlayerPositionsToShaderPreview` (EditorPreview/Editor/PlayerPositionsToShaderPreview.cs) writes the same property blocks to assigned Renderers so you can preview emissive/lighting effects in the Scene view. Those update 10 times a second.
- While not in play mode, the editor helper `PlayerPositionsToShaderPreview` (EditorPreview/Editor/PlayerPositionsToShaderPreview.cs) and `ShadowcasterUpdaterPreview` (EditorPreview/Editor/ShadowcasterUpdaterPreview.cs) write the same property blocks to assigned Renderers so you can preview lighting effects in the Scene view. Those update 10 times a second.
- The editor partial helper for building preview arrays is in `EditorPreview/PlayerPositionsToShader.Editor.cs`.
---
@@ -57,3 +58,4 @@ On PC, I haven't encountered any frame drops in the editor at all, even with 400
## Contributing
If you want to help with development, please contact me on Discord (@demuenu) so we can coordinate our efforts.
If your somebody with a education in Computer graphics, I would be even more thankfull for your help. As right now, it's just me with ~1.5 years of messing around in Shaderlab and ChatGPT as my advisor. So I'm sure that there are serious flaws in the codebase :-)

View File

@@ -61,6 +61,7 @@ Shader "DeMuenu/World/Hoppou/Water"
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
float4 tangent: TANGENT;
};
struct v2f
@@ -73,6 +74,9 @@ Shader "DeMuenu/World/Hoppou/Water"
float3 worldPos : TEXCOORD2;
float3 worldNormal: TEXCOORD3;
//Moonlight END
float3 worldTangent : TEXCOORD4;
float3 worldBitangent : TEXCOORD5;
};
sampler2D _MainTex;
@@ -125,6 +129,12 @@ Shader "DeMuenu/World/Hoppou/Water"
o.worldPos = wp.xyz;
o.worldNormal = UnityObjectToWorldNormal(v.normal);
//Moonlight Vertex END
float3 tWS = normalize(UnityObjectToWorldDir(v.tangent.xyz));
float3 bWS = normalize(cross(o.worldNormal, tWS) * v.tangent.w);
o.worldTangent = tWS;
o.worldBitangent= bWS;
return o;
}
@@ -140,7 +150,23 @@ Shader "DeMuenu/World/Hoppou/Water"
//Moonlight
float3 N = normalize(i.worldNormal + NormalOffset1 * _NormalMapStrength1 + NormalOffset2 * _NormalMapStrength2); //for lambertian diffuse
half3 n1 = UnpackNormal(norm);
half3 n2 = UnpackNormal(norm2);
n1.xy *= _NormalMapStrength1;
n2.xy *= _NormalMapStrength2;
n1 = normalize(n1);
n2 = normalize(n2);
// combine two tangent-space normals (whiteout mix; good enough for water)
half3 nTS = normalize(half3(n1.xy + n2.xy, n1.z * n2.z));
// rotate TS -> WS with the TBN (linear combo is cheaper than a matrix mul)
half3 N = normalize(
i.worldTangent * nTS.x +
i.worldBitangent * nTS.y +
i.worldNormal * nTS.z);
//Waterspecific
@@ -163,7 +189,6 @@ Shader "DeMuenu/World/Hoppou/Water"
//Watershader specific
//float fres = Schlick(saturate(dot(N, V)), _F0, _FresnelPower);
float3 R = reflect(-V, N);
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;