Implementation

Stroke Placement

The system generates a procedural canvas texture by stretching noise fields in both X and Y directions, using a Max blend to simulate cross-noise fibers.

To organize the brush strokes, the source image is divided into a grid with an offset second row, forming a triangular pattern. For each grid cell, sampling coordinates are transformed from global UV space into the brush’s local space:
float2 pos = float2(dot(uv, n), dot(uv, t)) / float2(brushWidth, brushLength);
The stroke direction is determined by estimating the color gradient at the cell center using central difference: δh[f](x)=f(x+h)f(xh)2h\delta_h[f](x) = \frac{f(x+h) - f(x-h)}{2h}.

Canvas noise
Stretched noise simulating cross-woven canvas fibers.
Grid division
Triangular grid pattern for stroke distribution.
Local space transform
Local space transformation and gradient estimation logic.

Stroke Variation

The implementation supports randomized brush positioning and stretching, stroke bending, and multi-layer stacking at varying grid scales to build visual complexity.

Source adjustment
Source image with adjusted contrast and brightness.
Small strokes
brushSize = 0.25 × gridSize
Large strokes
brushSize = 0.5 × gridSize
Randomization
Randomized positions and stretches.
Bending
Non-linear stroke bending.
Layering
Multi-layer stacking with different grid sizes.

Procedural Brush Shaping

The images below illustrate the progressive shaping of the brush stroke through staged mathematical operations:

Step 1
1. Base: Soft quad shape via 2D parabola.
Step 2
2. Edge: Remapped and clamped falloff curve.
Step 3
3. Hair: Procedural noise field multiplication.
Step 4
4. Flow: Vertical flow mask via cosine and noise.
Step 5
5. Ink: Denser ink simulation at tip and sides.
Step 6
6. Blending: Final contrast and brightness adjustments.

Result

Source 1
Result 1
Source 2
Result 2
Source 3
Result 3