What Really Happens During a Stroke
In ArmorPaint, a stroke is not a CPU brush image waiting to be uploaded after the fact. It is a coordinated GPU operation that turns input-derived brush values into writes across a layer’s PBR targets, subject to geometry, masks, and the current material. This chapter follows one stroke from cursor or pen input to visible result, then separates the surrounding stages—live preview, dilation, linked layers, and undo—that make the operation usable and reversible.
The material graph already defines what the surface can compute. The brush graph defines how the current interaction supplies position, scale, angle, opacity, hardness, masks, or stencils. The project model defines which layer and channels receive the result. The render path brings those facts together at the moment of painting.
Input becomes paint state
A brush graph is evaluated as interaction logic, not as a second material. Its output can provide the current paint vector, brush radius, scale, angle, opacity, hardness, and references to mask or stencil images. The brush output logic also checks whether the current tool is allowed to act. It considers whether the user is inside a paintable region, whether a layer is visible or a group, whether the interface is consuming the pointer, and whether the current operation is a pick, a stroke, or a path edit.
That first filtering step matters because a pointer position is not automatically a paint command. The same movement can occur over a toolbar, a node canvas, a layer panel, or a 2D view. A layer driven by a fill material should not be overwritten by an ordinary brush stroke. A group is a container, not a paint target. A path layer may use a gesture to add a point rather than to write pixels. The brush logic translates input into paint state only when the current context permits that transition.
The brush state also carries temporal context. ArmorPaint can distinguish a continuing interaction from a new one, and different tools can use different cadences. These differences are still expressed as inputs to the paint operation; they do not require the application to abandon the shared render path.
The important boundary is between interaction and rendering. The brush graph can say where and how strongly to paint. It does not itself decide how the surface’s base color, normal, or packed PBR values are blended. That work is performed by the generated paint shader with the layer and material resources bound around it. Keeping that boundary explicit lets the same interaction logic serve different paint consumers without redefining the surface model.
The surface test determines where a stroke can land
A screen-space cursor is not enough to identify a point on a 3D surface. ArmorPaint uses the existing render information to decide whether a fragment belongs to the intended surface and, depending on the tool, which coordinate or attribute should control the operation. Depth rejection can prevent a brush from painting through the visible geometry.