Search code examples
unity-game-engineshaderlight

Change plane of tile in shader Unity3D


To begin with I will explain. I’m writing a 2D game, added normal maps and lighting. All objects lie on one plane. As a result, the walls behave inappropriately and literally lie on the floor. Is it possible to write such a shader to replace the coordinates of this wall, as if it were in another plane, perpendicular to the one in which it is located and after that to do the processing?

Current plane:

enter image description here

Needed plane:

enter image description here


Solution

  • From my understanding, you want the tile to be flat on the ground, but you want it to be shaded as if it was standing up, with the light hitting it from an angle.

    There are three possible solutions for this. The first option is that you bake normals as tilted into the normal map. Consider this normal map of a sphere:

    The teal parts are pointing upwards, and the purple parts are pointing downwards. You can sample a color from an angle of your choice and overlay that color into your normal map. There is a tutorial on how to property combine normal maps here.

    Another method is to pass over a rotation matrix into your shader and use it to rotate your normal in the vertex shader.

    Here is how rotation matrices are constructed:

    enter image description here

    In the shader, you define matrices like this:

    float3x3 _RotMatrix;
    

    In the vertex shader, you would apply them like this:

    float3 normal = UnityObjectToWorldNormal(v.normal);
    o.normal = mul(normal, _RotMatrix);
    

    You can pass matrices into shaders using this function:

    Matrix4x4 rotMatrix = Matrix4x4.Rotate(rotation);
    GetComponent<MeshRenderer>().material.SetMatrix("_RotMatrix", matrix);
    

    Or, you could just pass a rotation vector and construct the matrix inside the vertex shader. Either way, you can then set a rotation offset per-material for your objects to use in the lighting calculations.

    The third option is to set custom normals for your plane model. You can do this inside the mesh - this is probably the easiest solution. Get the mesh from the mesh filter, loop over all the normals, and rotate them using a quaternion so that they face roughly the direction you want your wall to be facing.

    Mesh mesh = GetComponent<MeshFilter>().mesh;
    Vector3[] normals = mesh.normals;
    for(int i=0; i<= normals.Length; i++) {
        normals[i] = Quaternion.Euler(new Vector3(45, 0, 0)) * normals[i];
    }
    mesh.normals = normals;
    mesh.RecalculateTangents();
    GetComponent<MeshFilter>().mesh = mesh;