Search code examples
unity-game-engineshader

Cross section shader for box bounding using amplify Shader


I am trying to create shader through amplify shader for a cube to cut through plane or any mesh when cross section. I know that I should be using size, rotation and position for that but what exactly to do with them that I don't know. Yup by that it means that I am new to amplify shader and also in shader programming so please don't provide shader code as I need to make it customizable for future so please help me out in amplify shader nodes.

Currently I have this effect but I want to make it more box bounding specific not plane normals based. enter image description here

I want not this effect but the box effect shown below. This was achieved through ray marching concept but this I want to achieve with Amplify Shader. Kindly guide me through this. enter image description here

This is what I have done so far with the amplify nodes enter image description here


Solution

  • Result:

    Here is the result of doing the shader using "Amplify Shader":

    Result

    Solution:

    First we'll call the green cube the "intersector" and the red cube the "intersectee".

    So as you've done with the plane, the cutout works because the back face of the intersector is shown when inside the intersectee and the intersectee front face is show when it is inside the intersector.

    Create a shader (which is used by both cubes) and put them into two seperate materials - apply individual materials to each cube. After this we can get into the actually shader node stuff.

    First we need to make sure "Cull Mode" is off (Output Node > Cull Mode > off). This will ensure the back face is actually rendered (This can be optimized by decided depending on where the cube is in the intersector).


    Next we need to get the surface point in object space:

    enter image description here

    Most of the variables will be defined in script. The rotation matrix is used to rotate a point. However, it is inversed as the rotation matrix rotates the cube into world space, therefore, inversing this would rotate the world space point into object space. We also get a "_Cubepos" which is the position of the cube to intersect with (E.g it would be the intersector if shader is on the intersectee). This is subracted by the world pos as the rotation matrix rotates around the origin. After this it is added back to be in the correct position.


    Extents

    This leads to the next section where the extents are added and subtracted to the "_Cubepos" and "_CubeExtent" to find the minimum and maximum extents.


    Point inside cube?

    Unfortunately, Amplify shader has no good way to check if a vector lies within two vectors. So we have to break it into components. (I encourage you to learn how to write shaders). Each compare with range returns 1 if the point in object space is within the extents for each axis. If one returns 0 we use the last multiply node to make sure the final output will be 0.


    Final output

    Finally, we get to the last part of the shader. The "IsIntersector" is set in script to be 1 or 0 depending on whether the cube we are refering to is used to intersect or is an intersectee. Depending on the scenario, here we set the opacity mask to 1 or 0.


    After this we have to define the script to attach to each object. Add a new script and type the following in:

        [ExecuteInEditMode]
    public class SetVar : MonoBehaviour
    {
        //Transform of opposite cube
        public Transform intersectingCube;
    
        //Is this an intersector or intersectee
        public bool isIntersector;
    
        //Material of object
        public Material mat;
    
        // Start is called before the first frame update
        void Start()
        {
            //Get material
            mat = GetComponent<Renderer>().material;
        }
    
        // Update is called once per frame
        void OnRenderObject()
        {
            //Calculate rotation matrix
            Matrix4x4 m = Matrix4x4.TRS(-intersectingCube.position, intersectingCube.rotation, Vector3.one);
    
            //Set shader variables
            mat.SetMatrix("RotationMatrix", m);
            mat.SetVector("_Cubepos", intersectingCube.position);
            mat.SetVector("_CubeExtent", intersectingCube.localScale / 2.0f);
            mat.SetFloat("_IsIntersector", (isIntersector) ? 0 : 1);
        }
    }
    

    Then we can set the correct inspector values depending if the cube is an intersector or intersectee. Here is an example for the intersector cube:

    Inspector

    Make sure to have the IsIntersector ticked depending if the cube is an intersector or not.


    Here is a link to the shader: http://paste.amplify.pt/view/raw/4b248bc3. Also to do this for any mesh is a very complicated operation - too complicated for nodes. Learn about shader code and use a raycasting algorithm to determine if the point is inside the cube.


    Also, alternatively for any convex shape. You could calculate each plane and then using your method already used, can check if the world position point works for every plane. For a cube there would be 6 planes, however, its a bit slower than the above method (as it is optimized for a cube).