Best water shader tutorial I could find. Good job mate!
@RioTFF
2 жыл бұрын
I'm using UE, but this was extremely informative even though I'm using blueprints instead of shadergraph. Thank you!
@danixadem
2 жыл бұрын
Post more more more more and more shader videos mate 👍
@grantsgraphics7192
2 жыл бұрын
Thanks, I'll do my best
@vip3r_hd210
2 жыл бұрын
Thanks for an in depth view to shaders
@huttonamison-addy
Жыл бұрын
The tutorial was great. It just left with figuring out hiw to implement an underwater render
@user-so3nj7rj1d
2 жыл бұрын
Yo this is great, thank you!
@gerardoArF
2 жыл бұрын
really thanks for sharing. I'm learning a lot
@O2Dev
9 ай бұрын
Simply amazing! I'm working with URP right now but could follow almost everything (just missed tessellation and the depth blur). I'd still like some way to do depth blur with URP! Thanks for the video, 10/10!
@JustinS06
2 жыл бұрын
Great tutorial with a nice explanation, though please make your shader a bit more organized because sometimes I just had to look through my shader for 10 minutes to find the thing you were editing. The water looks truly amazing, I didn't believe I'd make something like this (even with a tutorial...). I love the explanations and appreciate what you've done. Please keep making them! You definitely earned yourself a like and sub!
@grantsgraphics7192
2 жыл бұрын
Yeah sorry about that, keeping the shader graph organized is a struggle. In hindsight I should have used subgraphs, not sure why I didn't.
@readyforlol
2 жыл бұрын
@@grantsgraphics7192 On top of that, making chapters in the video for each major feature of the shader could help a lot.
@stokaty
2 жыл бұрын
Great video, very helpful
@NathoSteveo
6 ай бұрын
rippa tutorial and explanations brother ty
@gerardoArF
2 жыл бұрын
Nice. Thanks!
@songerk7102
2 жыл бұрын
I am trying to do it with URP instead of HDRP, putting the Vector 3 at the position instead of Tessellation( 13:10 ) wouldn't work, it will show the plane as circle instead of ocean wave
@grantsgraphics7192
2 жыл бұрын
Yeah, my fault for not mentioning this in the tutorial. You’ll need to add the vector 3 to a position node set to object space, and output the addition of the two into the position offset.
@pw9568
Жыл бұрын
@@grantsgraphics7192 For me it needed to be transformed from world to object firstly and after that added to vertex position
@narenjj
2 жыл бұрын
Wow.. great work.. thanks. I wanted different foam for crest and edge.. Could you help me out please?
@ZenoFar853
Жыл бұрын
I hope if u can do with urp
@nickcapria817
Жыл бұрын
my screen position node is missing and I am unsure what to use instead of it.
@cgimadesimple
Жыл бұрын
great :)
@kopfstroh
8 ай бұрын
My water looks like the black one you had before changing the surface type from opaque to transparent, but after changing it to transparent it still looks like your black opaque one. Im on the built in render pipeline, does that maybe have anything to do with that?
@grantsgraphics7192
8 ай бұрын
Yes I believe you need to manually enable the depth texture with a script by setting Camera.depthTextureMode to DepthTextureMode.Depth or DepthTextureMode.DepthNormals. docs.unity3d.com/2023.3/Documentation/Manual/SL-CameraDepthTexture.html
@MrAlexandresp9
2 жыл бұрын
First of all, thank you for such an awesome tutorial. I had to do a few modifications to work on my project, but in overall, you helped alot! I have a question: If I need to sample the height in a specific world position, how would I do that? I'm looking for implementing a bouyancy mechanic and a functional boat system.
@grantsgraphics7192
2 жыл бұрын
I believe the most common way is to just calculate the height function again in a script, using the X, Z position of your buoyant object. There are of course ways to get data back from the GPU, though sometimes they result in a bottleneck. I believe you can write data to buffers, denoted RWStructuredBuffer, and textures, RWTexture2D, in a shader, and then sample that data from the CPU. Typically this is slow, however you could (and should) use AsyncGPUReadback to avoid bottlenecking. I've never used it myself but if you want to take this approach you should look into it. Furthermore, you would need to figure out how to write shaders for the HDRP, and I don't think there's a lot of good documentation / resources. Alternatively, you could calculate the heightmap in a compute shader that writes to a texture2D, and sample the texture in your vertex shader and the CPU (using AsyncGPUReadback). *I think.*
@grantsgraphics7192
Жыл бұрын
Sorry, after rereading this I realize I didn't answer you properly. Yes, since we are using gerstner waves we have horizontal displacement, and therefore running the function on an (x, z) position does not actually return the height at that point. However, we can approximate the height by running the height function several (2-4) times, and offsetting the new sample point based on the horizontal displacement multiplied with our wave tiling factor on each iteration. Take for example the point (2, 3), and tiling factor 0.1. Since our tiling is 0.1 we first multiply it with our point to get (0.2, 0.3). This is our initial UV coord, which we need to save. We then run the displacement function with this point. This will return some displacement vector3 value (x, y, z). We then take the X and Z components of this vector and multiply them with our tiling parameter, 0.1. Then subtract the vector2 (x, z) component vector from our initial UV coord (0.2, 0.3). So, the equation would look like (0.2, 0.3) - (0.1 * (x, z)) = new UV coord. You would then run the height function again with this new vector2 as the new UV coord and repeat the steps. You will always subtract from the initial UV coord (0.2, 0.3). Code could look something like this: float2 uv = float2(worldSpacePos.x, worldSpacePos.z) * _waveTiling; float2 uvStep = uv; float2 sample0 = ( **Execute height function using uvStep and grab x, z components** ).xz; uvStep.x = uv.x - (sample0.x * _waveTiling); uvStep.y = uv.y - (sample0.y * _waveTiling); float2 sample1 = ( **Execute height function using uvStep and grab x, z components** ).xz; uvStep.x = uv.x - (sample1.x * _waveTiling); uvStep.y = uv.y - (sample1.y * _waveTiling); float2 sample2 = ( **Execute height function using uvStep and grab x, z components** ).xz; uvStep.x = uv.x - (sample2.x * _waveTiling); uvStep.y = uv.y - (sample2.y * _waveTiling); float finalSample = ( **Execute height function using uvStep and grab y component** ).y; You run the height function an arbitrary number of times, 2-3 extra is probably good enough. Of course, you can see the performance downside of this since we are recalculating the height function multiple times. Ideally, we would want to store the wave displacement data in a texture generated in a compute shader, that is then sampled in the vertex shader as well as on the CPU using Async readback, replacing all the height calculations with a single texture sample. The value of this would only increase if you wanted more complex water by adding in more gerstner waves.
@Yas_Gur
Жыл бұрын
WOW this was a great tutorial! I was able to complete it after 6 hours. I have a question, how would you go about to render material under the plane perfectly without the shader affecting it? I was able to switch it from Lesser than to Always but the water color is impacting the object material . Thanks, and again amazing tutorial!
@grantsgraphics7192
Жыл бұрын
Yes, changing from Lesser to Always is just changing the depth testing method the material uses for rendering. It's still rendering before the water because it has a lower render queue. I think the best way to achieve this would be to use the stencil buffer. The stencil buffer is an 8 bit mask, per pixel, 00000000. Shaders can read and write to and from the stencil buffer. What you'll want is for the object you want to render over the water to write some value to the stencil buffer, like 1 (changing the stencil buffer to look like 00000001), and then read from the stencil buffer with the water material (with the reference value set to 1). Wherever the stencil value is not equal to 1 (our reference value), we want our water to render. You would probably also want to set a read mask to the first bit of the stencil, which can be done with a 1 (a mask of 00000001), since HDRP writes to some of the other bits in the stencil (I think the 4 leftmost bits according to the documentation). Also, ex: using 15 for the read mask would give a mask for the 4 rightmost bits, 00001111. This is, I believe, the standard method to not render water inside of boats, although keep in mind you may need an inner and an outer boat material for this, the inner one being the one that writes to the buffer. That, or you can use the pass back and pass front options with a 2 sided material, if the boat mesh is a plane. Take a look at the unity documentation on the stencil buffer: docs.unity3d.com/2023.1/Documentation/Manual/SL-Stencil.html. This is also one of the caveats with shadergraph, since you can't modify the stencil buffer through it. You would instead need to generate the shader code and manually modify the stencil block there. Of course now, if you want to make changes to the shader with shadergraph, you will have to regenerate the code every time and modify the stencil block every time. Or, you could try to make changes on your generated shader code, but you would probably need to refactor the variable names. It's unfortunate that shader graph doesn't have stencil support, and it might be somewhat overwhelming, but this is the proper way to do it.
@Yas_Gur
Жыл бұрын
@@grantsgraphics7192 thanks for the fast reply!! Interesting! I will def take a look at stencils buffer. Another question that I have : Is it possible to extract the height of the water at a specific location? So that your buoyancy gameobject follows the wave. From what I understood we are not changing the vertices of the plane. Thanks again!
@grantsgraphics7192
Жыл бұрын
@@Yas_Gur Yeah all the vertex transforms are occurring on the GPU, so we need to recalculate the height function at a specific point on the CPU. We then take the returned height, and add that to the water plane/ocean height. I left a pretty detailed comment here as a response to someone asking the same thing, take a look at it and hopefully it will help.
@chifuranger8590
Жыл бұрын
I don't understand how you made that plane made by many other plane
@grantsgraphics7192
Жыл бұрын
It's just a bunch of unity's default planes placed manually.
@andreistan2830
Жыл бұрын
I watched it all, but I am far from the effect you have. Seems like my planes are not not moving (static waves). Time node has no effect on them. Mind if you share this shader in comments or description?
@grantsgraphics7192
Жыл бұрын
You may need to toggle on always refresh in the scene view. I can put the project on GitHub
@manuelaldairmorenojurado5935
Жыл бұрын
@@grantsgraphics7192 could you upload it please?
@user-wz3bp4pm4z
Жыл бұрын
My plane doesnt show any animations and i have rewatched the first 15 minutes for 3 times now, but can't find why it doesnt work? Any help?
@grantsgraphics7192
Жыл бұрын
You might need to enable always refresh in the scene window.
@user-wz3bp4pm4z
Жыл бұрын
@@grantsgraphics7192 Ok thx that worked! But now I have this copperish effect on the waves and that seems tobe following the camera? Any ideas?
@grantsgraphics7192
Жыл бұрын
@@user-wz3bp4pm4z I'm not sure about a copper effect, but make sure you're using 'Absolute World Position' for your uv, opposed to 'World Position'. The copper effect could be from skybox sampling; your normals might also be flipped or incorrect.
@antonclark608
Жыл бұрын
Im having trouble with the refraction, I follwed everything step by step but when I raise the refraction value the object being refracted actually mooves away from the main object. This looks real weird. Otherwise awesome tutorial your work is so cool.
@grantsgraphics7192
Жыл бұрын
The refraction strength parameter should be multiplied with the R and G channels of a tangent space ripple normal map, and then those R and G channels should be added with the respective R and G channels of a screen position node. The reason the object is moving is that every position of the screen position node is having some value added to it, instead of only where the ripples are. First, check and make sure your ripple normal map is a tangent space normal map. You may be using a world space normal map, in which case the refracted channels would be the R and B. You would still add these to the R and G channels of the screen position node. Also, make sure you're just using the ripple normals, not the ripple normals blended with the normal from height.
@michaelcross8392
Жыл бұрын
This is really awesome. One question. Lets pretend I have cube, and I wanted to use a shader with waves like this. Any idea how to make it so the waves only move the top vertices lf the shape without detaching frk. The sides?
@grantsgraphics7192
Жыл бұрын
I'm a bit confused about what you mean, but you could paint a weight mask using vertex colors if you don't want certain vertices to move. You would only need one vertex color channel, like the R channel, which you would then multiply with the displacement before outputting it. Obviously painting 0 will result in no displacement, while 1 will be full displacement. I believe Unity or the asset store has a free vertex painting tool. You could also modify the vertex positions in UV space to control the weights, if you aren't using your UVs for anything else. This is convenient because you can have each cube face take up the full UV space, and then remap the UV coordinates into an edge mask (like I did at 01:19:23 for the screen mask, same concept). Also, I'm assuming the cube you're talking about is a cube with one tessellated face, and five other basic quad faces. The faces of a cube mesh aren't usually connected (in order to get hard edge normals), so each corner of a cube is actually 3 vertices in the same spot (and a cube mesh is actually composed of 6 [quad] sub meshes). Thus, if your top face is tessellated and the faces surrounding it aren't, then yes, the vertices will disconnect from the vertical faces (since they were never connected to begin with). I would recommend you just make a custom model in a 3D program like Blender or Maya. The 4 faces adjacent to the top face will need to have horizontal subdivisions equal to the number of subdivisions on the top face, like this: gyazo.com/4585a9a9ead7af2ce1f9c3752b5a38f0.
@michaelcross8392
Жыл бұрын
@@grantsgraphics7192 well this makes some sense( I'm really starting out pretty fresh) I did create my cube in blender and tesselate the top face so that it the waves were waves and not two triangles. I think my problem is I'm so new I don't fully understand. My current semi working solution was to take the object position and then split it and feed the y node into a step of .9 so anything .9 and above in the object started to move and it shorta worked.
@grantsgraphics7192
Жыл бұрын
@@michaelcross8392 No worries, it's an obscure thing that I wasn't aware of at first either. Also, there are multiple ways to create masks for the effects you need. Your method of using the object space Y position is perfectly viable for anchoring the 4 bottom corners of the cube in place. I only mentioned vertex colors because people usually forget about them, and it takes 0 additional calculations to create the mask, as long as the values are in the 0-1 range.
@andreistan2830
Жыл бұрын
Where did the starting equation in Desmos come from?
@manuelaldairmorenojurado5935
11 ай бұрын
I have the same question, did you figure it out?
@grantsgraphics7192
8 ай бұрын
I think I made some mistakes with that equation, and it can be simplified a lot. I wanted to solve for *a* in the equation: y = cos(axᵇ) I raised *x* to a power, *b* , because I wanted frequency to increase as *x* gets larger This means I had to multiply by some value, *a* , to get the cos wave to tile in the 0 - 2π range I know that I want this to tile in the 0 - 2π range, so I can set y = 1, and x = 2π, because cos(2π) = 1 and cos(0) = 1 Then it's just a matter of doing some algebra y = cos(axᵇ) 1 = cos(a(2π)ᵇ) arccos(1) = a(2π)ᵇ 0 + 2π(k) = a(2π)ᵇ arccos(1) is equal to every 2π(k) radians, where *k* is a whole number; _cos(2π(k)) = 1_ 2π(k) / (2π)ᵇ = a 2π / (2π)ᵇ = a can disregard *k* since it would always be 1 because I set x = 2π initially instead of x = 2π(k), _but there is technically a hidden k equal to 1_ Then you can just multiply everything by 0.5 and subtract it from 0.5 to bring it into 0 - 1 range
@hellpriestik
7 ай бұрын
cant make it to work at all in urp =(
@A_S__N_S
2 жыл бұрын
thank You for Tutrial but i dont find the Tessellation in Lit shader HDRP where i get it ?
@JustinS06
2 жыл бұрын
I could find it an Im using 2021.3.0f. Maybe try looking again?
@manuelaldairmorenojurado5935
11 ай бұрын
hi! awesome video! could you help me with the normal maps that you use? thanks
@grantsgraphics7192
8 ай бұрын
They were made in substance designer
@gzltrece6558
2 жыл бұрын
I am confused that the normals depend on which formula ?
@grantsgraphics7192
2 жыл бұрын
I used the 'Normal from Height' node that comes with shader graph. Normals are generally recalculated from height maps using the partial derivative functions ddx and ddy, or from recalculating the height function 2 more times and sampling slightly offset points, then taking the cross product of those 2 vectors. You can see Unity's implementation here: docs.unity3d.com/Packages/com.unity.shadergraph@6.9/manual/Normal-From-Height-Node.html
@gzltrece6558
2 жыл бұрын
@@grantsgraphics7192 Thanks for your reply!I have benefited a lot from it.Thanks!
Пікірлер: 57