Merge eureka/aetheria-gravity-stardust-bounds (closing verdicts cut-stardust-bounds.s1, cut-stardust-bounds.s2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
fdecde02d0
4 changed files with 120 additions and 29 deletions
|
|
@ -16461,7 +16461,6 @@ MonoBehaviour:
|
||||||
NebulaTint: {fileID: 8400000, guid: d196505464047f04a97f28689041209b, type: 2}
|
NebulaTint: {fileID: 8400000, guid: d196505464047f04a97f28689041209b, type: 2}
|
||||||
Heightmap: {fileID: 8400000, guid: 2fc21a9d3e9647948b56e384cd9378b3, type: 2}
|
Heightmap: {fileID: 8400000, guid: 2fc21a9d3e9647948b56e384cd9378b3, type: 2}
|
||||||
ParticleColors: {fileID: 2800000, guid: 56db63da398b2d04394ddbccedce943c, type: 3}
|
ParticleColors: {fileID: 2800000, guid: 56db63da398b2d04394ddbccedce943c, type: 3}
|
||||||
GravityCamera: {fileID: 1537342003}
|
|
||||||
--- !u!114 &1009503736
|
--- !u!114 &1009503736
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|
|
||||||
|
|
@ -892,7 +892,6 @@ MonoBehaviour:
|
||||||
NebulaTint: {fileID: 8400000, guid: d196505464047f04a97f28689041209b, type: 2}
|
NebulaTint: {fileID: 8400000, guid: d196505464047f04a97f28689041209b, type: 2}
|
||||||
Heightmap: {fileID: 8400000, guid: 2fc21a9d3e9647948b56e384cd9378b3, type: 2}
|
Heightmap: {fileID: 8400000, guid: 2fc21a9d3e9647948b56e384cd9378b3, type: 2}
|
||||||
ParticleColors: {fileID: 2800000, guid: 56db63da398b2d04394ddbccedce943c, type: 3}
|
ParticleColors: {fileID: 2800000, guid: 56db63da398b2d04394ddbccedce943c, type: 3}
|
||||||
GravityCamera: {fileID: 2103364771}
|
|
||||||
--- !u!114 &750375536
|
--- !u!114 &750375536
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
@ -140,6 +140,8 @@ namespace Aetheria.EditorTools
|
||||||
Call(volume, "Update");
|
Call(volume, "Update");
|
||||||
if (Differing(flowing, Frame()) == 0) Fail("scene-stardust-run", "the particle buffer did not move while the zone ran");
|
if (Differing(flowing, Frame()) == 0) Fail("scene-stardust-run", "the particle buffer did not move while the zone ran");
|
||||||
if (Shader.GetGlobalFloat("_NebulaNoiseTime") == noiseTime) Fail("scene-noise-clock", "_NebulaNoiseTime did not move while the zone ran");
|
if (Shader.GetGlobalFloat("_NebulaNoiseTime") == noiseTime) Fail("scene-noise-clock", "_NebulaNoiseTime did not move while the zone ran");
|
||||||
|
|
||||||
|
CheckStardustUncullable(dust, bufferField);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
@ -147,6 +149,88 @@ namespace Aetheria.EditorTools
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The dust is a command buffer on the target camera, which no culling sees: with every particle 10000 below the
|
||||||
|
// camera (far past any box a hand could inflate round it) a render still shows dust, and shows none when the
|
||||||
|
// same particles have no size.
|
||||||
|
static void CheckStardustUncullable(Stardust dust, FieldInfo bufferField)
|
||||||
|
{
|
||||||
|
var camera = dust.TargetCamera;
|
||||||
|
if (camera == null) { Fail("scene-stardust-uncullable", "Stardust has no TargetCamera"); return; }
|
||||||
|
var attached = camera.GetCommandBuffers(CameraEvent.BeforeForwardAlpha).Count(buffer => buffer.name == "Stardust");
|
||||||
|
if (attached != 1) Fail("scene-stardust-uncullable", $"{attached} Stardust command buffers on the target camera, expected 1");
|
||||||
|
|
||||||
|
var particles = (ComputeBuffer) bufferField.GetValue(dust);
|
||||||
|
var side = Mathf.RoundToInt(Mathf.Sqrt(particles.count));
|
||||||
|
float[] Layout(float size, Vector3 centre, float below)
|
||||||
|
{
|
||||||
|
var data = new float[particles.count * 7];
|
||||||
|
for (var i = 0; i < particles.count; i++)
|
||||||
|
{
|
||||||
|
data[i * 7 + 0] = centre.x + ((i % side) - side / 2f) * 4f;
|
||||||
|
data[i * 7 + 1] = centre.y - below;
|
||||||
|
data[i * 7 + 2] = centre.z + ((i / side) - side / 2f) * 4f;
|
||||||
|
data[i * 7 + 3] = data[i * 7 + 4] = data[i * 7 + 5] = 1f;
|
||||||
|
data[i * 7 + 6] = size;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
var transform = camera.transform;
|
||||||
|
var (position, rotation) = (transform.position, transform.rotation);
|
||||||
|
var (far, flags, background, mask, target) = (camera.farClipPlane, camera.clearFlags, camera.backgroundColor, camera.cullingMask, camera.targetTexture);
|
||||||
|
var texture = RenderTexture.GetTemporary(64, 64, 24, RenderTextureFormat.ARGB32);
|
||||||
|
var readback = new Texture2D(64, 64, TextureFormat.RGBA32, false);
|
||||||
|
var previous = RenderTexture.active;
|
||||||
|
// Image effects on the camera would write their own frame over the dust; only the dust draw is under test.
|
||||||
|
var effects = camera.GetComponents<MonoBehaviour>().Where(behaviour => behaviour.enabled).ToArray();
|
||||||
|
Color32[] Render(float size, Vector3 centre, float below)
|
||||||
|
{
|
||||||
|
Call(dust, "Update"); // on a bounded draw this queues the draw for the camera; here it dispatches the compute
|
||||||
|
particles.SetData(Layout(size, centre, below));
|
||||||
|
camera.Render();
|
||||||
|
RenderTexture.active = texture;
|
||||||
|
readback.ReadPixels(new Rect(0, 0, 64, 64), 0, 0);
|
||||||
|
readback.Apply();
|
||||||
|
return readback.GetPixels32();
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var effect in effects) effect.enabled = false;
|
||||||
|
camera.clearFlags = CameraClearFlags.SolidColor;
|
||||||
|
camera.backgroundColor = Color.black;
|
||||||
|
camera.cullingMask = 1;
|
||||||
|
camera.targetTexture = texture;
|
||||||
|
// control: the camera where the scene put it, dust 100 below, inside any box a hand-set extent would give
|
||||||
|
transform.SetPositionAndRotation(position, Quaternion.Euler(90f, 0f, 0f));
|
||||||
|
camera.farClipPlane = 1000f;
|
||||||
|
var nearWith = Render(5f, position, 100f); var nearWithout = Render(0f, position, 100f);
|
||||||
|
var nearDiff = 0; for (var i = 0; i < nearWith.Length; i++) if (!nearWith[i].Equals(nearWithout[i])) nearDiff++;
|
||||||
|
Debug.Log($"{Tag} stardust near-control differing pixels={nearDiff}");
|
||||||
|
if (nearDiff == 0) Fail("scene-stardust-uncullable", "control: dust 100 below the camera drew no pixels");
|
||||||
|
transform.SetPositionAndRotation(new Vector3(0f, -40000f, 0f), Quaternion.Euler(90f, 0f, 0f));
|
||||||
|
camera.farClipPlane = 30000f;
|
||||||
|
var withDust = Render(400f, transform.position, 10000f); // the material scales a quad by distance^.33, so at this range it needs this size to cover pixels
|
||||||
|
var without = Render(0f, transform.position, 10000f);
|
||||||
|
var differing = 0;
|
||||||
|
for (var i = 0; i < withDust.Length; i++)
|
||||||
|
if (!withDust[i].Equals(without[i])) differing++;
|
||||||
|
if (differing == 0) Fail("scene-stardust-uncullable", "the dust 10000 below the camera drew no pixels");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
RenderTexture.active = previous;
|
||||||
|
foreach (var effect in effects) effect.enabled = true;
|
||||||
|
camera.targetTexture = target;
|
||||||
|
camera.cullingMask = mask;
|
||||||
|
camera.backgroundColor = background;
|
||||||
|
camera.clearFlags = flags;
|
||||||
|
camera.farClipPlane = far;
|
||||||
|
transform.SetPositionAndRotation(position, rotation);
|
||||||
|
RenderTexture.ReleaseTemporary(texture);
|
||||||
|
UnityEngine.Object.DestroyImmediate(readback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CloudShader's pass 0 compiles with the flow keywords (it must #include_with_pragmas the shared include).
|
// CloudShader's pass 0 compiles with the flow keywords (it must #include_with_pragmas the shared include).
|
||||||
static void CheckCloudKeywords()
|
static void CheckCloudKeywords()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ public class Stardust : MonoBehaviour
|
||||||
public Texture Heightmap;
|
public Texture Heightmap;
|
||||||
public Texture2D ParticleColors;
|
public Texture2D ParticleColors;
|
||||||
//public Transform TargetTransform;
|
//public Transform TargetTransform;
|
||||||
public Camera GravityCamera;
|
|
||||||
|
|
||||||
private const int GROUP_SIZE = 128;
|
private const int GROUP_SIZE = 128;
|
||||||
private int _updateParticlesKernel;
|
private int _updateParticlesKernel;
|
||||||
|
|
@ -57,6 +56,11 @@ public class Stardust : MonoBehaviour
|
||||||
private ComputeBuffer _quadPoints;
|
private ComputeBuffer _quadPoints;
|
||||||
private const int QUAD_STRIDE = 12;
|
private const int QUAD_STRIDE = 12;
|
||||||
|
|
||||||
|
// The dust is drawn by a command buffer on TargetCamera: a camera replays its buffers at their event and never culls them,
|
||||||
|
// so the draw needs no bounds and no extent can fall short of how deep the field is.
|
||||||
|
private CommandBuffer _draw;
|
||||||
|
private const CameraEvent DrawEvent = CameraEvent.BeforeForwardAlpha;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region setup
|
#region setup
|
||||||
|
|
@ -91,6 +95,33 @@ public class Stardust : MonoBehaviour
|
||||||
new Vector3(-.5f, -.5f),
|
new Vector3(-.5f, -.5f),
|
||||||
new Vector3(-.5f, .5f)
|
new Vector3(-.5f, .5f)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//The buffers never change after this, so the material is bound once
|
||||||
|
ParticleMaterial.SetBuffer("particles", _particlesBuffer);
|
||||||
|
ParticleMaterial.SetBuffer("quadPoints", _quadPoints);
|
||||||
|
|
||||||
|
//Start may run again (the scene harness calls it by hand): drop the previous draw before building the next
|
||||||
|
DetachDraw();
|
||||||
|
_draw?.Release();
|
||||||
|
_draw = new CommandBuffer { name = "Stardust" };
|
||||||
|
_draw.DrawProcedural(Matrix4x4.identity, ParticleMaterial, 0, MeshTopology.Triangles, 6, Span * Span);
|
||||||
|
AttachDraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnEnable() => AttachDraw();
|
||||||
|
|
||||||
|
void OnDisable() => DetachDraw();
|
||||||
|
|
||||||
|
void AttachDraw()
|
||||||
|
{
|
||||||
|
if (_draw != null && TargetCamera != null)
|
||||||
|
TargetCamera.AddCommandBuffer(DrawEvent, _draw);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DetachDraw()
|
||||||
|
{
|
||||||
|
if (_draw != null && TargetCamera != null)
|
||||||
|
TargetCamera.RemoveCommandBuffer(DrawEvent, _draw);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
@ -104,7 +135,6 @@ public class Stardust : MonoBehaviour
|
||||||
// Particle lifetimes cycle on the flow clock (zone time), so the dust stops with the sim like everything the flow moves.
|
// Particle lifetimes cycle on the flow clock (zone time), so the dust stops with the sim like everything the flow moves.
|
||||||
ParticleCalculation.SetFloat("phase", FlowField.Cycle(VolumeSampling.FlowClock, Period));
|
ParticleCalculation.SetFloat("phase", FlowField.Cycle(VolumeSampling.FlowClock, Period));
|
||||||
|
|
||||||
var pos = GravityCamera.transform.position;
|
|
||||||
ParticleCalculation.SetFloat("period", Period);
|
ParticleCalculation.SetFloat("period", Period);
|
||||||
ParticleCalculation.SetFloat("spacing", Spacing);
|
ParticleCalculation.SetFloat("spacing", Spacing);
|
||||||
ParticleCalculation.SetFloat("ceilingHeight", Ceiling);
|
ParticleCalculation.SetFloat("ceilingHeight", Ceiling);
|
||||||
|
|
@ -121,38 +151,17 @@ public class Stardust : MonoBehaviour
|
||||||
//Dispatch, launch threads on GPU
|
//Dispatch, launch threads on GPU
|
||||||
int numberOfGroups = Mathf.CeilToInt((float)Span * Span / GROUP_SIZE);
|
int numberOfGroups = Mathf.CeilToInt((float)Span * Span / GROUP_SIZE);
|
||||||
ParticleCalculation.Dispatch(_updateParticlesKernel, numberOfGroups, 1, 1);
|
ParticleCalculation.Dispatch(_updateParticlesKernel, numberOfGroups, 1, 1);
|
||||||
|
|
||||||
ParticleMaterial.SetBuffer("particles", _particlesBuffer);
|
|
||||||
ParticleMaterial.SetBuffer("quadPoints", _quadPoints);
|
|
||||||
Graphics.DrawProcedural(ParticleMaterial, new Bounds(pos, new Vector3(Spacing * Span, 2048, Spacing * Span)), MeshTopology.Triangles, 6, Span * Span, TargetCamera, null, ShadowCastingMode.Off, false, 0);
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region rendering
|
|
||||||
// void OnRenderObject()
|
|
||||||
// {
|
|
||||||
// if (Camera.current == TargetCamera || Camera.current.name == "SceneCamera")
|
|
||||||
// {
|
|
||||||
// //Bind resources to material
|
|
||||||
// ParticleMaterial.SetBuffer("particles", _particlesBuffer);
|
|
||||||
// ParticleMaterial.SetBuffer("quadPoints", _quadPoints);
|
|
||||||
// // ParticleMaterial.SetFloat("velocityStretch", ParticleVelocityStretch);
|
|
||||||
// // ParticleMaterial.SetVector("velocity", PlayerVelocity);
|
|
||||||
//
|
|
||||||
// //Set the pass
|
|
||||||
// ParticleMaterial.SetPass(0);
|
|
||||||
//
|
|
||||||
// //Draw
|
|
||||||
// Graphics.DrawProceduralNow(MeshTopology.Triangles, 6, Span * Span);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region cleanup
|
#region cleanup
|
||||||
void OnDestroy()
|
void OnDestroy()
|
||||||
{
|
{
|
||||||
// m_emitterProxies.Release();
|
// m_emitterProxies.Release();
|
||||||
// _temperature.Release();
|
// _temperature.Release();
|
||||||
|
DetachDraw();
|
||||||
|
_draw?.Release();
|
||||||
|
_draw = null;
|
||||||
if(_particlesBuffer!=null)
|
if(_particlesBuffer!=null)
|
||||||
{
|
{
|
||||||
_particlesBuffer.Release();
|
_particlesBuffer.Release();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue