feat(render): Phase G.1 — billboard particle renderer for weather + spells
Add ParticleRenderer that draws every live particle from the shared ParticleSystem as a billboarded quad. Unit quad VBO + per-instance (pos, size, color) VBO with glVertexAttribDivisor for one draw call per emitter. Billboards using the camera's basis vectors so quads always face the viewer. Fragment shader does a procedural radial falloff (no texture pipeline needed — raindrops / snowflakes read as soft dots). AttachLocal emitters get re-centred on the camera each frame so the rain volume follows the player per r12 §7. Two-pass render splits additive from alpha-blend emitters so blend state flips once per kind rather than per-emitter. Wired into GameWindow.OnRender after static-mesh draw with depth write off (particles occluded by walls but don't self-occlude). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9957070cab
commit
9618c66813
4 changed files with 283 additions and 0 deletions
18
src/AcDream.App/Rendering/Shaders/particle.frag
Normal file
18
src/AcDream.App/Rendering/Shaders/particle.frag
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#version 430 core
|
||||
|
||||
in vec2 vTex;
|
||||
in vec4 vColor;
|
||||
out vec4 fragColor;
|
||||
|
||||
// Procedural rain/snow streak — no texture, just a radial falloff
|
||||
// centred on the quad so droplets read as small soft circles. Good
|
||||
// enough for weather + basic spell auras without a texture pipeline.
|
||||
|
||||
void main() {
|
||||
// Signed distance from quad center (in UV space).
|
||||
vec2 d = vTex - vec2(0.5, 0.5);
|
||||
float r = length(d) * 2.0; // 0 at center, 1 at corner
|
||||
float falloff = smoothstep(1.0, 0.4, r);
|
||||
if (falloff < 0.02) discard;
|
||||
fragColor = vec4(vColor.rgb, vColor.a * falloff);
|
||||
}
|
||||
31
src/AcDream.App/Rendering/Shaders/particle.vert
Normal file
31
src/AcDream.App/Rendering/Shaders/particle.vert
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#version 430 core
|
||||
|
||||
// Unit quad vertex (XY -0.5..+0.5)
|
||||
layout(location = 0) in vec2 aQuad;
|
||||
layout(location = 1) in vec2 aTex;
|
||||
|
||||
// Per-instance: world-space center + size
|
||||
layout(location = 2) in vec4 aPosAndSize;
|
||||
layout(location = 3) in vec4 aColor;
|
||||
|
||||
uniform mat4 uViewProjection;
|
||||
uniform vec3 uCameraRight;
|
||||
uniform vec3 uCameraUp;
|
||||
|
||||
out vec2 vTex;
|
||||
out vec4 vColor;
|
||||
|
||||
void main() {
|
||||
vec3 center = aPosAndSize.xyz;
|
||||
float size = aPosAndSize.w;
|
||||
|
||||
// Billboard: offset the quad vertex along the camera's right + up
|
||||
// basis vectors so it always faces the viewer.
|
||||
vec3 world = center
|
||||
+ uCameraRight * (aQuad.x * size)
|
||||
+ uCameraUp * (aQuad.y * size);
|
||||
|
||||
vTex = aTex;
|
||||
vColor = aColor;
|
||||
gl_Position = uViewProjection * vec4(world, 1.0);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue