4.26 Water And Volumetrics Preview | Inside Unreal

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
>>Amanda: Hey, folks! Wrapped up your shift with the free Factory Environment? Epic Games has collaborated with Unreal Engine Marketplace creator and creative studio SilverTM to bring an expansive 200 acre urban park to all Unreal Engine creators for free. The City Park Environment Collection contains everything you'd expect in a large outdoor environment, from winding walking trails to fun-filled playgrounds and quiet greens for the perfect picnic. Stop by the Marketplace to download the free collection. Are you interested in the emerging field of in-camera VFX as seen in the production of 'The Mandalorian', but not sure how to get started? Well you're in luck! We've created a new In-Camera VFX Example project that will guide you through the various steps you need to configure the system on a single machine--a setup you can then expand to multiple machines in your production deployment on stage. The project is accompanied by comprehensive step-by-step documentation to guide you through how the pieces all fit together. Download the new example project from the Learn Tab in the Epic Games launcher. Tossing over to our top weekly karma earners. Many thanks to these folks who are helping others on AnswerHub: Everynone, Expose, Tuerer, ClockworkOcean, Shadowriver, MMMarcis, PixelOmen, T_Sumisaki, DonBusso, and Chatouille. Now hopping over to our Community Spotlights: You won't get bored playing Let Them Trade, a minimalistic trading simulation in which you build a network of towns. As the king, expand your kingdom and secure trade routes between your towns, keeping supply and demand in balance. Wishlist Let Them Trade on Steam and keep an eye on their Twitter for development updates! Easily configure loading screens in your projects with this free Async Loading Screen Plugin. Automatically add new loading screens whenever you open a new level, use pre-designed UI layouts and default icons, or customize your own! Download the plugin for free on the Marketplace. And last up is the bright and fun, atmospheric adventure platformer Soria. Inspired by Scandinavian folklore, take part in a journey of friendship and found family that explores the bond between a fledgling gryphon and a young boy as they discover the secrets of a mysterious golden castle. Try out a demo of Soria on Itch.io today and follow them on Twitter @Polargryph! Thanks for watching this week's News and Community Spotlight. >>Victor: Hi, everyone, and welcome to Inside Unreal, a weekly show where we learn, explore, and celebrate everything Unreal. I'm your host, Victor Brodin, and my guest today is our Principal Technical Artist Ryan Brucks. Welcome to the show. >>Ryan: Hi, everybody. So today we wanted to take this opportunity to go ahead and talk about some of the upcoming water features that we've all been working on for Unreal Engine 4.26. So this is something that we've been working on for a while now, and we've kind of needed a water solution in Unreal for a number of years. But with Fortnite Chapter 2, that was a good opportunity for us to push on water. So what we did is we formed a strike team with a bunch of people from different initiatives or different teams-- a couple of people from rendering, a couple of people from tools, couple people from gameplay, me from tech art, along with some help from a few other individuals. And then what we did is we made the water system for Fortnite Chapter 2 based off of some GPU terrain modeling methods. And what that system allows you to do is define in the editor using splines where all the water features are going to go. And the goal of it was to have a unified surface as a result, so level designers wouldn't have to be going anymore and seaming up splines, connecting to lake meshes, and blending them out, and fiddling with terrain curves and all this stuff. We just basically wanted to be able to say, water goes here, and then have the level designers go in and place it. So we were able to get something working for Chapter 2, and now what we're trying to do is clean that up and deliver it to Unreal Engine at large so that everyone else can use it. It took a little bit longer than initially planned because we had to maintain backwards compatibility, all the while adding new things, but I think we're now at a state where it makes sense to start talking about it and showing it a little bit more. And of course, it's not actually accessible just yet, because this is in a plugin that's in the restricted part of the depot, but it will be available in the 4.26 preview stream. But that's probably still a few-- a couple months or at least out from now. So I'm going to go ahead and jump into the editor here. I'm going to start with some really simple test map style examples, and then we'll progressively build up from just basic water bodies and basic materials to a scene that's a little bit more indicative of what you could do with some actual art and materials, and showing a few of the ways that we use a system like this in Fortnite. So the first thing you see with the new water system-- we have these new water bodies in the world. This is a water body test map that's going to be with the water plugin just called Water Body-- Water Test Map. And you can see, with these water bodies, we now have an actor for each body. You can move it around, set it in the right height of the world, and it blends things together. And on these actors, there's a bunch of settings for things like how it's going to deform the world. So I guess I'm going to go ahead and just delete these things, and just place new ones, because it's a little bit more fun when you can see it from scratch. So this is just a blank landscape and a blank map, essentially. And then the Place Actors-- let me go ahead and search for water. And you'll see here we have a few different water types, like lake, ocean, river, island. We'll go ahead and drop down an ocean. So you'll see, what this does, is it actually creates a spline. And now, wherever we drag this thing, it's making the ocean level at the height of that actor. But it's also kind of flattening out an island around this terrain. And there's a number of things that go into how this carves the terrain. Let me go ahead make this a little bit smaller for now. So one thing I'd like to point out is what we have is a curve to control what ends up being the elevation of the water under the terrain or the water depth. So I'm going to go over here on the Water Body settings, under Terrain, Curve Settings. And notice that we have this curve here. And what this curve does is it actually represents the profile of that falloff. It's evaluated on the 0 to 1 range, so really just within this kind of box I've selected here. So it's kind of a nice S-curve by default. And then what you can do is control how it's mapped by using these settings here. So right now this ocean is set to be 2,000 centimeters, which is 20 meters deep. But we can go ahead and make it a little bit shallower so it's a little bit more obvious. And we have the falloffs set to be at about 80 meters. But you know, we can make the falloff really harsh. We can make it really long. Or we can kind of keep it back where we had it. So while we're looking at that curve, what we could actually do if we wanted to-- and this is something we end up tweaking a lot, but it's nice to know that it's there-- is-- let me try to make this a little bit more visible for you. When I'm placing these points in here, these are actually affecting the profile a lot. And it can be nonlinear. Not a very interesting example. But you can see that I've made kind of a concentric ring in the falloff there. So it really allows you to customize the profile of your water features. And then of course how these things work is they get added to a layer in the new Landscape > Edit Layers system. So you'll just end up with a new layer, called 'Water'. And then you can just tick it on and off as you want. And the neat thing about that, though, is you can still actually hand-paint inside that layer. And that allows you to make up for some of the things that are not controllable as per point attributes. And we'll go into some more detail about that in a little bit as well. But for example, let's go ahead and make a bigger brush here. >>Victor: Hey, Ryan, just a quick note that I'm getting heavily compressed and poor quality, unfortunately, through Discord right now. >>Ryan: That's too bad. It was really good when we first started the test. >>Victor: Mm-hmm, and yesterday as well. >>Ryan: So hopefully it improves. Do you think people can still at least see the programmer art content? I'm hoping it improves when I get to the actual art. >>Victor: Let's switch over to the editor. It's not even actually loading, where everything is still right now. I'm not sure what's going on. Hey, OK, let's do this real quick. Ryan, why don't you restart your stream real quick. >>Ryan: Sure. You mean just the screenshare? >>Victor: Yes. So stop streaming. And then I'm going to close that. Let's go ahead and get it back up again. >>Ryan: Sorry, folks. Bear with us. Any improvement? >>Victor: Yes, that is a lot better actually. All right, cool. Sorry, all. It looks good. Now let's everyone hold your thumbs and toes and whatever else you want to hold to make sure this stays up. Please, go ahead, Ryan. >>Ryan: No worries. All right, so what I'm trying to show here is that you can still go ahead and create another paint layer on top of Water to apply some custom changes, if you wanted to have the beach be a little bit softer, higher, or just really sculpted out. And what that's really doing is storing the delta between what was there and what was under. So if I unhide Water now, you'll still see I have a little bit of this kind of paint that I did before. But you can always just clear that, or you can go in and paint black to erase it and clear that. All right, so that's basically how the water depth is defined. It's defined using this curve. And there's some additional options for how far you want to offset this thing. You can tell it to have a big, flat area. But then there's also the component of how does it carve the rest of the terrain. You'll notice that we're kind of creating a little valley there. And that's basically using this setting called Water Heightmap Settings. And that's a struct that defines a bunch of different settings. We're going to go ahead and stick to the falloff settings for now. And you'll notice that right now this is in Angle mode. So as I move this thing down, it's essentially going to cut the rest of it to try and get an angle. And you can change that angle to be shallow, steep, or keep it as it was. Or you can also set it to Width mode, which in that case, now it's not going to be an angle. It's just going to be a constant falloff so that you can kind of control exactly how that's going to apply. And the Edge Offset here is kind of separate from the curve offset. So you really have a separate two-way control for your edge offsetting, which is pretty powerful. And below the basic falloff settings for things like width and angle, there's these effects areas. There's a little bit of blurring, because right now these shapes are actually generated on the distance field using jump flooding. So that process, when it's not done at a super high resolution, can pick up a little bit of alias-y artifacts. If I turn off blurring-- it's on, by default, at 2. And that pretty much fixes it. But you can adjust it as needed to kind of smooth out the result. But you'll notice that that's blurring the actual distance field result, not the height result. So you'll notice that no matter how blurry I set that, it's not going to blur this top edge. That's actually something different. That's actually this smooth blending step here. So I can go ahead and change Smooth Blending between inner and outer separately. So Inner Smooth smooths the inside, as you can see there. Then an Outer Smooth smooths the outer side. So you really have separate control over the plateauing. There's also some kernel noise options. But I'm going to refrain from showing those right now because they're a little bit broken with water. They're more of a landmass feature. But you're free to add in noise just by perturbing your points, doing whatever. I'm going to go ahead and remove some of that crazy edge offsetting. And then we can go ahead and recreate a little bit of that scene we had here before. So now we have an ocean. Let me go ahead and drop in-- how about a lake? So one thing that is a limitation with the system right now is it will not blend, say, a lake with an ocean. So if you try to put a lake overlapping an ocean, what you're going to get is something a little weird. But what you can do is place them at the same z-height. And then you could use the material to fade in some custom stuff there. So we do this in Fortnite a lot, is for the swamp, what we actually do is we have a lake there at sea level. And then we replace the material on it. And it has some sort of mask that colorizes just that region. But generally, when you're placing a lake, you have these part of the terrain, and then probably connected with a river. So let's go ahead and do that. So one thing to note about rivers is, currently, they are explicit. They won't try to automatically connect to the height of the ocean or lake. So it's good to be aware of the heights that you're going to put your lakes at. So I'm just going to go ahead and put that one to zero and ocean at 768, just so that now, when I drag in this river you'll notice the river dragged in is zero. So it's already blended in. And we do get automatic blending of the terrain height between water and rivers. You'll notice that the river blends into the lower depth of the lake. And the same thing will happen with the ocean once we set it at the correct height. It's just that here we have the river too high. Let me go ahead and make this ocean bigger so I have more room to play with. OK, so now that we have these spline points, let me go ahead and point out-- right now, not all of the defaults on these new actors are what they will be in the end. We expect we will probably include things like a wider falloff default, for example. Because there are certain limitations with the way that the water is rendered. And we'll get to that in a little bit. Let me go ahead and set that river down to 768. Oops. I did that wrong. I moved the whole thing. What I meant to do was, in Selected Points, when you select your water spline, you can now go down here, under Water. This shows all of the water properties such as depth, velocity, and width. But you can also adjust the point directly. So I'm just going to go ahead and set these river points to be exactly at the ocean height. So now we get an automatic blend. And another thing you probably want to do is set the Velocity at the endpoint to zero. And it'll automatically fade out back to whatever the river velocity was. And depending on the shape of each specific river, you might want to adjust things like the z-offsetting. In this case, I can tell we're getting a little bit of this river bleeding over here. We can offset the z-height of the terrain a little bit to fix that. And then the other thing that's causing this is I know, from experience, that the tangents aren't flat. So this river is actually going downhill. What we need to do there is just simply select the arrive and leave tangents, and set them to zero. And you can tell we overshot our z-offset a little bit now. Let me go ahead and reduce that. There we go. So yeah, with adjusting things like the z-offset of the terrain on the water body, you can get a pretty smooth transition. And then going up here, let me go ahead and select this point. Set its velocity to zero as well. And this is actually a good time to show visualizers, which if you saw my Unreal Fest/ was a GDC talk, you've already seen this. But what we can do now is just show these visualizers for width and depth. And start to just play with them directly in the editor-- or sorry, directly on the component without selecting things and typing separate values. Let me just come in and kind of do what feels right. We have some really deep, crazy spots. But it's not quite what we want. OK, and then of course also there's velocity, which acts very similarly. I don't have foam yet hooked up inside of this shader but what it's doing is it's changing the flow map strength. So as you increase velocity you will see the flow map increase. But by the time we ship this, we do plan to add the foam back. And that's what Fortnite does, is it basically has, in the material, it reads this velocity from the water body, and then uses it to apply some masking for some foam that shows up. And we can actually see what that looks like now. And I guess I'll back up a step here and mention that how these guys are all rendered. So these are all separate actors. As we mentioned, they're all just different water bodies. But they're all being rendered by the same actor. So in the world now you'll see we've got a pretty simple scene. We've got our landscape, and basic lights, and our water bodies. But then we have this water mesh actor, and also the water brush actor. We can ignore that guy. You don't really need to think about him. The water mesh actor, though, this is actually what's doing the final rendering for the water. So we can go ahead and jump into Wireframe mode. And we can see that, how it refines based on distance. And we have this one unified surface that's kind of connecting everything. So even though these water bodies are separate actors, what they're doing is they're communicating their material back to the main water system or the water actor. And we can actually-- I'm looking for the WaterMeshShowTileBounds one. That will show you how this water mesh actually gets broken up behind the scenes. So blue is ocean tiles, red is river, green is lake, and then yellow and purple are the transitions. And that's important when we start to talk about things like shader complexity. Because we do extra things like the flow maps on the river. But we don't want to have to pay for the flow map out in the ocean. So what we do is we restrict the flow map to being only on the river or the transition tile. And then what we do is we mask out using velocity. So technically you always want rivers to have some velocity. You could actually cause bugs if you selected all river points and set no velocity. And I'll show you why that is. Let me go ahead and set Velocity on these points to zero. And what that does, we're going to now see a little bit of a seam between these two, where the river and lake connect. And that's because what we're really doing is using velocity, behind the scenes, as kind of a way to blend between the different waves of the different water bodies. But before we go into waves, which I think will be the next thing that we talk about after this, I'd like to talk just briefly about the water mesh and the type of shading that it uses with the new single-layer water shading model. So I'm going to go ahead and hide this guy. And what I have here is just a water plane. So going back in time to when you had to place a static mesh for a river or a lake, and then you realized, oh god, it doesn't fit. I have to go in and make a custom mesh. So we're going to use this just to kind of show a really basic single-layer water material. So this material here is similar to the ocean one we were already looking at, but it's kind of bare bones. So this is the basic single-layer water. You can see here this is the Material Attributes pin. And all that's really going in here is we have this WaterTextureSurface node. What this really is is this acts as both the far-away normal map replacement-- so we're mostly rendering Gerstner waves for the normal map of the water. But far away, they get too alias-y. So we fall back to just simple painting textures. But it also reuses that texture lookup to apply a very faint detail texture near the camera as well. And this is all pretty standard stuff. Most of it's defined by this Water Attributes, which is pretty much just setting the specular, the roughness, the refraction. And there's a little bit of kind of the fading of the refraction with depth in here as well. But the Water Attributes is meant to be-- this is just the very basic constants of water, no textures or anything like that. And then the texture has the breakup. And then down here is where we get to the actual meat of what's new with single-layer water. So obviously this material is using single material attribute pin. But normally this is the material input. But single-layer water adds this additional single-layer water material input. And what that does is it allows you to specify your scattering and your absorption coefficients. So that's really what causes the blue coloration and the nice scattering inside of the water. And this new shading model has actually been in the engine since 4.24. It was written originally by Sebastian Hilaire. And it was done for the same Fortnite water initiative that was mentioned previously. But the reason it went in 4.24 is we don't have a way for shading models to be easily put in a plugin. So it kind of just had to be. But we hadn't really talked about it too much yet. But I feel like it might be worth spending a few minutes to talk about what exactly are these scattering and absorption coefficients before messing with them. So scattering, I feel, is more obvious. It's basically how much stuff like particulates and fog is in the water that's going to cause scattering or a brighter glow in the water. And absorption is more, how quickly does light get absorbed by the water? How quickly does it lose its energy? And if we want to figure out the correct way to apply this, we can actually jump over to Wikipedia to get kind of an idea of what this means. So absorption coefficients basically define, for the visible light spectrum, how much each frequency is going to be absorbed by water. And you don't really need to get too deep into the math here. But I'm just using this to point out that what you can do is look at a chart like this, which has the absorption spectrum, and you can actually just kind of pick some RGB colors, and grab the values from this chart, and then plug them in manually. So we're going to go ahead and do a quick attempt at that, really quick. But the first thing to point out is the absorption coefficients are actually specified using this weird thing called reciprocal meters. That's this m negative 1 thing. And actually it just means it's kind of 1/x. And it's just done that way because the units are in really small values. But you can also convert between an absorption coefficient and an absorption length. And they're the same thing, they're just opposite of each other. But personally I find it a little bit more clear to think in terms of the absorption length. So you can actually plug in either absorption length or an absorption coefficient into the shading model here. You just have to be aware of that 1 minus x. So what I've done here is actually plugged in the values for RGB from that chart, manually. And the way that I've done that-- let's take for example, red. I put 0.6. And how did I come up with that? On here, we can look. 700 nanometers is roughly the color red. And so what we're doing here is we're not a full-spectrum renderer. We're rendering in RGB color, which means we have one channel for red, green, and blue. So really what we do is we pick an absorption coefficient for each of those. So we just need to find the closest value that we can for each of those. So 700 nanometers is a fairly good representation of red. And this is a log chart. So we can see that we start at 0.1, 0.2, 0.3, 0.4, 0.5, 0.6. So at 700 nanometers, we have a value 0.6 on there. And what that really means is that-- how they define this is-- what it really means is that, after that length, you will have 1/e left of the water. And that's important to point out, is that because this is exponential falloff, the intensity of light will never go to zero, it'll just go to almost zero. But what we can do is use this to plug in our values in a little bit more intuitive way. So let's just continue that with-- green is about like 555. So I basically just ballparked around here. It's about 0.05. And then for blue, somewhere around 450 to 420, depending-- and you could do a lot of research. And there's papers on picking the correct weighted spectrum tables. But it's probably not worth it when you're going to be tweaking the value a little bit anyways. But it comes down to about 0.005 roughly, somewhere down in this region. And so I've just put that in here. And I've divided it by 100, because this is reciprocal meters. But Unreal takes centimeters. So that's with the physical values for water absorption from Wikipedia. And it gives a pretty decent falloff. You can see. And if you want to modify it from there, what you can do is just add a multiply to the alpha channel. So this is something I tend to do in a lot of my shaders, where you have a very specific color going on. I'll use the alpha as a multiply, just so that you get a convenient relative thing. And this is especially important in computer graphics, where this scale may not always be locked down. But now we can basically get different varying scales of water absorption by changing that one scalar. And of course turning on scatter, and you can see it's basically the fog inside the wire. But now I'm going to go back to what I was mentioning before about the absorption length as well. So you'll notice that, in here, it's pretty much just-- since it's reciprocal meters, to go to an absorption length, we just have to do 1/x. So what that means is, instead of writing this out in terms of how quickly light is absorbed, which is, I feel, not that intuitive, what you can do instead is write in terms of how far light goes until you have 1/e left. So how far does most blue go? And if we want to do that, we're just going to invert this. So I'm going to put 1/x on the color. But before I pass it out, I'm just going to straight-up convert my existing values by putting 1/x in each channel. So now 1/x for green, 1/x for red. And so now we have these values. And so notice that this is now a little bit more intuitive if I start to describe this to you this way. Let me go ahead and compile this shader. There we go. And as promised, it looks exactly like it did before we converted it. But now it's a little bit more intuitive. And we're basically saying, OK, most of the light goes 200 meters before it's been absorbed. Most of the red light goes 1.6 meters before it's absorbed. We can set that to 20. And then we're just going to get purple water because we made the red light go just as far. If we really want to see that red spread in action, we can get closer to the edge. And probably a good idea-- let me move this outwards a little bit less, have a steep slope. There we go. So you'll see where you have a less-steep value. That's where you're really going to notice the difference between your red absorption. And of course you don't have to stick to that value from there. I would just suggest to use that as a starting point. But that's really where the physical basis of the light absorption comes from. And that's what the coefficients versus the attenuation length means. I go ahead and just do it as attenuation length because I find it to be a little bit more easy to understand. OK, now hopefully I haven't bored everybody too much by going off on a tangent about the water scattering. >>Victor: That was great stuff. >>Ryan: Going to go ahead and delete this simple mesh that we had and go back to the water mesh actor. And now we're going to think about waves for a little bit. OK, so I guess the best place to look at waves is the ocean, right? So we'll select the ocean. And we're going to go ahead down here to Wave. Let me make this a little bit more readable. OK, so now you'll notice, on a water body, we have this Wave setting, and we have Wave Source. And right now this is set to Gerstner Wave Asset Reference. And that actually points to the content browser. This is something new that one of the rendering engineers, Kevin Ortegren, has been working on, that basically waves are now just an asset that you can place in the content browser. And it's extremely flexible in the sense that the generator itself can be changed to different classes of generators. Currently we just have the simple spectrum generator, which is based on the one for Fortnite, where essentially you set how many waves you want, what is the Min Wavelength, Max Wavelength, the smallest wave and the biggest wave, how random the directions are, and how steep they are. So you specify just these values, and what it does is it generates, and randomizes, and gives you all the waves. And this being an asset is useful, because now we don't have to worry about what if two different maps are supposed to be in the same locale, and you have to keep on manually copying waves back. Or two water bodies are supposed to be shared, like the lake example, you want to make sure that you reference the same wave asset on them so that you're never going to have a wave mismatch between the different materials. But it also can be used even just as a generator in place. So this is really powerful, that you can just set randomly-- I want to take these waves and put them right on this asset. And now I have a generator that just lives on this water body. And we can go ahead and start messing with the wavelengths. We'll go ahead and first say the Num Waves. Now we can increase this point in time-- currently all the way up to 32. Although it's worth pointing out that, when this actually ships, we're not planning on having a max. So that should be cool. It'll be something you'll have to just figure out, kind of per platform, what your affordable max is. In theory, you could have thousands of waves. I'm going to go ahead and make this look a little bit better. These waves are a little bit too big. So changing the Min Wavelength is saying, how short do I want the smallest wave to be. And then the falloff is the distribution. So with 1 we have a linear distribution. With 2 we're saying more and more of the small wave. And if we go too high, eventually it just turns to all noise. And we don't want that. So usually a value around 4 or 3 could be decent. And also something to point out-- notice we're getting these waves changing drastically a lot. On each water body is this Wave Attenuation Water Depth. And that's used to specify how quickly do the waves fade away as they reach the shore. So in this case, since we made our ocean not very deep, that's why we're not seeing much. Let me go ahead back to these curve settings. Yeah, this is only 2.5 meters deep. What is that, like 10 feet or something? So it's a really shallow, like a Bora Bora type thing. If we wanted to make this a little bit better, more realistic, we'll increase the depth. And then we'll also increase the width. Sorry, brain fart. So you can tell that the falloff width that you do under the terrain, while it affects the terrain underneath, it's also having just as big of an effect on your waves. So notice before, when I made the waves a little bit too intense because I had that falloff too high, this is an example of a bad default that we'll try to fix. And if I set this to 1, we're going to have absolutely no masking. And then we get these gigantic waves all the way up to shore-- not very good. So obviously we don't want to do that. The important thing to note about the wave attenuation factor is that it's actually affecting gameplay as well. So that's why it kind of had to be unified as part of the system. Because when you have a character out here, we need to know-- say, there could be a wave here that's gigantic, a tidal wave, but it's being masked out. So the gameplay needs to know to mask out this wave in this location due to the water depth. So it is handy to be able to tweak that curved water body as well. Let's go ahead, just for the heck of it, I'm going to put a really big wave. So that's the cool thing about having 32 waves now, is you can put this one really big wave, and it kind of breaks up our tiling without having to worry. I'm going to go ahead and look back at the material real quick again. Because you'll notice that, before, I mentioned that detail texture. And it's starting to be a problem. So let's take a look. All righty, so I mentioned before-- or I went over that very simple single-layer water for just the texture-based material. But now this is the single-layer water material that we use for all water surfaces. So lake, ocean, rivers, and transitions, they all use this. So we've tried to get this material down to be fairly cleaned up. Make it a little bit closer. And we'll go through this from left to right, one at a time, to show what's going on here. And I'll stop when I get to the problem part. So first and foremost, here's the new function for computing the Gerstner waves. This basically is a material function. But it actually bases on a new code library. And it's able to get all of the waves with a loop. So there's no more giant node mess of a bunch of functions to get all these different waves. There's no more material parameter collection. And Kevin also was able to make the water data all exist in this new water buffer data. It's an unstructured buffer that lives behind the scenes. So you can just access it at any shader by just telling it what's the water body index. So you'll notice that, when we place this in a material, we don't really need to say ""water body index"" on it. And the reason why is because, inside of the default function, the default input is set to this Water Body Index From Tile Instance. And this calls this code function that Kevin wrote that just gives you what the index of that water body is and allows it to figure out the right parameters. But it also means, if you're in some other random material, all you have to do to get the waves on this material to match, you go to Wave on your water body, and you'll see-- hang on. Watch me forget where this is. There it is. You go to Water on your water body. And it's under Water Body Index. So in this case, ocean is 0, lake is 1, river is 2. But I want to point out, rivers do not actually have waves. We just set them to have zero waves. And the reason for that is because it would be expensive for rivers to have waves because we'd have to blend between two full wave sets on the transition tiles. So what we do is we kind of treat them as an airlock. And also, rivers kind of have standing waves in real life anyways. They don't tend to have choppy waves like the ocean. So it didn't make sense to want to put Gerstner waves onto them. There's some sort of seam happening up there. I'm not sure what that is. Hmm. Probably related to something with the depth or something. But maybe figure that out later. All right, so where were we? So with the material-- go back there-- excuse me. OK, so we have this Gerstner wave function. Great. It gives you the waves. And there's this concept in here of material attributes called wave attributes. It's not really a proper structure or anything. It really is just setting material attribute parameters. It sets material attributes for normal and it sets them for world position offset. And the reason that that's pretty handy is when we did this for Fortnite initially, what we were doing was passing normal and world position separately everywhere. And it was just always a pain having these two pins blending. So what we do now is we pass wave attributes in a bunch of different functions that need to do blending of waves, just know to take these normal and WPO pins and manipulate them. So for example, velocity wave mask, this is how it knows how to mask out the waves using the river velocity. So this is how transitions are able to work. So there's a couple of different boxes to opt in to the transition behavior. And then, on the river, when you go down to Water, you'll get these two slots, River to Lake Transition, River to Ocean Transition. And if we pop one of those open, they don't have anything set except for they say Enable Lake Transition. And that brings over those extra instructions for the blending of both behaviors that I was talking about. OK, making some progress. So this is basically how waves are handled for all of the different permutations. Here is how the water texture surface is blended to. And I mentioned before I wasn't happy with the results. And that's because it's kind of content-dependent. For a real analytical solution, you need something like lean mapping, which analytically evaluated the waves to figure out the right distance to fade. But really what we usually do here is just kind of specify what's the start distance, and what's the fade distance for how long we fade to the normal map plane. So obviously the further you go with your actual waves, the better it's going to look. But you're fighting aliasing and noise. So I can go ahead and increase my screen percentage. And for getting realistic water rendering, that makes a big difference. And now we're able to render waves out to a pretty far distance. So that's just something to keep in mind as you're tweaking is, depending on the type of waves you have, if I had really small, noisy waves, I would probably fade to the normal map a lot quicker. Versus big waves like this, we push it out a lot further. And of course water waves are per water body. So we can now come on here and give this lake its own waves. Let me make them a little smaller. There we go. Oh, I know what's causing the seam. So you're intended to have the velocity faded out by the time you get to the end of the river. So I think if we just select this point here, and try velocity of 0-- there we go. So that's what I was mentioning before. We kind of use the velocity to know where the river is. Because we don't have any sort of spatial mapping inside the river. All these tiles are actually the same everywhere. And in fact, we'll show a little bit more about the material as we step through now. So what happens next is what we do is we set wave attributes, which is just combining the wave, normal and world position with these water attributes, which as before-- we looked at this-- was just the basic constants for water, specular, and roughness. This Water_Underside, this is a little function that currently is enabling the water to also look somewhat correct from the bottom side. So you'll notice, if we go under the water, I don't have post-processing in here. In dev rendering right now, the sky is not showing underwater. So the sky atmosphere doesn't show underwater. So that's why it's black. But if you put a sky dome, you will see out of the water now. And you'll see we have kind of an attempt at some refraction. I'll show this later in a more realistic map. But that function there is what's handling basically-- does not want to maximize. So basically what it's doing is it's accessing the Two-Sided Sign. And it's changing some of the parameters like speculative opacity and refraction on the bottom side, just to behave a little bit nicer. Because we're actually using the Pixel Normal Offset refraction mode. You could use Index Of Refraction, but we found that, for water, it just doesn't really work very well. Because refraction is a screen space effect, you really need it to stay on screen. So Pixel Normal Offset refraction still gives better results, even though it's less correct, because it keeps the refraction on screen. But it does mean you have to spend some time doing some little corrections for the underside. So this just applies, so far, waves, textures, and attributes. And then from here on what we're doing is we're just adding behavior as is needed. So river flow maps exist as their own kind of function. The river material, which is an instance of this one, just checks this box. So the ocean, by default, is not paying any cost for the river. Beach foam is very similar. Here's another function for beach foam, where by default it's bypassed. So there's nothing there. And it's not on by default. So you need to make a material instance for ocean. So we see here there's an ocean instance. If we look at this guy. By and large, all the settings are blank except for Enable Ocean Foam. And the reason I like to keep all of the parameters blank in most of these is so that you can decide to use inheritance, and then just change it at the top level without having all the instances have different values. So beach foam-- now, notice that there's this scattering pin which comes out. Because you can't really add scattering to the water material in the Water Attributes. And the reason for that is-- that's weird. It didn't want to go back to the regular pin. Oh, I accidentally turned off [INAUDIBLE]. Sorry. So scattering doesn't exist down here. Scattering exists on the single-layer water material node. So what that means is if you want something to pass between both parts of the material, regular shading and the scattering, you have to pass them down to this branch down here. So here again is this single layer water which we saw from this [INAUDIBLE] before. And it has, like before, our absorption, which I'm doing as the length like I showed before. Scattering here is basically also adding in scattering from the foam. So you'll see we have another option. If there's no foam, it just adds zero. If there is foam, it's going to add the foam scattering mask times the foam value. So you get a little bit more scattering wherever there's foam, which is nice. And then again, this Water_Underside, again-- I mentioned this before-- it also has to affect the scattering so we basically have scattering in, scattering out. And the reason why we want to do that is because when we're above the water, what it's actually doing is it's using the length between the pixel under the water and the water itself to determine that depth. And then it's kind of fogging and coloring based on that depth. But if you go underwater, if you don't mask that out, what would actually happen is it would fog based on the distance to the sky. So you would just get like solid blue above water. [COUGHS] Excuse me. I feel like I'm losing my voice. So we just set those values to zero when you're underwater. And that's what lets you still see through the ocean. And I'll talk about post-processing later after this. It's a little bit busted here. But there are some solutions. OK, so we're almost done with the basic water material. All right, so we got to the foam. Next we have another function for fluid simulation. We'll be showing that in a little bit as well. But this is a function that maps the additional displacement and normal map that comes from the optional fluid simulation. So that's how it's able to get little ripples, and bullet impacts, and things like that. This WaterOpacityMaskFromDepth, this one isn't actually really necessary. But what it's doing is it makes the final material a mask shader. And we do that kind of purely just for editor polish purposes. So you'll notice, when I select this right now, you'll see the outline of the water more or less. If I were to go into this function, WaterOpacityMaskFromDepth, and set this minimum water depth for the mask thing to be really high, now I've disabled that function. So now we're seeing the water in its raw glory. So as we looked at before with these water tiles, where-- so these tiles aren't lying. The water actually is just squares. and what we do is we use the data of the water texture to mask out the parts that aren't needed. And additionally, we set the absorption and scattering to zero when we're not doing the masking, so another way to do that is, instead of masking using opacity, what you can do is mask-- just the way that this is doing scattering here, you could also mask using this water depth. And this is a good time to show-- throughout all of these functions, there are a couple functions that are kind of universal water functions being used inside. And probably the most common and important one is WaterBodyData. And what this function does is it returns all of the water information from the water terrain system. So it has all of the relevant things that you might need, such as the Water Velocity, the Water Z Height, the Water Depth and the Water Depth Clamped. And that's important to notice, that water depth can be negative, which is useful in some cases. And then the Terrain Z Height. So because we know the water depth by the texture, we can mask out that region. And I'll jump into another example of how that Water Body function is used. Go to River Flowmaps. So this is still a relatively-- sorry. Having two-monitor troubles here. OK, so this is still a relatively simple Flowmap function. And you'll see what we have is the basic core for it again is WaterBodyData. As before, if we open WaterBodyData, this function, under the hood, has reference to this water velocity height, which is this global texture. And it has all this global information about the landscape. And it knows basically how to decode this data to get it and so that you can just use it in world space. And pretty much all of the materials that go through the water system have access to these things. And if you were to look at, say, you can go down, under Debug, on a water body, and pull up its material instance dynamic. And that'll give you an idea of what's being set on the water body. So you see we're setting the water body index, which doesn't technically need to be done, because it's also getting it by the tile. But we're also setting it because of some legacy stuff. This is the water velocity and height, which is the texture that comes from the terrain system. So I can go ahead and look at it there. And that's being assigned to all those rendering materials. And that's as you edit the water, you'll see that thing change every so often. OK, so I feel like we've done a decent job of showing the basic setup of all this stuff. I don't know. Victor, you still following along with me here? How are you feeling on this? Do you think that we've showed the basic shader enough on this point? >>Victor: Yeah, I think the material is good. And just to give you an update, we've had pretty stable quality throughout. Seen a couple of dips. But in general, we've been able to see everything. So please continue. >>Ryan: All right, so now that I've shown-- this is kind of the master material which we would say it's a good starting point from where you would start to add your more game-specific functionality. We'll go ahead and look in, a little bit, what are these different functions for, say, the foam, and the fluid sim, and the height map. Oh, I knew I missed one-- height map. So just to really quick-- what we actually do is use the Water Z Height here. And we can form the whole mesh to the water height, and kind of neutralize its base mesh position. So you can see here, it takes water height and then it subtracts absolute world position. So this means it doesn't matter where the original verts were. It will end up just positioning them to the right height. But this is also why we have challenges in coding water height, and dilating, and water bleeding above edges, and things like that. There are some considerations worth mentioning. But the reason it's necessary is because we don't actually save the water geometry. This is actually-- as you can tell from looking at the wireframe, it's instanced quads just being used everywhere. It's not like this is actually a high-res mesh. So we don't actually store, anywhere, hey, the height of the water right here is this. The only place we do store it is in that texture. So we mapped as well. OK, so now we've showed more or less most of the features for the really basic terrain stuff. Yeah, I guess we could go ahead and show some fluids and stuff. How are we going for time? Oh wow, we've already been going here almost an hour. >>Victor: An hour. But this is great. So why don't we just continue as long as you want to go. >>Ryan: All righty. So I've got a fluid sim terrain map here. And we'll start with kind of a simpler one, and then we'll build it up from there. And this one here, I've set the waves to be almost off so you can see a little bit more clearly just the detail normal texture. So as we're up close here, this is actually that detail normal. In fact, let's go ahead and just-- You can turn that off if you want. But I keep it on there just as a really subtle thing. I'm going to go ahead and turn it off for now since we're looking at the fluid sim. And what we have here, this is just the terrain with a lake and a river. And we have the fluid sim actor, and then we have a couple forces that are ready to do stuff. But I'll point out that-- this guy is turned off right now, so he won't do anything yet-- but when I press Simulate, I've got an impulse over here. So this is set to do an impulse. Right now it's set to do it every two seconds. And you can see we have some settings for Radius, and Strength, and things like that. So I can move it around. And I can also select this skeletal mesh guy. And we'll go ahead turn on his Strength. So now, as he's walking around, he's making some ripples. And you'll notice that the ripples are affecting the terrain as well. Although it looks like we've got a little bit of a bug on here. Hang on. All right, so first let's look at the actual settings for the fluid sim. So the fluid simulation is, right now, this Blueprint that lives in the world. The settings for it are under Simulation. You can optionally show the mesh just as a debug. But it's meant to just render with the existing water. There are some different modes for changing the solver. Right now, it's in Shallow Water mode, which is much more experimental. Fortnite shipped using the Ripple Solver version-- we'll show some differences there in a little bit. But the main difference is that ripple solvers do not use velocity. What they do is they add some water height, and then simulate the spreading of a wave from there. So all the waves kind of travel at the same linear speed. But you can still get some pretty nice results. So I'm setting it to Ripple Solver now. And then I'm going to go ahead and select this repeating force. And I'm going to decrease the strength. It's worth pointing out that the strength of the forces is pretty different between the two Sims. And that's just a factor of the fact that shallow water is much more physical. It takes things like terrain height into account. But you can see here-- this is with shallow water-- it's fairly simple and it's fairly robust. You can't really break shallow water. Sorry, let me restart that sentence. This is using the Ripple Solver. And the Ripple Solver is fairly robust compared to the Shallow Water Solver. And what I mean by that is it doesn't matter how big of a force you put inside of a ripple solver, it will never break down. Like even if we say 5,000, it's just going to look a little noisy. But it's never going to completely break down. But whereas with shallow water, it's much more advanced and it uses velocity. So it actually advects. And if the velocity gets too high relative to the resolution or the time step, it can kind of explode. But it looks a lot more realistic. So first I'll show a little bit more with the ripple, because that's what Fortnite is using. Go ahead and turn this back down a little bit. So you see here we have a movement component. The basic types of forces are -- there are impulse forces, which impulse forces are basically just instantaneous trigger things. And I'll go ahead and show the Blueprint for this. So basically you just call this Apply Fluid Force Impulse on the sim. And there's this Impulse Settings struct which defines all the settings for the impulse. So first what we do is we specify the actor location on the impulse. And then we pass it in. And you can change all the impulse settings here in the defaults or in the world. So this is, for example, how bullet impacts in Fortnite are done. Whenever a bullet weapon traces against a water body, it just calls this Apply Fluid Impulse at that hit location. And then you pass it in. So there's a couple of cool things kind of built into that, though. There's also this option down here, this Timed Water Drop Splashes that you can enable, which we use in Fortnite for the bullets as well, which kind of means, after your initial impulse, you have a secondary effect that, after a delay-- so here there's a 1 second offset. And this has a lag time of 3. We'll get these little raindrops. So first I'll play it again with it off. I'm going to set Impulse Every N Seconds to 0. So we're just going to be looking at this thing when I press it. So now we press Trigger. Set that a little bit less. There we go. So now I'm going to check this Enable Water Drops Effect. So after 1 second, we should get a 3-second splash. But the splash is way too intense. So let's go to something-- There we go. We saw we got a little raindrop there. And you reduce the number of drops. There we go. It's going to be a little more obvious. So effectively you can say how dense of raindrops. So I'll lower that down so we have less. And this should be a little bit more obvious. Maybe reduce Strength even more. So that's just kind of a neat way, when you have something like a bullet impulse, you're probably going to have an effect play that does a splash. And this lets you, say, time that splash so that it looks like it causes fluid when it's really not. So you might want to say make that much shorter. Maybe you're upset because it's a really short lifetime. I don't like how that's looking. There we go. So you can do quite a lot with this. So before the impulse types, this is Impulse, then there are Dynamic Forces, which this skeletal mesh guy is an example. And if you go to-- I feel like I should have led with this, but where we're working out of is in the Water plugin. So the various different categories here-- we tried to break it down-- fluid simulation is mostly under FluidSimulation, materials are mostly under WaterSurface, so all the water material stuff we looked at prior. We're going to go to FluidSimulation right now. Under Blueprints, this is where the main fluid sim is. Under Examples-- this is everything we're looking at right now-- it has an example for the impulse, the mesh, and the dynamic force. So the main difference between these things are a dynamic force can actually calculate some vectors based off of something like a skeleton. Let me close down these windows real quick. Here we go. OK, so these are examples of the dynamic fluid force. And you'll notice here that, on Force Type, you can choose different options. There is Component Location and there's Skeletal Mesh Sockets. So these are both actually the same class. It's just that the Component-only one is set to just be component-only. In fact, in this other Blueprint, I'm just making a fluid force dynamic and setting it to Component directly, and then just setting all these things. So they're just different Blueprints of this same type of example, but just different types. But we could, for example, on this skeletal mesh one, set it to use Component Location. Or we should be able to. Oh, you know what? I think we're not calling-- that didn't work. That should, in theory, work. It needs to have really low-- >>Victor: Live demonstration, everyone. >>Ryan: All right, so one thing that's making this pretty noisy right here-- we're going to go ahead and look at the material for the force. So that's how this works is you specify the type, the radius. And then, for skeletal mesh, what it uses is these sockets. So these socket mappings actually just use sockets on the mesh. Going to go ahead and select the mesh, just to show those. Character > Bones > Sockets. There we go. So these are the sockets that I'm using. Basically it's like a stick figure. We just have kind of feet, to shins, to pelvis. Or actually not even pelvis. We connect the knees up to the spine. So we basically just draw a really basic stick figure guy. A cool way to show this could be-- here, let me do this. And I will set him to use no animation. OK, so now I'm going to simulate. And going to open up his material. So the reason that it's actually getting so noisy is because we're actually doing collisions on the skeletal mesh. I'm going to turn that off for a second. So now we're just getting the motion. So we can actually look at that too, and see the stick figure. So I'm going to select the Fluid Sim. I'm going to go under Debug, Forces. And I showed this a little bit during my Unreal Fest presentation. This guy is so tiny. I don't know if you guys can see, but there's a little stick guy. That is our guy. Let's make it a lot bigger. So the reason it's not always showing is because we're really adding a velocity force. So sometimes it's negative. But you can see kind of the stick figure shape that we derive from. Although it is worth pointing out, whether or not you have collisions enabled really depends on what your intended behavior is. So I'm going to go ahead and turn that collision back on in here, which is just whether or not you output the alpha channel. And the way that this works is these are actually rendering these capsules. And it's passing in the socket points to the force. So you'll see, now that I have the collision back on, when we have the impulse, you actually get the-- let me increase the radius-- We actually get the ripples bouncing off of the character. It's a little bit more obvious when I make it-- so he's now acting as an obstacle. See that? When the wave gets there, it bounces back off. And that's purely because of setting that there. We can do the same thing for this component one. We can click on his material. And currently it's set to not have an opacity. We have this mask here we can apply. Go ahead and do that now. I need to set the blend mode to Alpha Composite. Bear with me. There we go. So you see you have choices, when you're defining the obstacles for these guys, how you want them to behave. All right, so that is the Ripple Solver. And it's also-- let's also really quick show the river velocity. So I don't think I have a velocity here yet. But we'll go ahead and select these points. And we'll say-- there we go. And also there's no guarantee that velocity is always downhill. So in this case, it's negative. It's based on the orientation of the actual spline. So it could be negative or positive. Now, if I put that-- actually, I'm not going to put that. I'm going to to put the impulse-- so we should see the impulse get advected downstream as well. A little bit too fast. So you control that velocity. So now we might want to reduce that. And it's also controllable in the sim as well. It's probably set a little high. I feel a crash. No, we lucked out. Just reducing these down. I think we just had that value set to be something that was for Fortnite. So notice that we still have the appearance of water flowing downhill, even though what we're really doing is simulating this all in a flat space. So the Ripple Solver doesn't have a concept of terrain height but it does have a concept of terrain edge. So when a ripple hits the terrain, it will reflect. That's more noticeable when we turn down dampening. Let's go ahead and increase travel speed. And we'll set damping to zero for now, even though it's going to go a little crazy. So without damping, you'll see much more obvious reflections. So the reason you want damping, obviously, is this is just going to bounce, and bounce, and bounce, pretty much forever, and just get crazier. Let me go ahead and set -- I had it at 0.01. We might want just a little bit. And damping acts very differently with shallow water, just to mention. So now you see just a little bit of dampening, the ripples have faded a little bit by the time they get to the other side. But they still have some persistence. So this is basically the configuration for the fluids that we use in Fortnite right now. And then the main difference being, for that foam channel-- and I don't know if you saw, a moment ago, I had the foam. Let's go ahead and turn that back on, add a force component. Did I lose it? There we go. We're adding back this force component again. So he goes a little bit crazy. So I typically turn off the collisions for the dynamic objects, just because they're not that important that they reflect waves. And it gives you a little bit more stable, especially once you turn down the strength. But I can go in here as well and add some foam. So right now we have the foam being set at zero. Let's crank that up. And that's going to the green channel. And that should-- let's see if that works. You know what, I might not have the right foam material enabled here. Oh well, we'll go ahead and skip that. So now I'm going to go ahead and switch this over to shallow water, just to show the contrast there. So shallow water is more experimental, and I would say definitely a lot harder to get it to behave. And there's also a Niagara shallow water plugin going to be coming out soon as well. So we will eventually be supporting that as well. But for now we're taking a look at just the shallow water inside the Fluid Sim. So you see, here, we have Ripple Solver. We'll set Shallow Water instead. And right now we're mapping to the whole fluid, just because we set the size to be the same. But this all works when we set the size smaller. But I'm just going to keep it to the full terrain for now. OK, so we set Shallow Water. But now what matters is, under Shallow Water, whether or not we are using Constant Water Depth, Terrain Water Depth Only, or Terrain with Elevation. So that's actually an important thing to talk about real quick, is that it depends on what your goals are. So in Fortnite, we actually wouldn't want to use shallow water with elevation because what ends up happening is, if you have a river that's uphill, that river will just drain downhill. And you'll end up flooding some valley or your rivers will run dry. And we'll go ahead and see if that works here. Let me make sure the rest of the settings are OK. So dampening might need to be a little bit higher. So you see what's happening here is that that river is actually flowing downhill into the lake. You see it's actually going pretty slow. So let's see, let's make it go a little faster. So that's what happens if you have a time step too small with shallow water. That's what I talked about, when it's not quite as stable as the Ripple Solver. Generally, when that happens, the only option is to either increase the time step or increase the density, or sorry, the dampening, not the density. Let me see what is happening here. Not getting the fast flow that I was seeing earlier. Let me load this other test map I have. OK, so this is a different shallow water map. Should be very similar. But I've set this one to have some waves and to have a different material that attempts to simulate the Gerstner waves on the shore. So of course this one did flow downhill a little bit better. So this is kind of an attempt at, wherever a Gerstner wave gets to a certain height, we add a little bit of force into the shallow water sim. And then that causes the waves to kind of lap up and down the shore. Although this is kind of in a state of flux in terms of getting it to blend nicely. And there's some issues with the waves causing it to clip. I ended up kind of breaking that in this map when I fixed it for the smaller-scale wave foam splashing example, which we will show you in a little bit as well. Yeah, that's weird. I'm not getting-- what we should be seeing is that the river essentially empties into the terrain when we set it to Shallow Water. Let me try that one more time. Maybe I just needed to reload the map. Shallow water. Oh, you know what? I think it could be this-- OK, I think I remember what it is. There's this dampening option. Let me set the advect. Bear with me one second. >>Victor: It's really exciting so far. >>Ryan: All right, I believe I figured it out. There we go. Yeah, so I had a parameter in there that was basically fading back the water to its default state to try to prevent it from losing volume. So this is another big problem with shallow water. When I take that off, this water is going to kind of lose volume over time. So it's adding maximum velocity. Sorry, it's eventually just going to drain. So this is that advection material. And right now I have this Fading Back parameter. And what that does is it kind of always fades back to the default state of the water so that it doesn't ever fully empty. And based on how strong you have that set, it will kind of determine how much this thing is stable. But yeah, it takes a lot of work to try to get the fluid to be completely stable with shallow water. So yeah, generally, smaller time steps and reducing the forces all help. Let's go ahead and-- the dampening of 0.01 is way too small for shallow water. You need at least, like, 1. All right, so these other forces-- let's go ahead and turn on some forces with the shallow water as well. So you see the effect-- oh there we go. The foam's working there. Seemed like it wasn't adjusting the strength. There we go. So yeah, depending on your resolution, you really might need to fiddle around a little bit with things like the strength of your forces and the dampening of your field. Because it just really depends on a lot of factors. There we go. We got one. It was a little bit too much. And generally we'll clamp the forces and clamp the time step to prevent that. So it should be a little bit more robust than that when it actually ships. But yeah, you can make cool boat wakes, cause explosions. And now, just as an example, I can set the dampening quite a bit higher. That can go above 1. And that will make it a lot more stable. Still not entirely, but here we go. All right, so I feel like that gives a decent context on the simulator. But now I'll go ahead and show that other example, which was using the Scene Capture mode. My fingers crossed, because I've been changing a lot of things. This might break for me. But basically the difference here is everything is the same except we're simulating a much smaller area. I've set it to show the mesh here just to debug. It's set to 2048, which is about 20 meters long. That's about like 10 people laying on their side, 11 people, if you're struggling for scale context. The resolution is 512. And you'll see here, under Shallow Water, we're using this different mode here, called Use Scene Capture Depth. And with this mode, it's actually using a scene capture to make a capture of the scene, including things like static meshes. So this is definitely much more on the experimental spectrum, versus the terrain stuff works with what's already available and baked off. So there's no more memory overhead or complexity. But with the scene capture, you have to be aware, can you afford that render target and that capture on load, as well as if this simulation needs to move, you might need to recapture the depth. So it's really great right now for testing a really high-fidelity small-scale sim. I'm going to go ahead and turn on the sim. It's actually not on right now. So you'll see there's some Niagara particles. I'll hide them. So this is what you get if you do nothing. You have waves, and they just kind of lap through. It doesn't look too bad, but you don't really get that feeling of interaction. So what's actually being done here to cause interaction? I'll turn on the sim now. And at first, it won't have the Niagara particles contributing. Hang on a second. I don't think-- there we go. I had to refresh something. Apologies. So what this is doing is it's the same shallow water sim we saw in the last map. And it's the same basic concept where you saw I was using the waves to kind of add on the shore. It's just this has been refined a little bit more. The way that this is being done is, on the fluid sim actor, there's actually components for a few different things. And they're all kind of separate. Let me pause this, because it's distracting. So right now, the shallow water sim is a separate component. And that's useful because it allows you to kind of build the solver as more of a standalone thing that doesn't bloat up the main Blueprint. And that's where it has the actual MIDs that it needs, and the additional render targets that do not have to belong to the rest of the sim. So like velocity and the height ping pong all exists over here. Sorry, total brain fart. Oh yeah, but it's also a great place to be able to do kind of specific overrides. So you'll notice that while this is mostly just debug-level stuff at the component level, because you're setting most of the simulation settings in the main Blueprint under shallow water. And kind of what it does, when it initializes, it just passes what it needs. But there's a few other things inside the component that you can override that are not exposed. Like for example here, this manual advect material, it's basically replacing the advect step with a custom advection material. And the advections step for the fluid is basically where it pushes and moves the fluid around. So it's kind of a generic place where you can add more fluid as well. So inside of this material, which I'll load up real quick, this left part, this is the base advect that everything else will do. And everything else that's in here is basically fluid foam experiment stuff. So I'll go ahead and show what that looks like over here. The way that that actually works is we have another texture called this foam render target. And this contains some useful detail that other systems can use. So this is actually a system that is rendering out foam for wave crests that we haven't actually shown yet. But I will show that in a bit. So we get this nice foam history you can see there as we simulate. Let me go ahead and show that. Looks cooler on the actual ocean of course. But we do this cool time history generation of wave caps. But then that shader also kind of renders out the Gerstner wave height so that another place can look up the Gerstner waves without all the complexity of computing them again, which is handy. So inside here, what we're doing inside the fluid sim, reading that wave height. And also, remember WaterBodyData, that function that I mentioned previously? We're reading the water body data inside of the actual fluid sim in a render target as well. So the only real difference to use it there-- again, you need to make sure that the texture is bound. And then there's this function, Fluidsim_RenderTargetWorldPosition So this basically just takes the implicit UV and converts it to a world position. So basically where in the world was this render target. And then it knows how to look up the data. So basically by using that combined with the scene capture depth, we're able to figure out, hey, where was the water over the rocks? So we take water z-height. We add the wave height. We subtract the scene depth. So right here, this little expression is basically how high is the water over the rocks. So then, using that-- in a couple places, it goes down here and it adds fluid to the sim using a max. So it's basically any time water is going over the sim, it will do a max with a little bit of the wave water. So it's kind of like, as a wave travels over the water, it just leaves some of itself behind. And I'll disable the rest of this stuff for now to show it more obviously. There's some foam injection based on velocity. And there's some foam injection based on a narrow band right at the intersection. So now, all that's off. So all we have is we're fading the foam and then we are adding the foam, edge foam as well. So some of this complex stuff over here isn't really needed in this custom material. It's just kind of some stuff that allows the system to automatically detect if it's not been initialized yet. Because we initialize it with this really negative color. So if there is this negative color present in the render target, what it does is it grabs the water body data-- so this is how it seeds the fluid sim with all the water data in the world. But the way that that seed works is different depending on whether you're using the terrain with elevation or the terrain with depth only, or with the scene capture mode. So basically it looks complicated, but it's just some switches that kind of do a different mode of initializing depending on which mode of fluid sim you're doing. So what I did just a minute ago is I set it so that all we're doing is adding fluid based on the wave. So you'll see now-- notice, when the wave goes, it sticks behind a little bit. And I'll go over to that parameter directly, this Fluid Per Frame thing. Set that to zero. So when that's zero, once all this fluid falls, it's not coming back. Does that kind of make sense? This is just like the raw fluid. If I set that to 1, that's too much, it's like there is always the maximum wave height, instantly. And you can see it builds up, and then it flows back down. You can even cheat in here and say, oh, we want to make the waves taller. So they go up a little bit more. So when we do this, this is really just causing them to flow downhill nicely, but it's not really giving it that punchy reaction. So the way to get that punchy reaction is we also identify this band, which this right here is kind of like building a sphere mask near that water-rock edge. And then what we do is we multiply it by another value and we call that the Fluid Addition Above Rocks. I'm going to set the other one to zero. So then we'll just do the edge one. And it'll be really obvious. And this one can be a little bit more aggressive. So I had to do like 75, but we'll set it to 100. So now, wherever the fluid first hits, we get more of like a quick, high, little water edge. We'll make that much higher. There we go. So now you kind of see, when the water goes up, it's actually kind of overshooting and causing the shallow water to shoot up over the rock a little bit. And then that causes an impulse which then spreads it back down further. So this is what I say-- this is really a mixture of an analytical technique, which is Gerstner waves are considered analytical because you say, hey, I have a time and a position, and I just get a value. Whereas shallow water is not analytical. It's more, I guess, time integrated. So to have them work together, you kind of have to have some rule for how does the Gerstner waves feed in its inputs to the system. And it's never quite perfect because it doesn't actually affect the Gerstner wave the way that it should. But there's actually some cool things that we can do to make that happen as well. But that ends up being kind of on the material side. So I'll go ahead and jump back to the main material. So this material in this scene here is a little bit different version of the water material. I made an _Beach version just so that I can do some stuff for this scene that's a little different. All right, where is the fluid sim? OK, yeah, WaveDistanceFieldReaction. There it is. Sorry, I was having trouble remembering where I had moved this thing. There's this other little utility function. It's down under the kind of general water functions area, where you see we've got a bunch of neat little functions here, like Wetness, WaveDistanceFieldReaction, DepthFalloff. These are all the utilities that you really need to use water. WaveDistanceFieldReaction is kind of interesting. What it does is it attempts to modify the world position offset of the Gerstner waves to kind of do a little bit of a reaction against the scene distance field. Currently it's being set to act on this distance field in a texture, which I mentioned before that this simulation here is actually using a scene capture to capture the rock. While it does that, it goes ahead and also generates a custom distance field, just because why not? You already have this texture here. So green, this is the height, but red is the distance field. And to do that, it's using that component that was mentioned. So just for the sake of completeness, I'll show it. I don't know why, but I'm having a clumsy mouse day. OK, wrong fluid sim again. >>Victor: Doing a good job on the one-monitor presentation though. >>Ryan: Thank you, sir. All right, so I mentioned before, we have this component. There's this JumpFlood_Component2D. This is actually from Landmass as well. And it's actually the same thing that the water rushes kind of use under the hood to generate themselves. But this Jump Flood Component is able to take an input of a scene depth, and then output out a distance field. So what we do with that is we generate a distance field of two masks. So you'll notice that we have here world sim size is 2048. But then we also have this wave simulation. That's the foam spreading that was mentioned. But usually you want to do that at a bigger scale, because it holds up at lower res. And you want to see foam further. So you want to kind of spread the wave simulation to a larger area. But for that same wave sim, you might want to have access to that distance field as well. And just a quick note that, whenever I'm talking about this distance field texture, you could also use global scene distance fields. The main reason I was using the texture here is because a) I already had that texture resource because of the height. And I wasn't really getting good results with the scene global distance field because some of these are Quixel rocks that are not capped. So they have to be two-sided distance field generation. And that just doesn't really give you a very accurate true inside value. So I'll just make this distance field from the actual height map. And then we have it. So if I look at this texture again, looking at the different channels, you see we actually have two different distance fields and two height maps. So red/green is the fluid sim one, and then blue/alpha is actually the wave one. So I don't know if you can tell, here, this one is actually much bigger. And it's including this little clip of the corner of the terrain there, which is this white bit. So that's pretty handy. I'm making use of that inside of Niagara, and all these materials, to kind of integrate that better. But that was kind of a very verbose way of saying, this is just a slightly more high-quality distance field that would work just like-- this is the global one, get DistanceToNearestSurface. And it kind of works off of the global scene depth one-- or not scene depth, but the global mesh distance field, which you see here. So-- also works. But what we were originally talking about was this WaveDistanceFieldReaction. What I'm going to do real quick is pause this wave sim at a certain spot where there's a wave kind of over the rock. Let me just wait for one. [SIGHS] Whenever you want a wave, it's never there. And then there are always too many when you don't want them. Come on, wave. >>Victor: Turned into a little mini game now. >>Ryan: I know. Waiting for-- catch the wave. Here we go. So going back here to this WaveDistanceFieldReaction function, we extract a band from the mask, and we take the original world position offset, but we boost it by a certain amount. So that's interesting. Ah, let me make this-- Actually have a slight problem here. But I can fix it by-- just reading the wrong color channel. Let me really quickly set the simulation sizes to match. And that will fix it. There we go. So that fixed it. And now we see the giant distance field wave reaction thing. There we go. So it's basically just really pushing up that wave whenever it's near an object. And if we unpause-- it's not always perfect, but it kind of gives it just this little extra oomph of a push when it finally does get over there. And if I turn it off, you'll see it just kind of feels something's missing. And then there's one other thing that can be done. It's already being done, but it can be turned up. Let's crawl all the way back here. You'll notice I actually already am using the DistanceToNearestSurface with this DF Lag value. And what we do is we feed it into this ComputeGerstnerWaves function as a time offset. So GetWaterTime is just the universal function for water time that lets you freeze time and control it. But it doesn't have to be used. But it's nice because it lets us have matching time across the board and all these different effects. But what I'm going to do now is open up the material instance and look at that lag value. So lag is set to 0.3 right now-- or negative 0.3. And what that's actually doing is pushing the wave back, away from the rock. If I set it to 0, you'll see it it's right up with it. If we go negative 1, can you kind of see-- it might help to-- you know what, let me-- here we go. I feel like I should disable the sim really quick. Yeah, I'm going to do that. Where is the advection? I'm going to set this to 0 and this to 0. Just because I feel like it was disrupting-- showing the lag. So can you see the lag there now? I'll load that material back up. What I really want to do is pause this when it gets to-- there we go. So we pause the wave when it's kind of right on a rock now. What we do when we apply the lag, you see, we're kind of pushing it back in time so that it's happened a little sooner or a little later. We can push the other direction as well, but it wouldn't really make sense. So like if I pushed forward one second, you'll see, when a wave gets near a rock, it kind of like telegraphs there. Whereas when it's at 0, then it just matches. Now I'll slowly drag it negative. Yeah, if you go really negative, like negative 10 or negative 5, you actually go back in time through all those wave states near the object. So that's not a good idea. You need to keep it probably between like negative-- small values. But I don't know, it's a subtle effect, but it's the type of thing where, if you turn it off, it's more noticeable that it's not there then when it is there, especially once you add in the Niagara particles. So those are those particles there. But they're not actually being fully leveraged just yet. But before we do that, let's turn off some of this other stuff, because we've gotten a little bit crazy. Just going to set this Distance Field Direction way back down. And notice that we've turned off pretty much all the foam and all the water interactions. So now we just have the wave lapping, and then those particles are just there. But if we want to turn on the particle interaction, right now, that's actually happening as part of Niagara using a new experimental feature. All we have to do to enable it is select this from this ticker. And this right here is just kind of an actor that's designed to tick in the editor. This all can be done without that. It's just I'm in dev rendering, where there's some stability issues when you stop simulate mode, sometimes crashing. So I'm doing this without that. But now that I have that Niagara grid enabled there, notice we get foam again. But it's only coming from the Niagara particles themselves. And the way that that's happening-- so this is actually a nice tie back to the start of this stream, when we talked about the fluid sim, the impulse force. So the way that this Niagara fluid sim communicates back into the fluid sim is actually pretty simple. It's just applying an impulse, just like a bullet. So you'll see down here that there's this Fluidsim Forces Material. What that's doing is it's reading the grid from Niagara. So let's go ahead and show that real quick. So when I simulate, you see this little mask down here? This is a mask of where all the Niagara particles are splashing against something. And then, in this material, we're reading that and then applying it to the red channel as a fluid force. And then we're adding it to the green channel as a foam force. So we can actually control those separately and see how it looks without, say, foam from Niagara. So let's set Foam Fill to 0, Foam to 0. So now we just have Niagara just adding water. And then if we were to turn off everything, now that's everything off. We'll set water to 10. It'll be really obvious. Now, wherever we get a particle, we get big, giant amounts of water. So now we can go ahead-- let's turn foam back on a little bit. And notice what I'm doing with this foam. I'm just kind of applying a noisy mask with the tiling texture inside this material just to do that, just to break it up. There we go. So if we look at that foam texture again-- we'll look at that fluid forces again, on the fluid sim. We'll go ahead and look at Forces RT. So here now we can see how that Niagara texture is being injected into the fluid sim, just like we saw before with our T-pose guy. But this is a little bit more obvious now because we don't have the scale and velocity issue. But you can see here, controlling the amount of contrast and offset in the foam with some of these parameters-- so you say, oh, we really just want little blots of foam. So the foam does diffuse, which is why it kind of blurs out really quick. But we can go in there and disable that. So if you see in here, see all these offset samples, this is actually just sampling the height texture next to the neighbors, and just averaging it. What we can do is skip that. Let me just delete it for now. So you see here, what we're really just doing is we're just adding the existing Height foam channel to the Forces foam channel. Go ahead and compile that. So now we have no diffusion on the foam. So it might look less foamy, but it will also be more-- you'll see the exact detail shape of the-- it's more obvious when it's first adding, because it kind of stomps over itself. You can kind of see how it has that modeled pattern in the foam. Can you kind of tell, Victor? >>Victor: Yeah, it's coming through. >>Ryan: So yeah, that model pattern, just basically from the texture. But this Blueprint that's actually providing this, this is something that you wouldn't need in the long run to pass this in. This is just-- because of the experimental nature of the Niagara feature that it's using, called Shader Stages, it's using this to basically get a reference to, and copy that render target from Niagara out. But let's go ahead and just I'll show that Niagara system, just because it's not that crazy and it might be interesting. So here we go. So here's the whole system. Like I said, it's actually a pretty basic system that's really just like-- it's really just spawning velocity in a cone. And it has a sphere location. And then it's doing something called rejection masking, where you kill particles that don't fit your condition. And in this case, the condition is the water has to be higher than where the terrain is and the water level has to be currently increasing. So using that, it can basically mask out particles that don't fit that. So let's see what it looks like when we disable that, just for fun. So let me go ahead and set this Wave Threshold. That basically means water has to be increasing by 2.5 centimeters in the current frame to be considered a splash event. We'll set that to like negative 64 so everything's a splash. And then this particle Distance Field Threshold, we'll just set that to like 512. Something's still not right. There we go. So now it's kind of always going. The reason it looks weird is because it's reading that wave height texture-- or sorry, it's calculating the Gerstner waves to match them. And I'll show what that looks like in a moment as well. That's weird. It should be much more consistent. I'm not sure why-- oh, you know what, it's because of the water height threshold in here. So in here, this function looks a little bit messy. But really what it's doing is it's just taking some conditions to say, like, is the rock higher than the water level, and uses some logic conditions. And really what it's doing is, if the condition isn't met, it sets lifetime to zero. Otherwise it sets it to the original lifetime. So pretty much it's just particles continue on as normal until they're ready. Or rather they'll keep trying to respond until the condition meets them. The reason why-- OK, yeah, it's only happening when the water level is higher. So I can, say, check that box. And that should make them all respond. No. Well, I'm missing something there. But oh well. Let me just show a video of that. I had a video of this effect in a different state. Give me one moment here. There we go. So this is what I was trying to make this do. But I guess I missed some bug. In this state here, I set these particles just to always go but to be much smaller, so that it's just more obvious that the actual particle is being fed into the system. And yeah, obviously it works better when you're thresholding the spawning based off of the waves and when you're trying to set the size and velocity as well. But hopefully that makes it a little bit more obvious what's happening. So now let me see-- trying to think if we-- probably a good chance to go through some caustics now at this point. Do we have some time? It's a two-hour stream. That's good. >>Victor: I do want to cover some of the questions. We received a couple of good questions as well. >>Ryan: OK. Maybe if you want to just start throwing me some quick questions. And we can transition over. Is there anything-- I'm assuming it's about stuff that was shown. >>Victor: Yes. And there's some general questions about the features as well. >>Ryan: Sure. Yeah, I'll load up the beach map. This is not a finished, pretty map or anything. But it's kind of a little fun example map about water foam. Then I'll show waves, and lots of rivers, and some cool, fun little painted graphs and flattened out sand. >>Victor: It looks nice. >>Ryan: All right, go ahead. >>Victor: Yeah, so in general, there were a couple of questions in regards to if it's possible to use swimmable so that your character can use the swim movement mode in the movement component automatically when it reaches the water. Or do you need to work around placing specific volumes for that? >>Ryan: No. That's all handled by the gameplay interaction system. So the one I mentioned before, when we did that strike team for the Fortnite water, there was a couple general engine/gameplay engineers as part of that. For example, Zak Middleton, who is kind of the guy who generally sets up a lot of the low-level player controller, and movement component, and stuff like that. So you shouldn't really have to do anything custom there. >>Victor: Let's see. >>Ryan: And there's also the Blueprint node, which it's not yet in this build, but in Fortnite, what you basically do, if you know you're overlapping a water body, you basically query that water body and give it a location. And then it tells you its wave height and velocity, if any, and all that stuff, and normal so Blueprints can use it for alignment. >>Victor: Is it possible to move the water bodies at runtime? >>Ryan: Not currently. So what happens is we showed before how, in the landscape, you get this Edit Layers. So kind of, as a general rule, all edit layers, what they do is they get collapsed on serialize. So that means, when you save the asset to disk, all this gets combined back down to just one regular set of height and weight maps. So that's for performance and memory reasons. Because we can't really just afford to bloat up the size of the landscape by however many layers it has. But that's still a really exciting area of research and something that people ask about constantly. Especially even on the Fortnite creative team, they're like, why can't we use this in Creative mode yet? And yeah, so believe me, we definitely hear you. It's just a matter of, right now, all this stuff is fairly GPU-intensive. And some of it is kind of hard to include as part of a cook process if that makes sense. >>Victor: How does the water sort with other transparencies? For example, how do you keep splashes from only drawing on top or below the water plane instead of being cut off as they intersect? >>Ryan: So that is just currently a hard limitation with the single-layer water. There's no translucency under the water. We tried some modes that allowed it to work. But there was just a lot of issues because you no longer have a correct depth to fog. So that translucent thing would look like if it was fogged from the distance behind it. So we found it was best to not allow translucency underwater. So for any effects where you need to show something under water, what we do is generally do like a masked effect, like an emissive bubble or something like that. >>Victor: Does it support ray tracing? >>Ryan: Currently no, but eventually it should. >>Victor: Can you control the tessellation amount of the water meshes? >>Ryan: Absolutely. I knew I was forgetting some stuff. So let's select the water mesh. Ah, what is wrong with me today? All right, so we've got our water mesh selected. We're looking at Wireframe. Let's hide Landscape. There we go. So what we're looking at here is the water mesh actor. And we can change the number of tessellations of course. I had it at kind of the highest setting it can go to right now. So this is progressive, obviously, to where it reduces in distance. You can also change the tile size. Right now it's not quite working correctly. When you decrease the tile size, it's reducing resolution. Kevin is working on that. And also, currently, you cannot go over 7 or it will kind of break. Let's see if we feel lucky. I'll show you what it does. It will hitch-- yeah, there we go. So the reason this happens is because it's using a 16-bit index buffer. And I think it's overflowing it. So he's adding the option to use a 32-bit index buffer. And then you can go really crazy. But yeah, absolutely, you can change-- there's LOD scale, which is per platform usually. So you definitely have to make sure you keep an eye on how your water features are working on different platforms, because it can be tricky to make it work correct on all platforms given that we're using that global water height texture. So it does take some tuning. I'll go ahead and show that foam sim thing. I didn't show that yet. So here's the same thing. So in this case here, this isn't doing the full fluid sim. This is just doing the wave-- or sorry, the wave foam. So you see here, I'll select the fluid sim. Go down to-- was there more questions or anything, Victor, while I'm setting this up real quick? >>Victor: Oh, yeah, we got several. >>Ryan: So down under Wave Simulation, there's this Fading. So if I set Fading to 0, it does reset. But this will basically-- it will only be diffusing, it won't be fading. But you'll see, it will eventually just kind of fill up with foam everywhere. Versus if we set it to 0.5, it's going to be a really aggressive fade. I think I had it at like 0.2, maybe 0.1 or something like that. But it can give it a nice feel. And then so by doing that combined with a little bit of standard distance field masking, you can kind of get a decent base, where it kind of feels like these rocks are reacting to the foam, and it feels like there's something going on even though there's really not much. But then this is a really great base. Once you start adding a few splash effects that are timed on top of this, it's surprising what the human mind and eye will do to kind of make it work. And you will kind of visualize the interaction even if it's not there. So I'm pretty excited about this combination of diffused wave foam plus separate simulation foam being combined in one thing. I think it's going to still take continued research to figure out the best way to push this stuff. But I'm really curious to see what people come up with. >>Victor: So there was a question in regards to transferring between underwater to above. And they called those just ""that nasty jump,"" if it's possible to reduce that. >>Ryan: So yeah, there's just a bug in here right now where it's not matching the waves. And I'm not sure why. I think it's only happening because of the steepness. So there's two different post-process methods. Let me adjust the waves really quick. So in Fortnite, we actually don't use much steepness on the waves. For the ocean, we use no steepness, which I'll show what that looks like. So this is an ocean with no steepness. You see the waves are much more sinusoidal. They're round. They're not sharp. But it reduces the horizontal sloshing. Because as you do steepness, you get this horizontal sloshing, which that caused some issues with players feeling a little bit like they were swimming against the tide when they really weren't. And it causes other issues with alignment. So we set that to 0. So when that's set to 0, the post process will match. It's actually that steepness that's actually the issue. But if you do want steepness, there's a separate post-process solution that does not use the volume. So I can go ahead and-- that setting exists just under Water, I believe, Rendering. So there is this Underwater Post Process Material. We'll go ahead and set that to None. And I think that clears it. So then there is this other post process. It's a mesh-based solution. Let me add this to the world. The only thing is, right now, you need to get a reference to the water texture for it to match. Of course watch it not work. Hang on. I haven't looked at this thing in a while. So yeah, the reason it's not working-- let me fix this. So really quick, I'll give a theory of what this version does. So what our regular post-process does that Fortnite uses is, per pixel, it actually computes the Gerstner wave height, and then masks against the screen on that. But that's actually pretty expensive because you compute the waves for every single post-process pixel. But what the mesh-based version does instead is use a vertex shader and solve the waves on that sparse vertex shader to kind of reduce the complexity. But the only problem is it's using the old Gerstner wave function before Kevin did his buffer change. I should just be able to switch this to the compute. And it should work. And then I think depth attenuation should be fine. Let's try that. There we go. So now we get matching waves with the mesh post process. And we can even go ahead and set the steepness back and watch as I hold my breath. OK. Oh yeah, I am having trouble selecting. OK, so now we will go back to Wave, Steepness. So now we've got a pretty good match. The only mismatch comes now from the fact that the post-process mesh actually has a lot more tessellation than the water mesh when you get this close to it. So it's hard to get a perfect match, but it's pretty darn good. And when the waves get steeper than vertical, it cannot resolve that. You know what, let me do this. There's actually a freeze time in here. By the way, this Gerstner wave controller thing, this is going away completely. It's purely there right now just as a debug tool. So you can see we're matching pretty good. Let me go try to find a really, really steep spot. Yeah, here we go. So it can't match these over-vertical steepness areas. So there might be a little bit of a discrepancy there. And that's just kind of a limitation. But I think, in motion, and once there's effects, you don't really notice it. But yeah, hopefully that answers the question. >>Victor: They're all curious what hardware you're running right now. >>Ryan: So I just have a-- what is it called-- 2080ti >>Victor: OK. >>Ryan: Something wrong here. Oh, you know what, I'm seeing the mesh and the post process. So there's something going on there. But yeah, we're still in a heavy bug-fixing phase, as I mentioned, because of the integration to Fortnite. So quite a few of my-- I'm surprised things are working as well as they have today. Yeah, I'll go ahead and do one more thing. I'll set the caustics on here. Rendering, and let me go find my ocean. >>Victor: Is there any way to transition from translucent water to an opaque shader at a distance for optimization purposes? >>Ryan: So, you can do whatever you want there. I wouldn't do that, though, because you're not going to match scattering. What we do instead is, on the water mesh, there's this thing called the far mesh. And what that does is it lets you specify a simpler material in a simple mesh. So if I look at the wireframe, you'll notice that all the water tessellation kind of stops like-- hang on. Yeah, see out there, on the horizon, where all the tessellation stops? That purple, by the way, is the post-process mesh that I was mentioning before. So that's that. So that's the tile size of the far mesh. So if I delete that, now there's no far mesh. So you'll just have nothing. So what I do instead-- and there's no reason to set that small. You can set that up to infinity if you want. Well, maybe not infinity, but something bigger than you could have to worry. And the reason this is looking different right here is because there's no landscape underneath it anymore to draw, so the depth is different. But if you go in here-- So this is basically still a child of the same parent. The main difference is it just has everything unchecked. It doesn't even have waves. So like that's pretty much like the bare-bones, single-layer water shading. I think it probably still has the fluid sim on. That's why the instruction count is up. I need to put this behind a switch as well. Ideally, everything is behind a switch, and the far mesh is really cheap. But yeah, you can put an opaque shader. You can do whatever you want. It doesn't have to be water. It doesn't care. Oh yeah, so what I just did here is I changed the material for the ocean. And I changed it to use a caustics material. So you can see, next to the non-caustics area, it's not matching. But I'll show what that looks like in the material here. Oh, I just already had it open. OK, so all of the caustics input-- here is the single-layer water material. There is a Caustics input node on there. And then here is the Caustics function. And this one is actually using a volume texture. And by that I mean it's an actual 3D texture, not like a pseudovolume. We can look at that here. So this is the volume texture. And here's a new mode where you can actually look at volume textures as a volume. So I would say this is like a thin, shallow volume. So it's actually like-- the number of frames is going in-between the z component. So it actually is only-- I think it's like 64 frames. So it has much more horizontal resolution than it does time resolution. But you get seamless blending on all three axes with this. So that's neat. And it also adds in the simulation caustics. So I'll show-- it's a pretty simple material. It's basically just a volume texture lookup. There is a little bit of math here to kind of take the light vector and parallax the caustics so that, when you rotate the light, it kind of projects it up to the water plane, so based on water depth. And you see we get water depth by getting-- there is this WorldPositionBehindWater which is using the scene depth without water-- scene node, scene depth? [CHUCKLES] Something is wrong with me today. There you go-- scene depth without water. So this is actually how, inside the water shading code, it gets the distance from water to behind water. It gets like scene depth without water minus scene depth. So this is kind of doing that same sort of thing here. But then it uses that depth to figure out the world position behind the water. Then it can figure out the depth by getting the difference in the z values of those two vectors. And then it can use that to basically parallax that caustic. And then all it's really doing is there is an append somewhere. Where is it? Oh yeah, that's this image. Here we go. The append is just appending time as the z. So for 3D texture, you don't need like a flipbook function. You just look up it with two 2D coordinates, and then you set your z-coordinate to be time. And then you just get a flipbook for much cheaper than doing it with 2D textures. And then, over here, this is adding in the simulation caustics. So it takes the normal from the simulation, and it uses that to perturb the UV of the caustics normal, or the precomputed caustics. And then it takes kind of a finite difference and extracts kind of a focal band from the blue channel. And that's just kind of a cheat to get some caustics-like response out of the simulation. And I'll see if I can kind of point that out in a little bit. But I guess we can show the caustics map itself. So this is just showing it applied in the world. But you can also apply it, rather than doing it as a caustics input, it can be done as a light function. It's just there are different trade-offs and different complexities. The trade-off with this method is I actually increase the refracted scene depth quality. So by default, in the engine, you get a little bit lower-quality scene depth. Because actually what it does is it half-res samples it. So you lose some resolution. And the reason it does that is because single-layer water renders as a compositing pass. So we first render the regular base pass. Then we render water. Then it composites them together. And in the past, where we're compositing, it downsamples the existing scene color and depth to make some of the refraction in a scene color look-ups more affordable. So you can tell we lose a little bit of quality because we effectively lose resolution in the position that we reconstruct. So we set it back to 1. And then you get nicer results. But going to the caustics example map-- we've got our first crash. >>Victor: There we go. >>Ryan: Want to go through some more questions while that reloads? >>Victor: Yeah, that sounds good. Can the water system be used to create more indoor-centric water bodies without terrains, such as aquariums, ponds, et cetera? >>Ryan: Absolutely. I forgot to mention that one when we looked at the types in the beginning. But in addition to Ocean, Lake, and River, there is Custom. And with Custom, you specify a static mesh. And you also can opt out of the terrain carving features. So that's what we do in Fortnite for-- for example, there is that nuclear reactor with the core. The cores are actually regular water bodies. They just have a custom mesh just set to a flat plane. >>Victor: That's awesome. I'm very excited to hear that. There are some general questions about a sample project or any way to get to break down everything that you've been working on here. >>Ryan: Yes, so the current plan is we're going to try to include this water sample project that I'm in right now, which would include that little beach map, as well as like the little fluid sim example. So yeah, it's probably not going to have a ton in it. It's going to be a beach with some rocks, maybe a little-- I'm in. Has anyone else ever gotten this bug, where you can't actually click anything? I'm going to have to restart the editor again. This happens to me sometimes, when I'm screensharing, when I start the editor. The only way to close is that. Sorry again. >>Victor: That's all right. We still have a little bit of time as long as you're good. >>Ryan: Yeah, no, what are some more questions? Hit me with them. >>Victor: Yeah, so during the scene, when you were showing off the buoyancy, and waves sort of bouncing back from other static meshes or skeletal meshes in the scene, there was a hard shadow on top of the water as well as one below. And they were wondering if it's possible to remove that top-- the hard shadow that didn't look very realistic. >>Ryan: So here, let's put on our rendering hat. [BOTH CHUCKLE] I'll show you what you need to do. So yeah, that's actually something Sebastian wrote. And he started working on a prototype of a volume shadow method for it. But it is not on by default. So I have my own little experiments in here too, like this chromatic spread. This is the SingleLayerWaterShading.ush. So this is, for example, where the caustics lives right now. Like I added in this little caustics line to hook up the caustics. But you see there is this #define VOLUMETRICSHADOW 0. If you set that to 1, what it does is it will actually evaluate this code block in here. And what this does is it raymarches the shadow map instead of just looking it up at the surface. So then you actually do get that connection from the bottom and top shadow. Although it's probably still not going to be enabled in 4.26. You'll probably still have to edit code. And there's also another problem, when you enable it, it'll fail to compile because there was some engine change in this line about the compute dynamic shadow. It will not find it because you have to move one of the includes in base pass pixel shader for forward lighting common to exist before single-layer shading. Don't ask me why that changed. I just had to figure that out when I was testing this myself. But yeah, this is a fun place to look if you want to look around for how the single-layer shading works. Some of it we went over when we talked about the absorption coefficients. But if you want to learn exactly what it's doing, it's all in here. But yeah, like for example, this is where it's grabbing the Caustics pin input. And this is how it's set to make sure that it doesn't go above 0 or below 0. And if it's not connected, it's just going to be 0 or it's going to be 1. Yeah, so there is plenty of stuff that will probably be happening in the future with water. All righty, so last thing we'll show here, this is the caustics generator. And this works very similar to what we saw before with water bodies, the main difference being that-- I was on the wrong Blueprint-- the main difference being that it has waves kind of right on itself. And it's meant to be-- it's an editor-only class. You cannot place the GenerateCaustics in game. And that's because it's an editor-capable class. But what it's designed to do is generate a texture that you want to render out. So by default, you set up things like the water depth defines the caustics or defines how deep you want to render. You can say how much chromatic aberration you want. You can say what is the world size of the texture. And it will kind of adjust the preview scene to all match. And then you get your own wave generator as well. So you can change the-- I think I'm not set to generate waves right now. Yep. You have to check the Generate box. Otherwise, it'll just use whatever waves exist in the scene. >>Victor: Got it. >>Ryan: Forgot. Where did I put that Generate? It's right there. OK, so yeah, it lets you really-- you can get some pretty specific caustics with this. You can change the resolution of the actual mesh so you can get sharper and blurrier. Generally you don't need to go too high because you're going to probably downsample it when you export it in the end anyways. So the general gist of this is you tweak your waves and you tweak your caustics to the desired depth and result. And then, from there, output and animation is going to determine what you're going to do with it. I'm going to tweak this a little bit more because I'm not happy with that, Uh, Waves. Let's see, so yeah, we need a little bit more variety. There we go. So we've got that. If you just press Begin Rendering when you're on Render Single Frame Only, that's just kind of leave this in a dynamic preview mode where it's just always capturing the current frame. But if you stop that and set it to Render Animation Using Time, then what it's going to do is just use all of these settings here for making the final Sub UV. So we could say, let's do 8 by 8 frames. And frames per second is really just how much time passes between frames. So now, when we press Begin Rendering, see, we saw it kind of fill out those frames one at a time. And you might think, oh, why did it start in the middle? That's because it has to make this a looping effect. So it kind of has to do the whole encode time and loop on itself. So it actually does render from the middle that way. And now what we're looking at in the end is the actual Sub UV result. So obviously the more time you have, the choppier it's going to be. So if I set like a low frame rate, like 5 frames per second, this is just going to jump around like crazy, right? But let's capture at 60. Now it's going to be really smooth, but it's not going to move very far. So in this case, that's actually pretty good for this value. But let's say we captured it like 150 FPS. That's actually probably going to not work out. So you see what ends up happening? Your caustics don't really move enough. It just like lerps back to itself if that makes sense. Especially if-- I'll go with even less frames. Yeah, so here's what happens when you try too high of a frame rate. It's like it was trying, it's trying, it's trying, nope, nope. And it falls back down the hill. So you kind of want to pick out the right frame rate for your caustics. And then there's this file path thing here. Once it's done, all you have to do is press Save Texture. And then it's created for you. So if you go to Water, Caustics, it automatically added that 2 at the end, because. So there it is. And there's a couple other things to keep in mind with this too. We go back down to Preview Scene, for example. Right now where we're looking at the Light Function Caustics mode. So what that means is, if we actually look at our light, we actually have the Light function right on the light. So it's got the render target applied there. But there's kind of a complexity with that because light functions can only be clamped-- the texture cannot go over 1 in the material. So that means we actually have to boost the value of the light. So the caustics has to be normalized by the max texel brightness. So that's why it looks like a darker texture. So the boost factor gets stored automatically for you. It's this 32. So that means, with the caustics texture, the brightest texel was 32 times brighter than the rest, or 32 times brighter than neutral. So what you actually have to do-- notice the light is really bright now. We boost the light by the caustics normalization factor. And then we set the Disabled Brightness also to be the normalization factor. So basically it's saying we make the light way brighter, but then we agree to set it back down to the original value. So that's all you have to do there. But you can also just change the preview mode right on here if you want. So I'm going to uncheck Light Function. So now we're looking at regular caustics. And in certain angles, this is the half-res buffer that I was talking about. You see those lines? >>Victor: Yeah. They're coming through. >>Ryan: So they go away when you set the full resolution. So this is not light-function caustics. This is in the water body. And they're very similar. They look slightly different right now because of a very slight difference in how the refraction is computed and also because the-- so the water material one can be RGB. So you see we have this chromatic spread. We can set that to 0, 1, 2, we can set it really high. But light functions are only grayscale. So that's another difference is the grayscale light function. But it's basically the same technique for both. And I think it's really not clear yet which technique is going to win out, because it depends-- with light functions, you end up paying more cost for the whole scene. Because you look up that caustic texture even if you don't render it above the water. Because you're just masking it, you're not actually dynamically branching it. And you also have to pass in things like that global terrain water texture to be able to know the water height everywhere in the light function. And that might be not ideal for certain cases. So the material-based caustics is better because it restricts the cost just to the material. But then there's different downsides. For example, notice, right now, we're looking at the light function caustics. And because it's light function, it's automatically shadowed. But the non-light-function caustics, it's not shadowed. We still have caustics in the ambient-- I mean, it's not a deal breaker. I don't think most people will care. It's just one of the things. It's like, yeah, the light function just gets shadowed automatically because that's what light functions are. And I would say, for something like an aquarium, where you have an object in a room, the light function caustics are probably not going to work either. Because you'd have to do like a raycast to determine if you hit the aquarium, and if the ray went through all the way, and all that stuff. Whereas you could just put the caustic shader directly on the water material, and then it would work. It's just you have to contend with the downsampling precision problem. And then the final option is there's this analytical caustics. Ooh, it's getting way too bright. I'm not sure what's going on there. >>Victor: Discord? >>Ryan: Hang on. Let me resave that. I'm not sure why that's so crazy bright. Hmm. Well, we can take a look at that. So the way all of these caustics things are organized is under the Caustics folder. And then under Materials > Preview, there is an additional copy of water materials that have the preview functions all. This is pretty simple water material. It has basic water Gerstner waves and texture for the material itself. And for the scattering and absorption it's the same as before. The only difference is this one has the three different types of caustics gated behind switches. So that's why there's the different instances. And the Blueprint is basically automatically picking the correct one based off of what you choose down here for Light Function or Analytical. So the one we're looking at here would be GerstnerWaves_PreviewOnly. So this would be-- this caustics brightness thing shouldn't be that bright. But we can adjust the caustics by looking at this WaterCausticsPreview_Analytical. This basically is coming up with these caustics using finite difference methods. And it's bugging me that it's so crazy bright. But let's see. You can still kind of see it. Let me-- oops. That must be being controlled in the Blueprint here. Just going to quickly put a mask on this since I don't have time to fully debug it. This shouldn't take too long to build. Any other questions to rattle off, Victor? >>Victor: Yeah, someone's curious if it's possible to create circulating ocean currents. >>Ryan: So I would say that you could do that using just flow maps and things like that. But currently it's not going to communicate to the rest of the gameplay system. So like the gameplay character swimming is not going to know about those. So I would say that's more an area of future research. But you know, we do have some people on our team now who have experience with that. I think what happened is the normalization factor from the other caustics was bleeding into this one. But you can see here, these are the analytical caustics. So they look different in that the features are not exactly the same. But they do follow the same pattern of the actual waves. Let's see we go ahead and disable the freeze time. Oh, do it on the caustics. So the analytical ones are nice because they work with the pattern without having to make a texture. They just kind of just work. But they're a little sharper. And they take some fiddling. But any waves that I do set, it is going to be reflected. And the way that you kind of get that to work is there's this-- it's basically like a finite offset. And the size of that offset kind of determines the size of the caustics band you're going to be extracting. And there's also some depth-based spread stuff. But it's a little tricky. >>Victor: There was another question. How does one use Simulation Stages? Can't seem to find any information on it. >>Ryan: There we go. So this is showing that you at least get the same pattern. Yes, so Simulation Stages, I wasn't planning on really going into very much on that yet because, well, for one, Wyeth Johnson and Jon Lindquist are doing a stream, I think, in maybe two weeks or something. >>Victor: It will be in beginning of November. >>Ryan: Oh, OK, so it's in like a month. >>Victor: Yeah. >>Ryan: But I think those guys are planning on talking more about those sort of things. And right now, Shader Stages is in a state of very wide flux. So anything we say could send people down a wrong path. But I mean, since we're already in here, I can just mention, just really quickly, the basic idea, as long as people promise not to get a-- just know you won't be able to copy this right now. Like what's Shader Stages in 4.25 right now is completely different. >>Victor: We'll ask for some promises in chat. >>Ryan: I'm looking for the effect. Hang on. Here we go. So the basic gist of it is that you have grids. So there's this emitter grid. Where is it? It should be showing. There it is. So you add grids through your emitter, Grid2D Collection or whatever. And you set them up in your particle spawn, setting it to be 256. And then when you have like a stage later-- so this is rasterizing a particle to a plane. Basically you set the stage to point to that grid. So like this Grid2DInput is EMITTER ParticleGrid. And then inside here, this is a particle stage. So it's basically using particles as the thing. And then, in here, it's basically doing some masking to be like, OK, this part builds a sphere to make it round. This actually renders a radius inside of the grid. So each particle is just writing one texel at a time. And you tell it how many texels to write. And it adjusts that texel based off of how in and out of the grid that it is. So when you're looking at it, that's how the points are shrinking. It's basically saying, the radius goes to zero as we get radius away from the plane, which is all this math. So this is a super, super bare-bones Shader Stages. Usually people are using them for iteration non-positions and other stuff. But here I am just using it to raster out a position. Or not a position, just a value, just to feed it somewhere else. But yeah, hopefully, come October, you guys will get a much better look. So yeah, I'm hesitant to say any more than that, just because, for one, it's changing a lot. For two, it's more the Physics team's feature to show off. So yeah, anything next? >>Victor: Yeah, we got a few more. Is the caustics sample project and system similar to the previous work on caustics? Ryan previously went in depth in other videos on how they work. >>Ryan: I mean, it's similar behind the scenes. I mean, if we can kind of deconstruct it really quickly by, in here, if you turn off this Show Water Scene, now what we're going to see, we're actually looking at particle-- they're static meshes, but I call them particles just for simplicity. So yeah, it's rendering out these particles. And there's a material for it. You can load the material. Let's see. Of course that's not helping me. Caustics > Materials > Generation. So it's this Mesh Scatter. Now, brace yourselves. This is a fairly complicated shader. Because this shader is doing a whole bunch of stuff to generate tiling Gerstner waves. And there's this whole pre-integrated spectrum thing. But the part that I really wanted to look at was there's this Photon Scale that, if I adjust it back down, you'll really be able to see the individual particles. So first let me reduce the resolution. How can I select this? OK, so resolution is 1,024. Let's go down to 512. And then so what it normally tries to do is it makes these particles scale with the resolution. So the higher res, you can see it's scaled them down for me. But in the shader, there's an additional expansion. And what these are doing-- there's a bunch of other stuff in here to make this thing tile and wrap. So if we disable the wrapping-- so that's without wrapping. You see that we don't have seamless tiling anymore. So if we were to-- it's hard to tell here, but it would break the tiling by disabling that. And then we control the chromatic spread. So the chromatic spread, this part is actually probably the only interesting technical part about this whole thing. The rest is all pretty standard. But basically what we do is we take these individual photons, and we kind of stretch them. Let me make the particle even smaller. >>Victor: And you actually refer to them as photons. >>Ryan: Yeah, I call them as photons because they really contain a full spectrum each. And so, as it gets longer, it basically spreads the rainbow spectrum more to its component parts. But it's doing that kind of without any additional blurring or additional lookups. And the way that works is kind of neat. I'll show that. And I mentioned this briefly a while ago on Twitter. But there's this pre-integrated spectrum texture, which this basically represents a single photon. At the very bottom down here is where a photon without any spread. And it's encoded where the spectrum is set to equal where it's indexed as the photon spread is 1/photon radius. So basically we tend toward black. And so this is energy conserving. So as you get to a point, you notice there's this kind of like antumbra in the falloff here, where it's just this white-and-orange region. So this is where you would have chromatic spread, where the aberration was about half of the photon size. You start to see the color fringing first before you see the full color separation. So it gives you that accurate energy conserving response, which is interesting. So that's basically just how it's able to do that efficiently. The more standard way of doing this is just to do three different caustics renders, one for red, one for green, and one for blue. But the reason that didn't work well for me is because I was also trying to use this for other purposes. So you can also take advantage of this rainbow gradient. Instead of using it as chromatic aberration, you can also kind of exaggerate the chromatic aberration and then blend between the different aberrations based on depth. And you can kind of use it as a proxy for storing depth variation. So almost like a four-dimensional texture, where z is time, but color is now z. And what that looks like-- let me see. There's actually a mode in here, it's just not quite working by default at the moment. So for render, you can render animation using time or you can render animation using depth. And with render animation using depth, what it actually does instead is it uses the-- instead of just animating the time, it will animate the depth as well. So like if I set 2048 and 0, let's make a flipbook of that. That did not work. OK, well that broke. So what that was supposed to do-- oh, never mind. I selected the wrong choice. There we go. >>Victor: Whoa. >>Ryan: So here. So now what we're looking at here is we're looking at increasing depth over time instead. So you could render a single time frame, and then choose between that instead of animating the position. But the other option there, you go back to animation using time. And you set that back to 0. But you exaggerate the chromatic spread something crazy. Maybe too crazy. That doesn't look right. Here you go. So yeah, it's hard to look at by itself. But when we look at this afterword, what we're looking at is basically each of these channels will have increasing chromatic aberration, which is also an interesting proxy for depth. But because we did it in that smooth way with that spectrum, it's not like we just have separate, discrete points. It's more like they're continually connected. So like when you zoom in, it's a continuous shift between those colors. So you can actually then extract that after the fact and use it to animate the depth just by color picking. There's no working example of that right the moment, but it's pretty powerful. I posted about it on Twitter in the past. Yeah, any other questions, Victor? >>Victor: Yeah, a few more. So I know you mentioned it earlier in the stream, but for everyone that joined a little bit later, when can people get their hands on the tools? >>Ryan: So this will be first available when the 4.26 preview comes out. >>Victor: Which is today, the first one. But it's not in the first preview. >>Ryan: Oh, sorry, preview 2. My bad. I didn't realize we already had that. Yeah, no, we did not make it in time for the first integration. >>Victor: So keep your eyes out. I have a few more. But we're at 2 and 1/2 hours right now. Let's do a last question. Do you have any tips for troubleshooting issues like the gap between the lake and the river? It was in the beginning of the stream. >>Ryan: Yeah, in that case, I think what we're really going to have to do is include a set of guidelines that make it obvious what those limitations are. Like that same thing could happen here. And in fact, I'm curious that it's not. It's hard to say. What I would suggest is looking at tile bounds. That is probably your really number-one thing that's going to help you debugging stuff. Because often, if the problem exists on the boundary between two colors, then that's when it's like something about how you're masking the behavior between the two different materials is off. But generally it's the velocity just needs to be zeroed out. Like in this case, it's kind of working the velocity is full at the end. Because we don't have a different color transition at the end of this tile. If you see that, it's still yellow. But if we happen to have a green there, that might be bad. So having velocity 0 at the end of your river is more safe. I'm not sure if that's something we'll be able to do by default. Because when you place a river, I think it has like 2 points. Or maybe it has 3. Let's just try it. Yeah, it's got three. So maybe what we can do is just, by default, make sure that the endpoints have 0 velocity. But I think it's a trade-off. I don't know don't know that there is a correct answer there. Foliage goes crazy with the wind. So anything else coming up? >>Victor: The last one was just a question in regards to, you did a mention earlier about custom shading models as a plugin. >>Ryan: No, I was mentioning that we do not support that. And that's why water was released-- the shading model was released earlier than everything else, because we did not have that option. >>Victor: Cool. I think, with that, it is time for both you and me to take a little break. Ryan, thank you so much for coming on the stream and showing off the tools today. Make sure you go follow Ryan on Twitter. It's @ShaderBits. He posts plenty of his work. And it's always amazing to see what he's working on-- some updates, sometimes tips, little mini tutorials almost even. And so make sure you follow there. Go through a couple of things real quick since we're at the end of the stream here. Make sure you follow us on social media for all news and updates regarding Unreal Engine. We post when we're streaming as well as links to all of our blogs, other cool project spotlights, et cetera. So that's where you get the latest and greatest. Make sure you visit our online communities. If you're new to Unreal Engine or you're been here for a long time, you're probably part of the forum on Discord. But if you don't know, UnrealSlackers.org is one of our unofficial community Discords. There's plenty of people in there helping out, participating in chat, doing good discussions about new tech, old tech, everything in between, how to use the engine, how to hack the engine, how to break it. Our forums is also a really good place. If you want to let us know what you're working on, you might be featured in our community spotlight at the beginning of the stream. Make sure you post on either the forums, the Discord channel, or server, that's also a good place to do that. Next week we are actually on vacation-- yay-- so there won't be a stream. But the week after, we'll be back with a group of students who have developed amazing projects as part of their graduation. And they're coming on to talk a little bit about what it's like being a student using Unreal Engine and learning how to create video games. And then we get to see some really cool things of what they made. Once again, thanks to Ryan Brucks for coming on the stream. Anything you want to end with? >>Ryan: Thank you guys for tuning in on the water stuff. I don't know if I still have the screenshare. I forgot to show that you can do the material layers on landscape with the water bodies as well. All you do is you add to this Layer Weightmap Setting. For example, here, we're using it to make wetness where the river is. And we're using it to add in kind of a simplified sand. So we would reduce all those bumps and stuff. And you get like full texture support for all this. And you can offset it and do crazy stuff. So yeah, hopefully it's self-explanatory. But people might want a way to do that for having wetness with their water bodies and stuff. Not that there's not other ways to achieve that. That's just one cool way, just check a box, done. But yeah, thank you guys so much for tuning in today. I had a lot of fun talking about water. And look forward to getting it into people's hands. >>Victor: Sounds good. Y'all stay safe out there. And we'll see you again in two weeks. Bye, everyone. >>Ryan: See ya.
Info
Channel: Unreal Engine
Views: 181,029
Rating: undefined out of 5
Keywords: Unreal Engine, Epic Games, UE4, Unreal, Game Engine, Game Dev, Game Development, water, caustics, fortnite
Id: XHugDXx6c5c
Channel Id: undefined
Length: 159min 46sec (9586 seconds)
Published: Fri Sep 25 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.