SkinnedMeshDecals
SkinnedMeshDecals copied to clipboard
Perspective distortion issue
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.

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.

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?
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.