Describe the field shader of a particle source (#170)
- Particles: rows Field shader, Field width and Field height, and a section on how the frame, its UV, its resolution and the checks work. - Shaders: a pointer to it, and the measurement with 1, 10 and 50 sources under What it costs. - Development: e2e total 1707. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent
7bddb77b5f
commit
315ee9215c
3 files changed
+50
-1
No files matched your search
+1
-1
@@ -18,7 +18,7 @@ php tests/php/accounts.test.php # the API's accoun
|
||||
# has and the web server's container has not (SKIP there)
|
||||
php tests/php/snapshots.test.php # the snapshot store (#231), the same way
|
||||
php tests/php/releases.test.php # the release store (#266), the same way
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1704 editor checks, screenshots in /tmp/gm-e2e-*.png
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1707 editor checks, screenshots in /tmp/gm-e2e-*.png
|
||||
GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step
|
||||
./e2e.sh start [GM_STEPS] # the same run detached, see below
|
||||
./e2e.sh status # live progress, or the last run's result
|
||||
|
||||
+28
@@ -87,6 +87,8 @@ mirror), so hanging one on a moving ship turns the whole spray with it. Its own
|
||||
| **Color** | white | The tint, and what an unconnected `color` output reads. |
|
||||
| **Move with source** | off | Off: a particle stays where it was born, so a moving source leaves a trail (rocket smoke). On: it hangs on the source and travels with it (sparks on a carried torch). The difference shows only while the source moves. |
|
||||
| **Shader** | none | A shader asset worn by **every particle** of this source, the way a sprite wears one. Its `Property` rows appear below, like a sprite's. |
|
||||
| **Field shader** | none | A shader asset laid over the **whole field** at once (see *A shader over the whole field*). Its rows appear below as well. |
|
||||
| **Field width**, **Field height** | 256 | The frame the field shader covers, in units, centred on the source. Shown only while a field shader is picked. |
|
||||
|
||||
Below them come the rows the asset's graph declares, one per `Property` node — so two fountains of
|
||||
the same asset can spray at different strengths, exactly the way two sprites wearing the same shader
|
||||
@@ -110,6 +112,32 @@ own row wins and the uniform of that name keeps its declared default: two rows c
|
||||
one row with two meanings. Nothing is rejected on the shader's side — the same shader on a sprite is
|
||||
perfectly fine, it is only this source that already has the name.
|
||||
|
||||
**A shader over the whole field** (#170). The *Field shader* row lays a shader over everything the
|
||||
source draws at once: the particles are drawn into a picture of a fixed frame, *Field width* by
|
||||
*Field height* units centred on the source, and the shader draws that picture. So it can do what a
|
||||
shader per particle cannot: a glow or blur where particles overlap, an outline round the whole
|
||||
cloud, a ripple through all of it. It is an ordinary shader asset; its `u`/`v` run from 0 to 1
|
||||
across the frame and never jump, however the cloud changes, and `Texture color`, `Texture alpha`
|
||||
and `Sample texture` read the picture of the particles (`Texture size` is that picture in pixels).
|
||||
The frame is the edge: whatever flies past it is cut off, so make it as large as the effect reaches.
|
||||
The scene view draws it as a dashed rectangle round the selected source. It turns and grows with the
|
||||
source.
|
||||
|
||||
Both shaders may be worn together: the one per particle draws into the picture, the field shader
|
||||
over it. A name both of them declare is one row, and both read it. The field shader's *Padding* and
|
||||
*Vertex output* do nothing here: the frame already is the room around the field, and its corners
|
||||
stay put, as with a nine-sliced sprite.
|
||||
|
||||
The picture has as many pixels as the frame covers on screen, so it stays sharp at any zoom, but at
|
||||
most 2048 per side. A source whose frame is off screen draws no picture at all. What it costs is one
|
||||
extra pass per source, sized by the frame, not by the number of particles; see *What it costs* in
|
||||
[Shaders](Shaders) for the measurement.
|
||||
|
||||
Measured in the checks (step 31): a field shader that changes nothing draws the field where it was
|
||||
and the right way up, with a picture of 256 pixels at zoom 1; a flat red fills the frame 100 units
|
||||
off the source where no particle is, and stops before 140; taken off again, the particles draw as
|
||||
before.
|
||||
|
||||
A **behavior** can start a source over with the `Emit` node (its `source` input names which one): in *Burst*
|
||||
mode that is another burst, in *Continuous* mode the stream begins again at particle 0.
|
||||
|
||||
|
||||
+21
@@ -260,6 +260,12 @@ never there to lose. The difference is what the fragment can see — a particle'
|
||||
age or index — and that a source, having rows of its own graph as well, keeps a name the shader
|
||||
also declares: the uniform then stays on its default rather than taking the row.
|
||||
|
||||
**A source can also wear one over its whole field** (#170, the *Field shader* row, see
|
||||
*A shader over the whole field* in [Particles](Particles)). There the shader draws a picture of
|
||||
all the particles inside a fixed frame round the source, so its `u`/`v` span the frame and
|
||||
`Sample texture` reaches across neighbouring particles: glow, blur and outlines of the whole cloud.
|
||||
Unlike a shader per particle this is an extra pass, one per source.
|
||||
|
||||
**A text object wears one the same way** (#169), through a *Shader* row below Align. Its glyphs are
|
||||
rasterised into a picture of their own and drawn on the sprite quad, so the graph sees exactly what a
|
||||
sprite's graph sees: `Texture color` is the glyph pixel in the text's colour, `Texture alpha` the
|
||||
@@ -314,6 +320,21 @@ Up to 400 wearers a grid hardly shows; at 800 it costs a quarter of the frame ra
|
||||
with 32 subdivisions is below the 57 at 400, so single values here are good to a few frames, not
|
||||
to one.
|
||||
|
||||
A field shader on a particle source (#170) is the one place a shader costs a pass: the source's
|
||||
particles are drawn into a picture of its frame first. One run per row with
|
||||
`tools/perf-field-shaders.mjs` on the same HD 630, sources spread over the 640x360 camera, a frame
|
||||
of 256 units (256x256 pixels at that zoom), 50 particles a second each, a field shader that hands
|
||||
the picture through unchanged, in frames per second:
|
||||
|
||||
| sources | 1 | 10 | 50 |
|
||||
| --- | --- | --- | --- |
|
||||
| no field shader | 60.1 | 60.0 | 59.2 |
|
||||
| field shader | 60.0 | 60.0 | 57.6 |
|
||||
|
||||
Up to 10 sources nothing shows; at 50 the field shaders cost about 1.6 frames a second. The 60 Hz
|
||||
cap hides what a pass costs inside a frame that is not full, and a graph that samples the picture
|
||||
many times (a blur) adds its own price per pixel on top of the pass.
|
||||
|
||||
## In the code
|
||||
|
||||
`src/core/shader/nodes.ts` is the catalogue, `src/core/shader/compile.ts` turns a graph into the
|
||||
|
||||
Reference in new issue
Block a user