Matrix calculation for mirror Camera

Godot Version

v4.2.2.stable.mono.official [15073afe3]

Question

I’m trying to make mirrors for a game, as realistic as I can. I played around with reflection probes but too many of them were laggy (I need a lot of mirrors sometimes)
So I went back to the old classic: a camera pointing at the player.

Test scene is setup like this:
Level

  • Ground
  • Player
    • MainCamera
  • PlaneMirror
    • Subviewport
      • Camera

Problem: to make it realistic the camera would need to move and rotate in opposed sync with the player, using the mirror as its “pivot”
Looking everywhere, the closest I could find to what I wanted was an old unity tutorial for portal making: tuto. I thought “that’s what I’m doing except the entry and exit portal are the same plane, I just need to adjust for that”
And using it I got a result where the camera rotation was a perfect match, but not the position. The mirror camera would go to the center of the mirror and not move to match the player mouvements.
So I tried to tinker with positions etc, and I kinda got somewhere? But not really. It’s still not fully accurate.
I feel like I made a mess of the whole matrix thing and if I could figure it out it would solve most of the issues but I’ve no idea where to start.

using Godot;

public partial class MirrorCamera : Camera3D
{
	public Node3D playerCam;
    [Export] public Node3D _mirrorMesh;
    Transform3D _mirrorTransform;


	public override void _Ready()
	{
        
		playerCam = GetTree().Root.GetChild(0).FindChild("Player", true, false).GetNode<Node3D>("MainCamera");
        _mirrorTransform = _mirrorMesh.GlobalTransform;
	}

  public override void _Process(double delta)
    {
        //Calculate the reflection matrix
        Transform3D reflectionTransform = _mirrorTransform *  playerCam.GlobalTransform;
        GlobalTransform = reflectionTransform;
        float distanceZ = _mirrorMesh.GlobalPosition.Z - playerCam.GlobalPosition.Z;
        float distanceX = _mirrorMesh.GlobalPosition.X - playerCam.GlobalPosition.X;
        GlobalPosition = new Vector3(GlobalPosition.X + distanceX, playerCam.GlobalPosition.Y, _mirrorMesh.GlobalPosition.Z + distanceZ); 

    }

}

Thanks in advance to anyone that would take a look.

I found a solution after a loooong while of trying just about everything, and dusting some old geometry school notes.
Leaving the solution here if anyone want it:

using Godot;

public partial class MirrorCam : Node3D
{
	public Camera3D playerCam;
    public Node3D player;
    public MeshInstance3D _mirrorMesh;
    Node3D playerMimic;

	public override void _Ready()
	{
        playerMimic = new Node3D();
        _mirrorMesh = GetParent() as MeshInstance3D;
        GetParent().CallDeferred("add_child",playerMimic);
        player = GetTree().Root.GetChild(0).FindChild("Player") as Node3D;
		playerCam = player.GetNode<Camera3D>("MainCamera");
	}

  public override void _Process(double delta)
    {
        //PHYSIC CLASSES DE 5e MY OLD NEMESIS
        float angleOfIncidenceY = Mathf.RadToDeg(_mirrorMesh.GlobalRotation.Y- player.GlobalRotation.Y);
        float reflectiveY = -_mirrorMesh.GlobalRotation.Y - Mathf.DegToRad(angleOfIncidenceY);

        float angleOfIncidenceX = Mathf.RadToDeg(_mirrorMesh.GlobalRotation.X - playerCam.GlobalRotation.X);
        float reflectiveX = -_mirrorMesh.GlobalRotation.X - Mathf.DegToRad(angleOfIncidenceX);

        //Calculate the reflection rotation
        Vector3 reflection = new Vector3(reflectiveX, -reflectiveY, GlobalRotation.Z);
        GlobalRotation = reflection;

        //copy player mouvement rotation etc
        playerMimic.GlobalPosition = playerCam.GlobalPosition;

        //reflected position
        Position = new Vector3(playerMimic.Position.X, playerMimic.Position.Y, -playerMimic.Position.Z);
    }

}

And the shader that goes with it

shader_type spatial;
render_mode unshaded;

uniform sampler2D albedo:hint_default_black, source_color;

void fragment() {
    vec3 portal_color = texture(albedo, vec2(-SCREEN_UV.x, SCREEN_UV.y)).rgb;
    
    ALBEDO = portal_color;
}
3 Likes

Im also trying to implement mirrors, so I have two questions:

What do you need the shader for if you could just use a viewport texture?

Is there a way to only render the stuff in front of the mirror? I ran into the problem that a room behind the mirror was rendered as well, since the camera is moved behind the mirror.
Thanks!