Preview:


Breakdown:

Stretch the noise in the x and y directions and take the maximum value.
The result is across-noise texture that simulates a canvas texture.


Left: Divide image into grids to organize the placement of brush strokes, with each grid cell geting one brush stroke.
Every second row is offset to create a triangular pattern;
Right: To simplify later brush application, transform the sampling coordinate from the global UV space into the brush’s local space:
float2 pos = float2(dot(uv, n), dot(uv, t)) / float2(brushWidth, brushLength);



Estimate color gradient at the brush center using central difference: [f(x+h)−f(x−h)]/(2h).
Left: source image with contrast and brightness adjusted;
Middle: brushSize = 0.25 × gridSize;
Right: brushSize = 0.5 × gridSize.



Estimate color gradient at the brush center using central difference: [f(x+h)−f(x−h)]/(2h).
Left: Randomize brush positions and stretches;
Middle: Bend the brush strokes;
Right: Add multiple layers with different grid sizes.






The images above show the progressive shaping of the brush stroke through staged mathematical operations:
1. Base Shape: Create soft quad shape using a 2D parabola;
2. Edge Sharpening: Remap an clamp the falloff curve;
3. Hair Texture: Multiply the shape by a procedural noise field;
4. Vertical Falloff: vertical flow mask using cosine and noise based on y;
5. Ink Distribution: Simulate denser ink at the brush tip and sides using a combination of cosine and exponential functions and vertical blending;
6. Final Blending: Final brightness adjustment based on vertical position, adding contrast, then clamped.


01


02


03


05


06