how do you calculate correct normals when doing vertex displacement shaders?

here's a lil guide/example on the overly complicated but mathematically correct way of doing this~ Image
say you have this simple smoothstep bump, with a radius (r) and an amplitude (a)

the space we're working in will be in uv space, where (0,0) is in the center of this plane

o.uv = the current 2d uv coordinate

d = length(o.uv)
height = smoothstep(r,0,d)*a;
what we need is called the partial derivatives (rate of change) of this function, in order to calculate the normal

now, we're going the unnecessarily accurate route, so we're not using the ddx/ddy functions in the frag shader, that's cheating~
in order to do this, we have to unpack the math a little so we can differentiate it, so, let's do that!

first off, smoothstep(a,b,v) is a (clamped) inverse lerp:
t = saturate((v-a)/(b-a))

...followed by a cubic smooth function:
t²(3-2t)

source: developer.download.nvidia.com/cg/smoothstep.… Image
the point of inverse lerp here is to remap the distance d from 0 to r into 1 to 0

(1 in the center, 0 at radius distance away from the center)

simplifying:
t = invLerp( r, 0, d ) =
(d-r)/(0-r) =
1-d/r

so, now that we know t = 1-d/r, let's apply the cubic smooth function
now, working out this math is tedious and boring so let's throw what we've got into wolfram alpha:
t²(3-2t) where t = 1-d/r

and, we get a nice formula back! thx wolfie~ Image
finally, we need to scale it by the amplitude with a simple multiply, and we end up with:
height = (a(d-r)²(2d+r))/r³

beautiful! however, we forgot the clamp~

in code we can just clamp d to never be greater than r:
d = min( length(o.uv), r )
now, it's a little hidden in the formula, but we actually have *two* inputs to this function, not one

d = length(o.uv) is kinda hiding that it's based on u and v, so let's be even more verbose:

d = length(o.uv) = √(u²+v²)
so what did we just do?

instead of:
d = length(o.uv)
height = smoothstep(r,0,d)*a;

we now have:
height = (a(√(u²+v²)-r)²(2√(u²+v²)+r))/r³

wow this looks much less clean, thanks math!

however, with math, we can do some magic~
we need to know - what is the rate of change (derivative) of this formula, along u and v, respectively?

well, we just throw this to the wolves again to get the partial derivatives:
f(u,v) = (a(√(u²+v²)-r)²(2√(u²+v²)+r))/r³

and wolfie gives us exactly what we need~ Image
∂f/∂u = 6au(√(u²+v²)-r)/r³
∂f/∂v = 6av(√(u²+v²)-r)/r³

we can write this in one line as a vector in shader code, since there's only one place they differ!
pd = 6a*o.uv*(√(u²+v²)-r)/r³

we can also simplify since √(u²+v²) = d:
pd = 6a*o.uv*(d-r)/r³
so what we have now, is the *slope* of this surface, along u and v, but we don't have the normal

now, whenever you want to convert from a slope to a vector, you insert the slope as components, while the remaining components equal 1

normal = normalize(float3(pd.x, pd.y, 1))
that's it! hell yea normals 💜

d = min( length(o.uv), r )
h = (a(d-r)²(2d+r))/r³
pd = 6a*o.uv*(d-r)/r³
normal = normalize(float3(pd,1)) Image
also, here's an interactive version of this hump (in 2D instead of 3D) if you want to play with sliders 📈
desmos.com/calculator/aoq…
~FAQ~
Q: isn't it easier to just-
A: yes! this is the long way around. very slow to work with as you need to redo the math for every new input, but it's accurate as heck/not an approximation~

Q: what if my shape isn't a plane
A: transform this from tangent space or something idk

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with Freya Holmér

Freya Holmér Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @FreyaHolmer

Aug 14
okay haha holy buckets

Material.SetInt() allocates a *huge* amount of memory, and yet it isn't marked as deprecated (as of 2021.2)

its inline doc says it's deprecated, but it doesn't have the obsolete attribute 💀

and why was a function added, instead of fixing the original?
it looks like it's related to a change in 2021.1, where unity got support for proper integer shader properties

prior to this, they were floats under the hood, and somehow, this means, allocations, somewhere, along the way
wait now I'm getting it with SetInteger too ;-;

why is it allocating help
Read 4 tweets
Jun 23
I have no idea how to tell if I'm learning anything when doing this ,_,
it's hard to tell how the ribcage/hip boxes relate to, almost anything. like, I feel like the ribcage box should align with the spine/ribcage back, but the top/bottom faces don't really represent anything when it comes to their angles?

pose refs from deviantart.com/lalunatique/ar…
like I guess it's meant to mostly hint at the orientation/approximate size, but, if that's all it is, then I'd rather just make, a cross at the back of the spine or something, because the boxes to me feel like they're trying to be more than that
Read 4 tweets
Jun 22
you ever just start working on something for like 20 minutes after lunch, and then you get distracted away from your work by something else, and suddenly it's dinner time and you've still only gotten 20 minutes of work done
burnt out and scatterbrained? haha cannot be me!!
I hate this feeling, it's so so so hard to focus and I get pulled away by the smallest things

sometimes pulled away by literally nothing but my brain deciding to just fuck off for a while
Read 4 tweets
Jun 21
swapping splines
this is part of my upcoming video on Splines!

if you like my work, consider supporting me on patreon! it's legit the best and most cost efficient way to support me 💖

patreon.com/acegikmo
I've spent, months of research, code implementation, and animation on this whole project, and I've some animation work left to be done - last 20% or so
Read 4 tweets
Jun 2
I have a looming feeling AI generated art will devour the economic sustainability of being an illustrator

not because art will be replaced by AI as a whole - but because it'll be so much cheaper and good enough for most people and corporations
it feels like digital artists will slowly fade into obscurity, limited to only artsy indie projects where hand authored art is now a rare and niche aspect of a project
it's easy to say "oh I would only get art from real artists"

..until you have to choose between a $500 commission vs $0 for an AI that can get you 95% of the way there
Read 10 tweets
Mar 28
the relationship between C (parametric) and G (geometric) continuity between curves

• C0/G0 are equivalent
• G2 continuity is also G1 and G0 continuous
• C2 continuity is also G2, C1, G1 and G0 continuous
it's a little difficult to wrap your head around, but this is how they relate to the derivatives (top)

notably, G-continuity has no restriction on parametric continuity.

G2 *can* be C1 too, but it only has to be the lesser strict G1

G3 *can* be C2 & C1, but has to be G2 & G1
a general definition of Gn continuity:

the join between two curves A(t) and B(t), is Gn continuous, if a function g(t) exists, so that A(t) and B(g(t)) is Cn continuous
Read 6 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us on Twitter!

:(