Expression attributes
The @ values you read and write inside an Expression — and which ones exist depends on what you feed in.
An Expression runs your code once per element of whatever you wire into it. An @name is one of those per-element values: a point's position, a pixel's colour, a voxel's density. You read them to make decisions, and you assign to them to change the result.
// read @Cd, write @P.z — push each point up by its brightness
@P.z = luminance(@Cd) * 100.0;
What you feed in decides what you get
The kind of thing you plug into an Expression sets the "loop" it runs over and which @ values are on the table:
Points / a collection — runs once per point. You get
@P,@Cd,@N,@pscale,@id,@ptnum,@analytic.Image (raster) — runs once per pixel.
@Cdis the pixel colour,@P.xyis its position,@uvis its texture coordinate.Volume — runs once per voxel.
@Pis the world position,@densityis the value at that voxel.Distance Field — runs once per sample.
@Pis the position,@distis the distance to the surface.Mesh — runs once per vertex. You get
@P,@N,@uv.
You don't pick the mode — it follows the input automatically. See Automatic conversion for how DNA converts between these.
An image or volume going into an Expression usually comes out as points (one per pixel/voxel). That's how you turn a depth image into a 3D point cloud — read @Cd, write @P.z. A Volume stays a Volume so you can keep processing it.
The common attributes
These are the ones you'll reach for most. Vectors support swizzles, so @P.z, @Cd.rgb, and @uv.x all work.
| Attribute | What it is | Where it lives |
|---|---|---|
@P | Position | points, mesh, volume, distance field |
@Cd | Colour (RGBA) | points, image |
@N | Normal direction | points, mesh |
@pscale | Per-element scale / size | points |
@uv | Texture coordinate | image, mesh |
@density | Voxel value | volume |
@dist | Distance to surface | distance field |
@analytic | Whether the shape stays an Analytic shape | points |
@id | Stable identity that survives across frames | points |
@ptnum | The element's index in the loop | points |
For the full picture of how attributes attach to data, see Attributes.
Reading vs writing
Reading is just mentioning the attribute. Writing is assigning to it on the left of an =.
@Cd = @P.y > 0 ? {1, 0, 0, 1} : {0, 0, 1, 1}; // read @P, write @Cd
@P += @N * noise(@P * 0.1 + $T) * 5.0; // read @N and @P, write @P
A few worth calling out:
@ptnumand@distare read-only.@ptnumis your position in the loop;@distis reported by the Distance Field. Assigning to them doesn't make sense.@idis yours to set, but treat it gently. It's the handle that keeps a point recognisable from one frame to the next (used by simulations and effects). Overwrite it only when you mean to.Swizzled writes keep the rest.
@P.z = ...changes only Z; X and Y stay as they were.
Not every attribute exists on every input. Reading @density on a point cloud, or @N on an image, won't give you anything useful — match the attribute to the input type from the table above.
See also
The Expression language — what an Expression is and where it runs
Globals & parameters — the
$values like$T(time) and$F(frame)Built-in functions —
noise,luminance, and the rest of the toolboxAttributes — attributes across the whole app
Analytic shapes — what
@analyticcontrols