Fast 3D domain warping in p5
This library is secretly building and applying a shader for you from the warp function you build! It works with p5's material system so that you can keep using normal lighting and color functions as usual.
It uses a vertex shader to adjust the position of each point on a model according to the warp, and also update the normals of the model so that lighting still works. Because it's done in a shader, that means you can animate your warps and it will still run fast by leveraging your GPU! It also means that it only moves the points that already exist on the model: for a warp to look smooth, your model has to already be somewhat dense with points (which, for example, p5's box()
is not.)
Add the library in a script tag:
<script src="https://cdn.jsdelivr.net/npm/@davepagurek/p5.warp@0.0.12"></script>
Or on OpenProcessing, add the CDN link as a library:
https://cdn.jsdelivr.net/npm/@davepagurek/p5.warp@0.0.12
If you're using p5 without importing it globally, you can manually set up p5.warp:
import P5 from 'p5'
import { setupWarp } from '@davepagurek/p5.warp'
setupWarp(P5)
In your setup function, you can create a new warp by calling createWarp
and giving it a function that takes in the warp inputs and returns a 3D offset that will be applied to each vertex of a shape:
let distort
function setup() {
createCanvas(600, 600, WEBGL);
distort = createWarp(({ glsl, millis, position }) => {
const t = millis.div(1000)
return glsl.vec3(
t.mult(2).add(position.y().mult(4)).sin().mult(0.15),
t.mult(0.5).add(position.z().mult(2)).sin().mult(0.15),
t.mult(1.5).add(position.x().mult(3)).sin().mult(0.15)
)
})
}
Then, when you want to apply the warp, call the warp function you made before you draw shapes:
function draw() {
background(220)
distort()
lights()
sphere(150)
}
createWarp
has the following signature:
createWarp(offset: (params: Params) => VecOp, options?: Options)
type Params = {
glsl: AD
position: VecParam
uv: VecParam
normal: VecParam
mouse: VecParam
mouseX: Param
mouseY: Param
millis: Param
pixelDensity: Param
size: VecParam
width: Param
height: Param
color: VecParam
}
type Options = {
type?: 'specular' | 'normal'
space?: 'world' | 'local'
defs?: string
}
A params
object is passed as an input to your offset function. Here's what each property is and how you might want to use them:
-
glsl
is an instance of the glsl-autodiff library. From it, you can create now constant values (e.g.glsl.val(123)
,glsl.val(Math.PI)
, etc), or create vectors containing other values (e.g.glsl.vec3(0, 1, position.x()
) -
position
is avec3
with the position of each vertex, by default in object space. Note that the general scale of these values will vary from shape to shape:sphere()
goes from -1 to 1, while a normalizedp5.Geometry
will have values ranging from -100 to 100. Passspace: 'world'
in the options object to use world-space coordinates, which will not have a different scale per object. -
uv
is avec2
with the texture coordinate for each vertex. -
normal
is avec3
with the normal for that vertex, which is a direction pointing directly out of the surface. -
mouse
is avec2
with p5'smouseX
andmouseY
values stored in x and y. -
mouseX
is afloat
set to p5'smouseX
. This is equivalent tomouse.x()
. -
mouseY
is afloat
set to p5'smouseY
. This is equivalent tomouse.y()
. -
millis
is afloat
set to p5'smillis()
. -
pixelDensity
is afloat
set to p5'spixelDensity()
. -
size
is avec2
with p5'swidth
andheight
stored in x and y. -
width
is afloat
set to p5'swidth
. This is equivalent tosize.x()
. -
height
is afloat
set to p5'sheight
. This is equivalent tosize.y()
. -
color
is avec4
representing either the whole model's fill color, or the per-vertex fill color if it exists.
Each input property comes from glsl-autodiff
. You can see a full list of the methods you can call on them in the glsl-autodiff
readme.
After your offset function, you can specify an optional options
object with these properties:
-
type
: The type of p5 material to make, eitherspecular
ornormal
, corresponding to p5'sspecularMaterial()
andnormalMaterial()
. The default isspecular
. -
space
: what coordinate space theposition
property of the offset params will be in, eitherworld
orlocal
. The default islocal
. -
defs
: A string with any uniforms you want to splice into the header of the shader. You can then reference them by usingglsl.param('myUniform')
within your distortion function, and pass in an object of uniforms when you apply the distortion, e.g.distort({ myUniform: 0.5 })