2D mirror effect
A mirror or reflection effect. It works by duplicating what you want to be reflected in a Viewport and render the Viewport as a texture. See it in action in the image below.
This is a quite naive way of making an effect like this and has some limitations, but it works and can make for a cool effect under the right circumstances.
Instructions
To get this to work requires a little bit of setup:
- Create your player as a new scene with all code for movement etc.
- Add your player to your main scene.
- Add a Viewport to your main scene, and add a copy of your player node as a child to it. Set the Viewport’s Size property to the same size as your game (Project Settings > Display > Window). Make sure Transparent Bg is checked. Under Render Target, set Update Mode to ‘Always’.
- Add a Camera2D as a child to both of your two player nodes. The two cameras must have identical settings and both should have the Current property checked.
You should now have something like this:
Main Scene
└ Viewport
└ Player copy
└ Camera2D
└ Player
└ Camera2D
Now, make sure the ‘Player copy’ node always has the same position as the ‘Player’ node. You might have to make a new script for the ‘Player copy’ node to do this. In the _process()
function of the Player copy script, add position = get_node(pathToOriginalPlayerNode).position
. If you have code for movement or other behavior you want to be reflected in the mirror copy it over from your original Player script.
Create the Sprite which will be the mirror surface and add a texture to it. This texture will be what the reflective surface will look like, in the image above it is the blue crystal. In the Inspector under Material create a new Shader Material and paste the shader code below. Also under Material, go to Resources and check “Local to Scene” (not Resources under Shader). This makes it possible to use a Viewport as a texture in the shader.
In the shader parameters choose a new ViewportTexture for Reflection Viewport and choose the Viewport you just created. If you add a normal map to the Normal Map shader parameter the reflection will refract according to the normal map and the Amount parameter. Set a very low value for Amount. In the image above the value is set to 0.03.
That is it! Now you can play around with different values. I suggest you offset the Viewport camera slightly (x: -40
, y: 20
. These values are reversed for some reason) and make the ‘Player copy’ sprite a bit larger and tint it a bit under Modulate to really sell the effect.
Known issues
The limitation with this method is that it is only what is inside the Viewport that will be “reflected”, so if you want your whole world to be reflected you will have to duplicate it in the Viewport.
If you have Smoothing and Drag Margins enabled on your cameras you might have to move around the Player a bit for the Viewport to catch up.
Shader code
/*
Shader from Godot Shaders - the free shader library.
godotshaders.com/shader/2d-mirror-effect
Feel free to improve and change this shader according to your needs
and consider sharing the modified result on godotshaders.com.
*/
shader_type canvas_item;
uniform sampler2D reflection_viewport;
uniform sampler2D normal_map;
uniform float amount : hint_range(0, 1);
void fragment()
{
vec4 color = texture(TEXTURE, UV);
// Define the distortion from the normal map
vec2 offset = texture(normal_map, UV).xy * amount;
// Offset the viewport texture with the distortion
vec4 reflection = texture(reflection_viewport, SCREEN_UV + offset);
// Alpha blend the reflection with the main texture
color.rgb = color.rgb * (1.0 - reflection.a) + reflection.rgb * reflection.a;
COLOR = color;
}
Really great shader and useful for many things!
In the gif the reflection should show the back of his head, not his face, though :/
Haha, yes that is a natural limitation of how this effect is set up. It’s a bit weird. But since the mirror is showing a copy of the player you could replace it with a sprite that shows the back of the player. It should work.
Hello, I think I may find another solutions(also have some limitations but simpler than your method)
Below is my result
https://note.youdao.com/s/MWqvLoWx
You do not need any viewport. all you need is to set the crystal image z_index greater than the player sprite, set the root viewport transparent to true , and following shader code :
this is perfect for mirror reflection, but the limitation is set brackground to true will disable glow effect, this is bad for me .
So another way is set background to some color you will never use(for me it is black color), and then the following code:
but this will have problem for transparent object. It will show black for them. So I set transparent object z_index lower than the shader to disable them(maybe you can use some mask to show them? I tried but failed…)