GLSL Autodiff
Tired of doing math to get normals in your vertex shader? Same. Use this library to write your function once and generate derivatives automatically!
Why?
Sometimes, I want to displace mesh vertices in a vertex shader. After doing this, the normals of a surface should change:
However, per-vertex normals don't automatically update! Manual updating of vertex normals requires you to take the derivative of your displacement function. This gets pretty involved and error-prone, so I wrote this library to automate the process. I wrote a long writeup on the math involved if you're interested in the internals of how it works.
Instead of writing GLSL code to first compute an offset and then compute a new normal induced by that offset, GLSL Autodiff lets you write just the displacement function using a Javscript-based API, and it will generate GLSL code for you for both the displacement and the normal!
import { gen } from `@davepagurek/glsl-autodiff'
const vert = `
void main(void) {
vec4 objSpacePosition = vec4(aPosition, 1.0);
float x = objSpacePosition.x;
float y = objSpacePosition.y;
${gen((ad) => {
// Compute an offset using the uniforms and attributes in the shader
const x = ad.param('x')
const y = ad.param('y')
const time = ad.param('time')
const speedX = 1.5
const speedY = 2.8
let offset = ad.val(0)
for (let i = 0; i < 3; i++) {
offset = offset.add(ad.sin(
ad.sum(
offset.mult(0.5).add(x.mult(speedX)).add(y.mult(speedY)),
time.mult(0.002),
)
))
}
offset = offset.mult(0.1)
// Generate GLSL code for the offset
offset.output('z')
// Generate GLSL code for the derivative
offset.outputDeriv('dzdx', x)
offset.outputDeriv('dzdy', y)
})}
// Use the generated displacement
objSpacePosition.z = z;
// Calculate the normal from the auto-generated derivatives
vec3 slopeX = vec3(1.0, 0.0, dzdx);
vec3 slopeY = vec3(0.0, 1.0, dzdy);
vNormal = uNormalMatrix * normalize(cross(slopeX, slopeY));
vec4 worldSpacePosition = uModelViewMatrix * objSpacePosition;
gl_Position = uProjectionMatrix * worldSpacePosition;
}
`
Using the library
Install the library via yarn with:
yarn add @davepagurek/autodiff
Then, you can import it in your Javascript or Typescript code:
import { gen } from `@davepagurek/glsl-autodiff'
Full API usage is available on GitHub.
Example Output
I've used this library a few times now to bend 3D models and still have working normals: