Freya the stray Profile picture
Apr 11, 2021 15 tweets 4 min read Read on X
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 the stray

Freya the stray 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

Mar 18
"do you do performance first or last when programming?" sets up a misleading false dichotomy

there are valid questions around when and how to consider performance when writing code - but it's not about "first" or "last"

(thread)
"do you do code readability first or last?" is a weird question, because most people understand it's a consideration throughout

every piece of code you write should be mentally checked for a bunch of things in the back of your mind, not like, obsessively, just, ambiently!
quite often, there are more valid questions underlying the statement though, like,

how much should you plan for performance ahead of time?

is it okay to write sloppy, less performant code, and optimize it later?
Read 25 tweets
Feb 26
if you're going through burnout, here are the two most important things I learned at rehab:

1. physical activity is much more important than exercise

a 10 min walk every day is significantly more effective for burnout recovery, than 1 hour of intense workout once a week
so, when it comes to physical activity, this is the order of importance:
• frequency
• duration
• intensity
set goals too high?

just keep lowering them until you're able to do them, there's no limit to how low you can go

10 min walk every day?
5 min walk every day?
5 min of doing *something* physical at home every day?

you get the idea
Read 15 tweets
Feb 10
I know it depends on art style etc but,

high frequency contrast like this, from pure white to pitch black, of both background elements and foreground elements, gives me art direction failure/school project/asset flip vibes

a short thread on visual contrast 🧵

Image
Image
looking at this scene by highlighting high-frequency noise, can you see how there's no distinction between the player path and the surroundings?

pretty much the only thing that pops is the character, because their design is less noisy, making for a visual oasis Image
if we look at the values only, there's a slightly stronger distinction - the wide climbable rocks are uniquely horizontal and more uniformly bright, thanks to the notorious yellow paint Image
Read 18 tweets
Dec 6, 2023
ok how the heck do you make a generalized miter join for n planes in 3D space?

I think I've boiled it down to finding some sort of best-fit generalized orthocenter of the spherical polygon convex hull formed by the face normals, but that seems like,, a lot

(thread)
for two normals, hardcoding it is relatively easy (code/math below)

for three normals, you can just find the intersection of the miter plane/point normals, because they converge at a single point (green lines in the .gif above) code for miter joins that read:  // using trig: float angle = Mathfs.AngleBetween( a, b ); float beta = angle / 2; Vector3 dir = ( a + b ).normalized; Vector3 miter = dir / MathF.Cos( beta );  // without trig: Vector3 dir = ( a + b ).normalized; float x = Vector3.Dot( a, b ).ClampNeg1to1(); Vector3 miter = dir * ( Mathfs.SQRT2 / MathF.Sqrt( x + 1 ) );
long story short, this is for an extrude algorithm

given n faces connected to a vertex V, find a vector offset for V such that offsetting it maintains consistent face distances, as much as possible

if a vertex is only extruding two faces, the miter offset is easy to calculate:
Read 11 tweets
Sep 17, 2023
alright, a thread with my first impressions of @godotengine, let's see how this goes screenshot of a selected cube in godot
@godotengine the Y axis is very shy and likes to hide every now and then
when the viewport width (in pixels) is odd, it's fine, but not when it's even
Read 15 tweets
Sep 15, 2023
a few of us unity insiders just had a meeting with some of the leadership at Unity

we're under NDA, so I can't share details of what was discussed

but, I got a tiny glimmer of cautious hope restored

I'm happy they were willing to talk frankly, and take the time to listen
I will continue to push on two points:

1. retroactive TOS/monetization changes are completely unacceptable, this is why trust was broken, irreparably for many. Any big changes to monetization should only apply to future versions of unity. not retroactively
2. the monetization scheme has to be something you can run the numbers on, it has to be predictable and workable as a business, using metrics/data we actually have access to, like sales. "installs" as a metric is a chaotic mess with lots of privacy and trust concerns
Read 4 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!

:(