SkinnedMeshDecals icon indicating copy to clipboard operation
SkinnedMeshDecals copied to clipboard

Perspective distortion issue

Open HeavyArmoredMan opened this issue 3 years ago • 1 comments
trafficstars

Hi! Thank you for making this open source plugin. When I project a decal onto a surface parallel to the camera's clipping plane, it looks pretty good as shown in Pic 1. The size of the decal is just right. 20221019042800

The problem is when I project the decal onto a surface with an angle pointing towards the camera, it has perspective distortion as shown in Pic 2. The decal appears larger as it gets closer to the camera. 20221019042809

I have tried modifying the code with a perspective matrix, like changing the following lines in PaintDecal.cs

    public static void RenderDecal(Renderer r, Material projector, Vector3 position, Quaternion rotation, Vector2 size, float depth = 0.5f, string textureName = defaultTextureName) {
        // Only can draw on meshes.
        if (!(r is SkinnedMeshRenderer) && !(r is MeshRenderer)) {
            return;
        }

        if (!r.TryGetComponent(out MonoBehaviourHider.DecalableInfo info)) {
            info = r.gameObject.AddComponent<MonoBehaviourHider.DecalableInfo>();
            info.Initialize();
        }
        // Could use a Matrix4x4.Perspective instead! depends on use case.
        Matrix4x4 projection = Matrix4x4.Ortho(-size.x, size.x, -size.y, size.y, 0f, depth);
        Matrix4x4 view = Matrix4x4.Inverse(Matrix4x4.TRS(position, rotation, new Vector3(1, 1, -1)));
        
        // We just queue up the commands, paintdecal will send them all together when its ready.
        instance.commandBuffer.Clear();
        instance.commandBuffer.SetViewProjectionMatrices(view, projection);
        info.Render(instance.commandBuffer, projector, textureName);
        Graphics.ExecuteCommandBuffer(instance.commandBuffer);
    }

to this one:

    public static void RenderDecal(Renderer r, Material projector, float fov, Vector3 position, Quaternion rotation, float size, float depth = 0.5f, string textureName = defaultTextureName) {
        // Only can draw on meshes.
        if (!(r is SkinnedMeshRenderer) && !(r is MeshRenderer)) {
            return;
        }

        if (!r.TryGetComponent(out MonoBehaviourHider.DecalableInfo info)) {
            info = r.gameObject.AddComponent<MonoBehaviourHider.DecalableInfo>();
            info.Initialize();
        }
        // Could use a Matrix4x4.Perspective instead! depends on use case.
        //Matrix4x4 projection = Matrix4x4.Ortho(-size.x, size.x, -size.y, size.y, 0f, depth);
        Matrix4x4 projection = Matrix4x4.Perspective(fov, 1f, 0.1f, depth);
        Matrix4x4 view = Matrix4x4.Inverse(Matrix4x4.TRS(position, rotation, new Vector3(size, size, -1)));
        
        // We just queue up the commands, paintdecal will send them all together when its ready.
        instance.commandBuffer.Clear();
        instance.commandBuffer.SetViewProjectionMatrices(view, projection);
        info.Render(instance.commandBuffer, projector, textureName);
        Graphics.ExecuteCommandBuffer(instance.commandBuffer);
    }

I pass in the fov of the camera, together with the correct position and size, the decal can be successfully projected onto the mesh, but the perspective distortion is still exist, almost exactly the same as using an orthographic matrix.

So what is the proper way to eliminate the perspective distortion?

HeavyArmoredMan avatar Oct 18 '22 21:10 HeavyArmoredMan

Using the orthographic projection should eliminate the perspective distortion... My guess is that perhaps you have dilation enabled? (Found in the PaintDecal singleton behavior serialized properties)

Dilation would try to grow the decal by 1 pixel all the way around, depending on how the car was uv unwrapped this can make the decal appear to have a perspective distortion.

You seem to be using it exactly as I intended otherwise though, what's happening is that I'm bringing the vertices into clip space, which I just read as UVs in the shader. The transformation from world space to clip-space I know can do some crunching, but this crunching would be exactly right for sampling the uvs.

There's a snippet of video here that's really helpful for visualizing what "clip space" is and what I'm kinda doing: https://youtu.be/cWpFZbjtSQg?t=267

I assumed that Amplify would be doing the perspective divide for me (I'm pretty sure it does), and I'm not sure where else the problem may lie.

naelstrof avatar Oct 19 '22 08:10 naelstrof