Freya Holmér 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 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 18, 2024
I can no longer recommend people apply to @FuturegamesEDU, the school where I've been teaching at, after a number of things have come to light

I talked to several students last night who've described a school in complete disarray and disregard for their students, absolutely unrecognizable from what I remember back when I was a student in 2010

• there have been multiple cases of courses running completely without a teacher, because they couldn't find one in time, or because the teacher asked for too much money

• the animation/vfx students have not had any animation or vfx courses after a whole year. for context, students go to futuregames for about 2 years. but they have learned a little bit of after effects and mocap tech, I guess

• they also didn't get to participate in the cross-disciplinary game projects, which, in my opinion, is one of the most important things you (used to) get to do at futuregames

• when they inquired about all this the animation/vfx students were told the course was for animation and vfx in cinema, not for games, which means they were either lied to before they joined, or while they were there, as there was no indication of this in the marketing material before they joined, nor at the time they were told this

• once the students tried to self-organize into something more useful, asking if they could watch lecture recordings from the other disciplines and classes, they were *denied*, which is absolutely wild to me. as a result, some students have started passing around recordings on their own,, which, what the heck, why do they have to do this??

(on a tangent, this makes me glad I've specifically asked them ahead of time to let me publish my lectures publicly)

back when I was at FG, there were two groups of students, game design and 3D art, about 18 students each, at a single location in Stockholm

but now I'm hearing there's classes with 40 students, with 12 separate educations you can apply to, across 6 locations in sweden alone, and they're apparently now expanding to warsaw as well

I have to wonder why the heck are they expanding when they are stretched so thin they are effectively transparent and have no ability to find or pay teachers

yesterday one of the students told me they don't even have a teacher yet for the upcoming shader course, which is starting tomorrow :)

FutureGames hasn't reached out to me, by the way, so they either lost the people who had contacts within the industry, or lost their record of past teachers, or maybe my $50/h was too expensive for the, now multinational, company

they've clearly shifted gears into trying to grow as much as possible, with a complete disregard for its students, which is such a shame because FG used to be a really really good place, with teachers actively working in the industry, but is now regarded as one of the worst. it's so frustrating

I'll see if I can do something for the shader course next week for the students regardless, they deserve better than this
@FuturegamesEDU ok just heard that they do have a teacher for the shader course next week at least thank god
I've gotten messages from students and teachers since posting this, mostly concurring, but also some contradictory. so mind that all of this is second hand, and I'm not in a position to verify it all

but at the end of the day, there's very real disappointment among students
Read 4 tweets
Aug 11, 2024
"you can't debug print shaders" oh yeah? well check this shit out

that's right, a whole ass view matrix, served directly from the GPU
I'm using the R1 single-channel 1bpp 2D texture format for this one an image of pixel digits, along with code that reads: static uint dBits[5] = { 	3959160828, 	2828738996, 	2881485308, 	2853333412, 	3958634981 };  float DrawDigit(int2 px, const int digit){ 	if (px.x < 0 || px.x > 2 || px.y < 0 || px.y > 4) 		return 0; // pixel out of bounds 	const int xId = (digit == -1) ? 18 : 31 - (3 * digit + px.x); 	return (dBits[px.y] & 1 << xId) != 0; }
ok they can be placed in 3D now
Read 4 tweets
Jul 26, 2024
you've heard of sine and cosine

now get ready for uh squine and cosquare
source code. source math? idk

desmos.com/calculator/r0i…
screenshot of the equations in the linked desmos graph
i regret exposing myself to the pentagonal trig functions 😭 the sine/cosine equivalent curves for a pentagon. it looks very jagged and almost random. a horror, one might say
Read 6 tweets
Mar 24, 2024
there's something hauntingly beautiful about using vscode, an electron app, basically a browser, running v8, a js engine, to launch node, a server running v8 as a headless browser with js, which compiles my typescript code into js, for other people to run js in their browsers
it's, deeply cursed, but also, the fact that my IDE, my build step, and my audience, are all running js engines, and so that any js plugin I get or make, could potentially run in all three of these, plus serverside if I wanted to, is pretty neat and/or terrifying
stuff like this makes it pretty easy to move code between my ide vs build vs server vs client, which feels unusually flexible, it's pretty cool that it works
Read 5 tweets
Mar 18, 2024
"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, 2024
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

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!

:(