Skip to content

Instantly share code, notes, and snippets.

@pcwalton
Last active September 27, 2025 08:13
Show Gist options
  • Save pcwalton/ff76c2781b6ca302450c01d578a7efa1 to your computer and use it in GitHub Desktop.
Save pcwalton/ff76c2781b6ca302450c01d578a7efa1 to your computer and use it in GitHub Desktop.
Unity Freeform Stretching

Unity Freeform Stretching

Revision 2

This note explains how the freeform stretching and rotate with stretch features in Unity's Shuriken ("Legacy") particle system work. It may be useful for those who want to duplicate the effect on the GPU, via Unity's VFX Graph or otherwise. These formulas were obtained through experimentation and, while they seem algebraically equivalent to what Shuriken does, are not the exact operations that Unity uses.

Vector projection of $V$ onto $U$ is notated as $\mathrm{proj}_U(V) = U\frac{V \cdot U}{U \cdot U}$. The magnitude of a vector $V$ is denoted with $||V|| = \sqrt{V \cdot V}$. A normalized vector is notated with $\hat V = \frac{V}{||V||}$.

All these operations are in particle space. That is, we assume that the particle is centered at the origin $(0, 0, 0)$, with its normal facing the +Z direction, with +Y being the particle "up" vector (i.e. $-v$ in $uv$ texture space with the origin at the upper left corresponds with +Y in particle space). If necessary, rotate and translate the camera as necessary to move it from world space to particle space before beginning. Assume particle space has an orthonormal basis.

Let $C$ be the position of the camera relative to the origin (i.e. relative to the particle center). Let $U$ be the up vector for the camera (which may not be $(0, 1, 0)$ if the particle has been rotated around its normal). Then define the basis vector $Z_0$:

$$ \begin{align*} Z_0 &= -\hat C \\ \end{align*} $$

Let $s$ be the size of the particle and $\ell$ be the length scale of the particle. Then the basis vector $Z$ is:

$$ Z = s^2(\ell Z_{0x}, \ell Z_{0y}, Z_{0z}) $$

If rotate-with-stretch mode is not enabled, then define the vectors $X_0$ and $Y_0$ as follows:

$$ \begin{align*} X_0 &= \frac{U - \mathrm{proj}_{Z_0}(U)}{||U - \mathrm{proj}_{Z_0}(U)||} \\ Y_0 &= X_0 \times Z_0 \end{align*} $$

Now scale them, first along the z-axis, then uniformly, to produce $X$ and $Y$:

$$ \begin{align*} X & = s( X_{0x}, &X_{0y},\quad &\ell X_{0z}&) \\ Y & = s( Y_{0x}, &Y_{0y},\quad &\ell Y_{0z}&) \\ \end{align*} $$

On the other hand, if rotate-with-stretch mode is enabled, then first compute the basis vector $X$ as follows:

$$ X = s \frac{(Z_y, -Z_x, 0)}{||(Z_y, -Z_x, 0)||} $$

Then compute the direction of the basis vector $Y$:

$$ \hat Y = \hat X \times \hat Z $$

And, finally, compute its magnitude:

$$ ||Y|| = \frac{s}{\sqrt{1 + \hat{Y}_z^2\left(1/\ell^2 - 1\right)}} $$

The positions of the vertices of the resulting quad, again in particle space, can be calculated as follows:

$$ \begin{bmatrix} X & Y & Z \end{bmatrix} \begin{bmatrix} \pm\frac{1}{2} \\ \pm\frac{1}{2} \\ 0 \end{bmatrix} $$

where $X$, $Y$, and $Z$ are the column vectors as specified above. The normal of the quad will point toward $Z$.

After calculating the vertex positions in particle space, you may transform the positions back to world space if desired.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment