Confused about how unity_WorldToObject works

Hey all, I’ve been banging my head against this one for a bit but I’m having trouble finding a clear answer. I’m working on modifying a shader I found that implements decals using HLSL: How do i implement decals into a shader in hlsl

It works very nicely for default Unity primitives, but breaks on other objects. For example, here are 4 objects. The left 2 are a Unity cube and sphere, and the right 2 are a 3D modeled cube from Blender and a Bus model I found:

With the Decal shader, you can see the left 2 objects fade nicely while the right cube is completely solid and the bus doesn’t appear at all:

Debugging it, the issue seems to arise when converting the vertex from world to local space. Here’s a modified version of the shader that visualizes the localPos variable:

For reference, this is created by sampling the scene depth at the vertex position, then converting it to local space using unity_WorldToObject. My hunch is that scale has something to do with it, and I had some success multiplying the localPos by the object’s scale using code from here: Can I get the scale in the transform of the object I attach a shader to? If so, how?

However, this changes the values (which I don’t want) and still doesn’t seem to work for the bus in the bottom right.

Can anyone explain how unity_WorldToObject works, or have any guesses what I’m doing wrong?

Hey everybody, I think I more or less solved it. One issue was when importing the mesh, make sure to uncheck “Convert Units” (which scaled the model 100x converting it from Blender). The bigger thing though is it seems that the for Model/Object space, the coordinates are not normalized from 0,1. The shader assumes that (.5,.5,.5) will be the middle of the object, but that’s only true for Unity primitives and other objects that are 1m x 1m. For other objects, you need to scale the effect by the object’s size.

In the shader, I Introduced a vector property called _MeshBounds and used it to scale the coordinates:

    float3 decalClipFade = saturate((0.5 - abs(localPos.xyz / _MeshBounds.xyz)) * fade);

Then I added a script to any object using the decal effect that would get its mesh bounds and set the value in the shader accordingly:

        Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
        Bounds bounds = mesh.bounds;

        Renderer renderer = GetComponent<Renderer>();
        MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock();
        propertyBlock.SetVector("_MeshBounds", bounds.size);

        renderer.SetPropertyBlock(propertyBlock);

Not sure if this is the most effective solution, but it works :smiley: