If you need help/did not understand something, feel free to ask in the comments! ATTENTION: As pointed out by GDQuest it's better to use *get_viewport_transform()* instead of *global_canvas_transform()* .
Was about to say this ! I wish I found your comment earlier, that would have saved me from trying to find where to invert a + sign into a - for half an hour ^^ (I try, I try, but shaders are still a bit voodoo magic for me sometimes ^^) Also, don't forget in G4 to enable "repeat" on your noise texture ( it's not inherited from project settings -> render like other texture resources), like so : uniform sampler2D noise: repeat_enable;
Couple more Godot 4 notes: `tool` needs to be `@tool` SCREEN_TEXTURE is deprecated, but the error in the editor explains how to fix it :) Thank you for this tutorial!
For anyone else who runs into this issue. This looked ok for me in the editor, but it looked all messed up when launching the game. I fixed it by using this instead: material.set_shader_param("y_zoom", get_viewport_transform().get_scale().y) Hope that helps
Cool tutorial! A note about using the global_canvas_transform: it will give you inconsistent results between the editor and the game view, and when you change the window's size - because the global canvas transform changes with the viewport size. You should use the relative canvas_transform of the viewport instead. You can use get_viewport_transform() to get it directly from any CanvasItem.
Thanks for the tip! I haven't had issues with global_canvas_transform so far, maybe when I change the stretch mode? Anyways I'd love to have something like this in Godot : github.com/godotengine/godot-proposals/issues/1078#issuecomment-645490060
Although I only fully grasped like 30 percent of what was being done, what I got in the end was fully functional. I am a beginner programmer and this level of programming is still a bit beyond what I can comprehend. A week ago I learned what a variable was haha. Thank you for the tutorial, I am going to definitely check out your other work.
I've been programming for a long time, and shaders are still quite mysterious to me. :D When they start _"add this multiplied by that, times the sin of this divided by the cosine of that, times two divided by the other...",_ it's like getting lost in a maze. No idea how they came up with some of this stuff. :) One of these days I'll read The Book Of Shaders. Heard good things about it. Maybe it demystifies it.
@@Nolkaloid In part, yes. But I'll have to watch it again, because I didn't assimilate some stuff. The thing is, and I realized this a few hours ago, for the longest time I've been having this barrier with some terminology still being abstract to my brain. For example, when you used the term _"sample"_ (as a verb) there was an immediate disconnect in my understanding. I just didn't know what you meant by it. After thinking about it, (and correct me if I'm wrong) I realized sampling just means picking a color from a source. This should probably be obvious, given that essentially all a frag shader does is working with colors, but my brain was still not putting all these basic things together. Another barrier I've been having is not knowing what values are going through the pipe, sometimes. For example, I read a few pages in the Book Of Shaders a few hours ago, and my initial assumption with _gl_FragCoord_ was that it was a normalized position. After playing around with it, I'm thinking maybe it isn't. Unfortunately, the book isn't explicit about this (unless I overlooked it), and iirc neither were many tutorials I watched in the past. And not knowing these things for sure can make it feel like a blind man trying to create a painting. Because of course we can't just print out the values. :)
A little tip on top of this: if you want to shrink or enlarge the reflection vertically, you can multiply the viewport transform by a factor above or below 1, respectively. I shrunk mine to add a little bit of a 3D-ish perspective effect, by multiplying by 1.2. I may still bump it up a bit. Seems to do the trick without issues.
@@Nolkaloid Since I'm doing a clone of Kingdom, unfortunately it's not as simple. The old flash version doesn't do this. The more recent versions do, but not to the reflections, only to the foamy waves on the water surface. The foam seems too random to not be a shader step, but it's the only thing in 3D. I'm wondering if it's actually a translucent 3D plane on top of the water, with a shader of its own... I could try that too. Though I could still do what you suggested, and make it a personal touch, but I'm not quite getting it. I tried multiplying the *reflected_screen_uv.x* by the *UV.y,* and things of that sort, but that only results in the bottom being skewed to one side... The stretching must go both ways, and also has to be relative to the center of the screen, rather than the center of the texture, but beyond that I'm kinda clueless atm... Also the stretching must account for pixel size, such that the closest (bottom-most) pixels aren't bigger than the others.
Thank you for you awesome shader tutorial! Shaders are something I gotta learn sometime; but this one saved me! Now time to maybe work on actual gameplay instead of just water visuals for my game
HI Nolkaloid, why screenuv.y + 0.375 * uv.y * 2 can flip the image 。。。。i can't understand ....in my mind ,i thing it will be offset by 0.75*uv.y.......
amazing video man, I'm using godot 4.0.3 and your tutorial works, didn't even run into a single problem! I'm curious though, the waves at the start of the video have like a white blanket on top of them, how did you do that? edit: nvm, got it myself, forgot that you mentioned it at the end of the video lol :D
Thank you so much for the tutorial! I have a question, is there any way to make the distortion of the water more consistent between the Maximized v.s. Windowed game?
Please forgive me for this really noobish question, why does multiplying the offset by UV.y and 2 flip the image? My math isn't really that good and I'm struggling to really understand that part.
No worries, the idea is the following: Basically as we move towards UV.y = 1.0 (the bottom of the sprite), we want to read the screen that's above in a "mirrored manner". So when rendering the line at UV.y = 1.0, we want to read the screen texture at SCREEN_UV + SIZE_OF_THE_SPRITE × 2 (where SIZE_OF_THE_SPRITE is the offset as explained in the video). On the other hand, at UV.y close to 0.0, we want to read the SCREEN_TEXTURE that is just a tiny bit above. And in between I want a linear progression, so that's why we multiply by UV.y If it's still not clear I can send you a diagram explaining it visually.
This reflects properly in the scene viewer, but when I run the scene, it does not reflect what is above it, only distorts what the sprite is directly on top of. Please advise.
@@Nolkaloid It turned out that the issue stemmed from using Mode 2D; Aspect Keep with a small native resolution, increasing to a larger resolution on screen. I fixed it by multiplying the reflected screen UV.y by a window_resize_ratio that is the ratio of the native resolution, in my case, 480x270 to the screen resolution (aka test_width, test_height), in my case, 1920x1080. By hooking up a signal to the screen resize, I am able to calculate the ratio on the fly and send to the uniform in the shader. Thank you again for this tutorial!
Great tutorial! I'm making a pixel art game and the reflection part worked perfectly, but the distortions look a bit muddy because of the way Godot scales down the image. Do you know if there's a way to use something like nearest neighbor scaling for the shader or some other way to snap it to pixels?
@@Nolkaloid Thanks, it looks better now! Looks like some interpolation is still being done (the reflection has colors that the original sprite doesn't) but it looks good now so it isn't a big problem. Thanks for the video and the reply!
is there a way to make this work with parallexLayers/CanvasLayers? Have other sprites in front this shader-sprite and render their reflection below them on screen where this sprite is visible. Tried using it as is, but the reflections don't work kinda the way I would like them to. If I have it in front canvaslayer, it renders it on top of it those sprites, but if this shader sprite is behind everything else, it wont render any reflections on it.
I have one question: how to deal with light?? for example I do not want the shadow to be reflected, what should i do? I set render mode but does not work. also, i tested AT_LIGHT_PASS, still not work. and another question: how to mask out some thing? that is , i want to reflect something and others do not . do you think this is achievable?
@@Nolkaloid I just set the texture of the sprite to repeated, but nothing changed. I'm assuming I have to set the noise texture to repeat. Not sure how to do that tho.
@@animebinger7974 You need to add the repeat_enable hint to the sampler2D declaration : uniform sampler2D noise : repeat_enabled; docs.godotengine.org/en/stable/tutorials/shaders/shader_reference/shading_language.html#uniforms
How would you update it to work with an isometric view 2D game? What I mena is that the relection should only apply to the top part of the sprite. Anyway thank for the tutorial great videao! You deserve my follow up!
Thanks for this tutorial. It helped me get the water working on a small clone of Kingdom (the old flash version) that I'm trying to make in Godot. :) EDIT: do you happen to know how I could pixelate it? The shader distorts things to float positions, and I'm needing it to snap to pixels, but not real pixels, rather the pixels being scaled up from a low resolution. I have no idea how, though.
Hey! I'm glad I could help! To pixelate the texture, you will need to sample the texture "per block", UV coordinates evolve linearly, we want to make that evolution "blocky": www.desmos.com/calculator/eqgpc3phow
@@Nolkaloid well, snapping values to steps/grids is something I've done before, but in the shader code I'm kinda clueless of which values to snap... I understand what you mean by the UV evolving blocky, but I'm still confused at how to do it... It also doesn't help much that because I'm using a scaled resolution, what I see in the editor doesn't match what I see in game...
@@Nolkaloid Thanks. No hurries though. I was thinking of also bringing this up on discord, but what I got now is actually already pretty good, so I can leave it as it is for now and move on to other stuff. I still need to turn the game into more than just a character moving on an empty map with pretty water. :)
Thanks for the awesome vids and explanations ! I know understand better shaders and how they interact with gdscript. However, I encounter a problem when the tile reflected are not in the viewport anymore they are not reflected. I can't see in the video if you have the same problem but when you lower the camera enough to make the reflected sprite cut in half, only the visible half will be reflected. The rest will glitch or disapear. Do you have any idea how to fix that ? Thanks !
Hey, it's normal behavior, as this a screen space effect. We are using the screen texture, once an object is no longer visible in the view, it's simply not rendered so it's not accessible in the screen texture.
Question: Your tutorial works well for most sibling nodes in the scene tree, but it doesn't reflect some things when I switch around things in the scene tree. For example, children of sibling nodes are not reflected. Sprite nodes are not reflected when they are below the Water node in the scene tree, but particle nodes are reflected in this way. Since I'm instancing the water in a complex scene where the player sprite is a child of a KinematicBody2D, do you know how to get nodes with different parents to be reflected? For instance of getting BadSprite reflected by Water: Root BadParent BadSprite Sprite Water Particles2D It's not related to in-game or editor zoom nor waves as I didn't implement those parts of the tutorial.
Hi, you need to put the water as the front most node, so the lowest in the tree. This is because rendering is done in from the highest node to the lowest node in the tree. The screen texture in the shader contains only what was rendered before, so it makes sense that nodes that are lower won't appear on the screen_texture. As for particles, I'm quite surprised, It has probably something to do with how the rendering pipeline works...
@@Nolkaloid I figured it out. The rendering pipeline renders based on the z_index of the nodes, so the issue was unrelated to the scene tree node structure. I increased the z_index of the water above everything I wanted reflected, and now it works perfectly. Great tutorial!
@@josephcantrell1 Yup, it renders from back most to front most, thus according to the tree. Of course if you change the z-indexes of some of the nodes, the rendering order will get perturbed.
Going down the shader rabbit hole again these are so good I hate them. How am i supposed to just know how to do that. My weakness in Godot. Or any engine as a matter of fact.🤣🤣
@@joseramiromartinez388 Are you sure you are multiplying the correct member in the expression? Also the scale.y should be set accordingly to the scale of the sprite, so if the tool script doesn't work in will display nothing, because by default the scale vector is (0.0, 0.0).
This does not work well in version 4, there is an illustrated bug: ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-5DJKV5FyKNc.html If you can help me?
Hi, this is not related to the Godot version you're using, this is expected behavior. The shader cannot read what's above the screen (it is simply not rendered). To fix that you could render the game zoomed out (×0.5) and render it at twice the resolution. Then display the result zoomed "back" in (×2.0).
@@Nolkaloid ok I tried it didn't work, however you are right that this product when the reflected area is outside the camera, I tested with 0.5 scale and less and multiplied the effect by 2 it doesn't change anything, do you have another solution?