Create a 3D Ripple Animation with React and Three.js using react-three-fiber | A Beginner's Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone my name is caleb and today we are going to be making this ripple animation that you see behind me using react and 3js okay so to start making this animation i'm just going to create a normal react project using create react app so i'm just going to name the project app after this i'm going to install some additional dependencies that will allow us to work with 3d and webgl in react and we should be good to go then to start making our animation so i'm just gonna wait for this okay and it's done so i'm gonna cd into app now and just install those additional dependencies that i mentioned so i'm going to install 3js this really does most of the heavy lifting for us in terms of like webgl and stuff um and i'm also going to install a reconciler called react3 fiber this basically allows us to interact with vjs using react components and jsx which makes it really nice to work with especially like if you're using 3d in in react i highly recommend using react-free fiber it just makes things a lot nicer to work with and you type like a lot more declaratively instead of like comparatively like 3gs is usually used so i'm just going to install these and do some project setup so i'm going to create an assets folder um so i'm just going to delete the styles there and just delete this stuff just cleaning up the template uh the clean slate i'm going to just be working the app component um but if this was an actual application you would want to you know um put this in a different component and do it there but i'm going to that was a typo so i'm going to uh just use order box so box sizing border box and hide the overflow on the x overflow hidden on the x and now i'm going to style this in a div which is going to hold everything basically and make it fill the whole screen so just say to that to that and background color to that cool um so now i'm going to just set up some of my imports so i'm going to import three i'm going to import the canvas from react from react 3 fiber i also want to use an image asset which i'll drag in right now [Music] this it's literally just a circle with the transparent border the transparent border is important you can really use any image you want um but this is what we're going to be using in our material to render each point um again the only thing that's really important about the exact image um when using the texture it should have a transparent um background wherever you don't want it to be visible um so if it's just black the black is gonna show so make sure it shines transparent so i'm going to import that image from assets circle png cool so we're going to be using um this circle image as a texture um and so i'm going to set up just my components just so i can show you how we can load this in so we're going to have an animation canvas component and this canvas component is going to basically wrap this canvas component and then points is actually going to contain the actual animation so first i'm gonna hold i'm gonna call this here i'm gonna call canvas in canvas i can set up some basic scene things um so i can like turn off color management and i can specify camera position uh so let me just do that real quick just looking up the values that are the best so camera position i found this placement works kind of well um but again this is entirely up to you where exactly you want the camera um since i just noticed that this finished i'm going to npm start so we don't waste that much time and uh continue on so this canvas is i'm also just going to add i'm going to add call the points component and this brings me to an important concept in working uh with 3gs in react so the canvas and points these are going to take time to load and the important thing is we need to specify fallback ui that should be displayed while that component is uploading luckily uh react provides a suspense component for this and we just specified this fallback ui like this for example so i could sit like the loading and then wrap put the animation canvas inside of the suspense and so now while animation canvas is loading this div is going to be displayed now you should notice that if i did the exact same thing in points i'm going to get an error it's because while points is loading you can the fallback ui that you uh specify has to be displayable um where it's going to be so the thing is when you have the inside canvas uh you can only uh display uh three components like three js components and not normal like html components like div or paragraph or something so instead of doing this i'm just going to place them all and it should work um yeah so now let's get into actually displaying these points so to display points we can use the points class from 3js so the points class is used to display points and it takes in a geometry of type buffer geometry and a material of type material you can see that the default material is called points material so a cool thing about reactive fiber is we can really like look at the 3gs documentation and get the uh and figure out um what the appropriate react code is gonna be um just straight from it and i'll show you that here so i know it takes geometry and material so i'm gonna go into my code and i'm going to call the component that corresponds to the points class then i'm going to create a buffer geometry because that was the type of the geometry i want to attach it to geometry geometry and i'm going to do the same thing for material remember it said the default material uh let me just pull it up it says default is a new points material so i'm going to use points material and in here you guessed it i'm going to attach it to material now note um since we're at material anyway i'm just going to set up points i mentioned a while back that i was going to be doing loading so we're going to just do that right now the thing is that we cannot use the surfboard image just out of the box like this we need to load it as a texture so to do that we can get an image tech constant and use the use loader hook and use a texture layer like this and pass it in our circle image to get the loaded image texture and now we can pass this texture in and map the map properly and pass the color i just found that this works but again entirely up to you we also set our size you can add size attenuation if you want to it doesn't really matter um and the things you need to pay attention to though are setting transparence transparent to false alpha test to 0.5 and opacity to 0.1 um or to 1.0 this basically ensures that the transparent portions are rendered transparent and not black so that's why we get this stuff here now if we look in buffer geometry we'll see that it uses buffer attributes to read and edit the data so again directly from the documentation we go and take the buffer attribute and we're going to attach this uh to the positions of these points because that's that's really what we want to be controlling when we animate our points so i'm going to pass in position cool and now i'm going to pass in an array of positions which i'll define above okay but this isn't going to be the proper way to define the positions um just for uh performance purposes we're going to be using the use um not me use memo hook um and pass like that um so we're going to pass our the arrow function like this and we're just going to create our positions so here here's how our positions are going to look so we cannot normally like if someone were to ask me how would i represent positions i would say 2d array or like an array of tuples or something like that where you you're basically going to have an array of like your xyz xyz for each point so but the thing is we can't actually pass a 2d array as far as i'm aware uh to a buffer attribute so we need a 1d array and so how that's going to look instead of that we're going to have something that looks like this so note that each item or point is having three values in this array um and so in this way we can define all the points but just in one dimension instead of two so this look so we know that each item or point has three values so because of that we're going to specify our item size to three buffer attribute also needs to know the count or the number of positions and all that's going to be is just the length of this array um divided by three because uh each position is going to take three of those values so to calculate uh the number of positions we're just going to do this basic arithmetic operation okay so now let's go ahead and actually populate uh this positions with values so i'm going to define this array locally and then i'm going to return something that can actually be used in the buffer which is a float32 array and passing positions like this okay so how can we populate these with values well this is this is what's going to happen i'm going to iterate over an index in my xz grid and i'm going to iterate from left to right on the x axis and then in that nested loop i'm going to iterate uh from back to front and i'm going to get the x comma z values then i'm going to determine the y value for that corresponding point and finally add it all into positions so to do that you need to keep we need to have two uh parameters if you will not really look i think you guys understand what i'm saying so we're going to need to know the count and the set the count is going to be the number of points across one axis and the set is going to be the separation or the distance between each point so note that the count squared or in this case like 10 000 points are going to be in the entire grid even though count is only 100 so let's write this loop so i'm going to say use x i for the index the x and then i'll use zi z now i can calculate the x and y values note that if i just did something like this since x i is always positive we're only going to get positive values of x and 0 0 like x comma z uh zero comma zero is going to be in the bottom left if you want um but that's not what we want we want our grid to be symmetric about the origin and to be able to do that um we are going to have to offset this by half the number of points so i'm going to subtract count divided by two and i'm going to do the same thing for z but with zi i'm just gonna set y to zero for now um and then we'll have an example that we can look at this should just i'll put us just a normal grid um of just flat points so we're going to now just push the x y and z to this array and we're finally returning this note that use memo is going to require a list of dependencies as you'll see here right here the dependency list so even though count and separate constants i'm just going to pass it anyway probably a bad idea but i'm going to do it um it shouldn't really matter if you do or not okay so now if i refresh let's see did i miss something i'm missing semicolo that shouldn't be a comma um okay so if i go to localhost 3000 we can see just a greater points cool so now let's actually um cast these onto the graph of the function so now let's try to build a mathematical function for our wave animation so consider the function sine of x graphing it we see that sine of x clearly has a cyclic pattern this cyclic pattern is defined by a period a period is just the distance that this wave has to travel before it repeats or this function has to travel before it repeats so we can see that from this point to this point the wave has a unique behavior but after that this portion is going to repeat this portion so we would say that the period of sine of x um is going to be the distance between these two points and some people know this as 2pm okay the second notificable feature about uh sine of x is the amplitude the amplitude you can think of is how tall the wave is and so amplitude in this case is going to be one finally we're going to look at this function sine of x plus pi over 2 when we graph this one we'll see it looks something like this it is the exact same thing as this function except it's shifted to the left pi over 2. this shift is known as phase shift putting it all together we can create a general uh sine function note that i'm using kind of like sine and wave interchangeably here so g of x equals a sine f times x plus theta note that changing theta is going to change our phase shift changing f is going to change our period no if you look at something like sine 2x it's going to have double the amount of peaks in the same amount of time so the period is going to be half in this case so we can use f to control our frequency and a is also the is going to control our amplitude so checking this you can use a grapher and if i look at like sine x squared plus y squared times 0.2 we can see we get a graph the 0.2 is clearly driving this amplitude if i add a small value like this we can start to see some animations with changing the phase of their sine function and finally if i multiply all this inside by two we can see double the amount of weights occur so using these three parameters phase shift kind of something that drives the frequency and the amplitude we can achieve a lot of different results okay now that we understand how this function works we can just use it directly so remember we have three parameters and so those parameters were t for the phase shift f for something that drives frequency and a for amplitude again these values that i'm using are just stuff that i found to work you can definitely tweak it for the look you're going for now i'm going to use the use callback hook again for performance um to basically just create use to create a function um that takes in an x and a z and outputs the corresponding y with the graph so i'm going to call this a graph use callback take in x and y z and return math dot sign so we're going to take our frequency multiplied by the distance not y the distance uh to the origin for x z uh add my phase shift and multiply down my t cool remember we also need a dependency list and this is really important because we want this graph to be re-memoized every time this changes so that's why we're putting these in here cool so if we um now we can call this graph right here and pass in our accent a z which will evaluate and get the corresponding y value before we push it into positions we should update this dependency list with graph so now we can see that these are actually um casted onto the the surface for the ripple that we got from this function now to animate this it's fairly simple we can use the use frame hook which will basically call the function past every frame and we just manipulate whatever variable we wanted to change in this case the phase shift i'm just using as an example and 15 is just arbitrary really it's first and this note will make the graph be updated so now the graph is the new graph that we want to follow so we want to update all the y values instead of like clearing the whole um position so what we need to do now is get the current um array of positions so how we're going to do that is using the ref so i'm going to say like a photograph and i'm going to create this ref up here up here and down here i'm going to just say buffer current and i'm going to get the array here so now i have all the current positions now i want to modify these i can use a very similar loop as before and now i just want to set the y values to the graph the new graph evaluated at x and z but to do that i need to know the corresponding index to that y so i can use an index to keep track of my position through the positions array and i can do that i'm just going to use i so sorry about java um i'm gonna just use i and since every position is going to go three values in the buffer array um i'm gonna increment this by three every time and remember the values are stored in x then y then z from up here so we can use i plus one remembering our zero indexing stuff and uh this should update all the y values with the new graph okay so now finally we need to make sure this updates so i'm going to say needs update equals true and now if i didn't well i did screw something up um circle i don't know how that got there use wrath just import this okay so we got our animation working great now let's uh mess around a bit i just want to add one thing i want to add camera controls um so if you look 3js is actually going to provide orbital controls um out of the box and it has a class to do this but unfortunately react3 fiber doesn't have out of the box support for it but that doesn't stop us because react 350 provides us a way to take some definition that you just find on the github that uses 3js and turn it into a jsx component that we can then use and the way we do this is using extend so first i'm going to import um that orbital controls that i just showed documentation so orbit controls and i'm going to import this from i think it's like js um yeah controls and orbit controls yeah and now i'm going to call extend on this orbit controls and this will basically create the appropriate jsx component uh with the same name except it's lower cable case so now i'm going to make my new component so i'll call it camera controls just so it doesn't clash and i'll call orbit tools i'll lower case this kind of remember that okay so how does the orbit controls actually work well if we inspect right here we'll see that what 3js is really doing or webgl rather it's drawing everything on a canvas so orbit controls is going to allow us to like hand the camera around and manipulate the camera using mouse um so to do that it needs a way to get the current camera and the dom element or the canvas and the scene we can actually do this by using the use three hook from react3 fiber and using this we can kind of extract if you will the camera and the dom element uh from u3 and so now that we have this we can pass this in as arguments and that's basically allows it lets all the controls know uh what camera and dom element to operate on i also want this i want my orbit controls to just make um the camera slightly rotate um if no mouse movement if no mouse input is being sent so i'm going to turn on auto rotate and set auto rotate speed to that something really small now we can finally animate this the reason we really need to animate this is we need to call update on this orbit controls component and every update it'll take then the mouse input and actually do the adjustments to the camera um so for animation we're going to use use frame so every frame we want to update it and so we're going to get some like pretend we have a controls breath and we can call update on this graph and i'm just going to make that rough right now um so we want to be able to update this orbit controls component and the only way to do that or i shouldn't say only but like one of the ways to do that is with the ref uh so control graph okay and so that should allow that should set the reference up and now we can use that reference in our use frame to update cool so you get this warning it's never defined it's defined but never used that's just because we should add camera controls [Music] and now we can see i can uh like move this around rotate it i can zoom in and out i can do all sorts of things so this shows how all the controls can really help okay now this is mostly done for the coding part but i also want to show you like with the system that we set up we can actually achieve a bunch of different results so if i just change the separation and count um bump the separation down and just make the count super high refresh we can see that it almost looks like a mesh in anything obviously you could try emulator mesh and like actually do the work but like this is a cheap way to do it that's not very performant but we're going to ignore that another thing that you can do is actually animate variables other than t so let's say i want to animate the amplitude so doing this we can see we get this cool effect of the waves kind of growing in like height um which i think looks kind of cool if i zoom out looks kind of like raindrops in a random pattern so yeah the point is there's a lot of different results you can achieve with the system that we've just set up and it's really up to you to tweak things and uh you know one thing you could do is instead of putting the points on a grid you could put them in a circle so that you don't have these corners that are kind of radially like awkward um because they're looking at ripples that others don't have there's a lot of stuff you can do thank you for watching this tutorial using react in 3gs the source code and other learning resources are going to be linked in the description so be sure to check that out and if you would like more content like this or have any questions please let me know in the comments see you in the next video [Music] you
Info
Channel: Alvan Caleb Arulandu
Views: 50,393
Rating: undefined out of 5
Keywords:
Id: wRmeFtRkF-8
Channel Id: undefined
Length: 29min 10sec (1750 seconds)
Published: Thu Apr 01 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.