Aetheria/Assets/Shaders/Compute/Stardust/Stardust.cs
Meta Vens 1973b6d220 Stardust: draw at BeforeForwardAlpha (deferred cameras never run AfterForwardOpaque); probe checks the event, culling mask 1 and a near control; Main Menu drops the stale GravityCamera
Both target cameras are deferred, so the AfterForwardOpaque buffer never drew. The render half of scene-stardust-uncullable now attaches-checks BeforeForwardAlpha, renders with cullingMask 1 (DrawProcedural is layer 0), and requires a near control (dust 100 below, size 5) to draw before the far probe is read, so a miss at 10000 can only be culling.
Builds: not compiled by this commit alone; verified in the Unity batchmode run reported with the cut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 05:46:50 +02:00

173 lines
5.6 KiB
C#

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Rendering;
public class Stardust : MonoBehaviour
{
#region variables
//public bool debugLog = false;
//public float proxyPersistTime = 2;
public GameSettings Settings;
public Camera TargetCamera;
public ComputeShader ParticleCalculation;
public Material ParticleMaterial;
public int Span = 512;
public float Period = .25f;
public float MinimumSize = .1f;
public float MaximumSize = 2;
public float Spacing = 4.0f;
public float Ceiling = -250.0f;
public float Floor = -25.0f;
public float MinHeadroom = 25;
public float MaxHeadroom = 100;
public float HeightExponent = 4;
public RenderTexture NebulaSurfaceHeight;
public RenderTexture NebulaPatchHeight;
public RenderTexture NebulaPatch;
public RenderTexture NebulaTint;
public Texture Heightmap;
public Texture2D ParticleColors;
//public Transform TargetTransform;
private const int GROUP_SIZE = 128;
private int _updateParticlesKernel;
#endregion
#region Structs
//Notice that this struct has to match the one in the compute shader exactly.
struct Particle
{
public Vector3 Position;
public Vector3 Color; //Color is a float3 in the compute shader, we need a Vector3 to match that layout, not a Color!
public float Size;
};
#endregion
#region buffers
private ComputeBuffer _particlesBuffer;
private const int PARTICLE_STRIDE = 28;
private ComputeBuffer _quadPoints;
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
#region setup
// Use this for initialization
void Start()
{
//Find compute kernel
_updateParticlesKernel = ParticleCalculation.FindKernel("UpdateParticles");
//Create particle buffer
_particlesBuffer = new ComputeBuffer(Span * Span, PARTICLE_STRIDE);
Particle[] particles = new Particle[Span * Span];
for (int i = 0; i < Span * Span; ++i)
{
particles[i].Position = Random.insideUnitSphere * 100;
particles[i].Color = Vector3.one; //white
particles[i].Size = Random.value;
}
_particlesBuffer.SetData(particles);
//Create quad buffer
_quadPoints = new ComputeBuffer(6, QUAD_STRIDE);
_quadPoints.SetData(new[] {
new Vector3(-.5f, .5f),
new Vector3(.5f, .5f),
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
#region Compute update
// Update is called once per frame
void Update()
{
//Bind resources to compute shader
ParticleCalculation.SetBuffer(_updateParticlesKernel, "particles", _particlesBuffer);
// 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("period", Period);
ParticleCalculation.SetFloat("spacing", Spacing);
ParticleCalculation.SetFloat("ceilingHeight", Ceiling);
ParticleCalculation.SetFloat("floorHeight", Floor);
ParticleCalculation.SetFloat("heightExponent", HeightExponent);
ParticleCalculation.SetFloat("maximumSize", MaximumSize);
ParticleCalculation.SetFloat("minimumSize", MinimumSize);
ParticleCalculation.SetFloat("minHeadroom", MinHeadroom);
ParticleCalculation.SetFloat("maxHeadroom", MaxHeadroom);
ParticleCalculation.SetInt("span", Span);
ParticleCalculation.SetTexture(_updateParticlesKernel, "HueTexture", ParticleColors);
//Dispatch, launch threads on GPU
int numberOfGroups = Mathf.CeilToInt((float)Span * Span / GROUP_SIZE);
ParticleCalculation.Dispatch(_updateParticlesKernel, numberOfGroups, 1, 1);
}
#endregion
#region cleanup
void OnDestroy()
{
// m_emitterProxies.Release();
// _temperature.Release();
DetachDraw();
_draw?.Release();
_draw = null;
if(_particlesBuffer!=null)
{
_particlesBuffer.Release();
_quadPoints.Release();
}
// m_livingProxyIndices.Release();
}
#endregion
}