1. 程式人生 > >Unity3D開發之自制座標軸(二)

Unity3D開發之自制座標軸(二)

我在上篇部落格寫出瞭如何用程式碼建立一個我們可以任意控制頂點得圓柱體。本片部落格著重記錄如何在空間建立類似unity可以控制物體的座標軸。由於我們要控制圓柱體的長度,所以我們設計的座標軸是兩個方塊在圓柱體的兩端。效果圖如下:

                                                                               

首先我們要繪製兩個方塊,我們知道空間顯示在我們眼前的物體都是通過由多個三角面組成的mesh顯示在我們面前的。所以我們首先建立一個屬於我們的BOXmesh。程式碼如下:

public static Mesh CreateBoxMesh(float width, float height, float depth)
        {
            // Clamp dimensions
            width = Mathf.Max(width, 0.0001f);
            height = Mathf.Max(height, 0.0001f);
            depth = Mathf.Max(depth, 0.0001f);

            // Store half dimension values for easy access
            float halfWidth = width * 0.5f;
            float halfHeight = height * 0.5f;
            float halfDepth = depth * 0.5f;

            // Prepare the vertex attribute arrays
            Vector3[] boxVertexPositions = new Vector3[24];
            Vector3[] boxVertexNormals = new Vector3[24];

            // Generate the vertices. Start with the front face
            boxVertexPositions[0] = new Vector3(-halfWidth, -halfHeight, -halfDepth);
            boxVertexPositions[1] = new Vector3(-halfWidth, halfHeight, -halfDepth);
            boxVertexPositions[2] = new Vector3(halfWidth, halfHeight, -halfDepth);
            boxVertexPositions[3] = new Vector3(halfWidth, -halfHeight, -halfDepth);

            boxVertexNormals[0] = -Vector3.forward;
            boxVertexNormals[1] = boxVertexNormals[0];
            boxVertexNormals[2] = boxVertexNormals[0];
            boxVertexNormals[3] = boxVertexNormals[0];

            // Back face
            boxVertexPositions[4] = new Vector3(-halfWidth, -halfHeight, halfDepth);
            boxVertexPositions[5] = new Vector3(halfWidth, -halfHeight, halfDepth);
            boxVertexPositions[6] = new Vector3(halfWidth, halfHeight, halfDepth);
            boxVertexPositions[7] = new Vector3(-halfWidth, halfHeight, halfDepth);

            boxVertexNormals[4] = Vector3.forward;
            boxVertexNormals[5] = boxVertexNormals[4];
            boxVertexNormals[6] = boxVertexNormals[4];
            boxVertexNormals[7] = boxVertexNormals[4];

            // Left face
            boxVertexPositions[8] = new Vector3(-halfWidth, -halfHeight, halfDepth);
            boxVertexPositions[9] = new Vector3(-halfWidth, halfHeight, halfDepth);
            boxVertexPositions[10] = new Vector3(-halfWidth, halfHeight, -halfDepth);
            boxVertexPositions[11] = new Vector3(-halfWidth, -halfHeight, -halfDepth);

            boxVertexNormals[8] = -Vector3.right;
            boxVertexNormals[9] = boxVertexNormals[8];
            boxVertexNormals[10] = boxVertexNormals[8];
            boxVertexNormals[11] = boxVertexNormals[8];

            // Right face
            boxVertexPositions[12] = new Vector3(halfWidth, -halfHeight, -halfDepth);
            boxVertexPositions[13] = new Vector3(halfWidth, halfHeight, -halfDepth);
            boxVertexPositions[14] = new Vector3(halfWidth, halfHeight, halfDepth);
            boxVertexPositions[15] = new Vector3(halfWidth, -halfHeight, halfDepth);

            boxVertexNormals[12] = Vector3.right;
            boxVertexNormals[13] = boxVertexNormals[12];
            boxVertexNormals[14] = boxVertexNormals[12];
            boxVertexNormals[15] = boxVertexNormals[12];

            // Top face
            boxVertexPositions[16] = new Vector3(-halfWidth, halfHeight, -halfDepth);
            boxVertexPositions[17] = new Vector3(-halfWidth, halfHeight, halfDepth);
            boxVertexPositions[18] = new Vector3(halfWidth, halfHeight, halfDepth);
            boxVertexPositions[19] = new Vector3(halfWidth, halfHeight, -halfDepth);

            boxVertexNormals[16] = Vector3.up;
            boxVertexNormals[17] = boxVertexNormals[16];
            boxVertexNormals[18] = boxVertexNormals[16];
            boxVertexNormals[19] = boxVertexNormals[16];

            // Bottom face
            boxVertexPositions[20] = new Vector3(-halfWidth, -halfHeight, -halfDepth);
            boxVertexPositions[21] = new Vector3(halfWidth, -halfHeight, -halfDepth);
            boxVertexPositions[22] = new Vector3(halfWidth, -halfHeight, halfDepth);
            boxVertexPositions[23] = new Vector3(-halfWidth, -halfHeight, halfDepth);

            boxVertexNormals[20] = -Vector3.up;
            boxVertexNormals[21] = boxVertexNormals[20];
            boxVertexNormals[22] = boxVertexNormals[20];
            boxVertexNormals[23] = boxVertexNormals[20];

            // Generate the indices
            int[] vertexIndices = new int[]
            {
                // Front face
                0, 1, 2, 0, 2, 3,

                // Back face
                4, 5, 6, 4, 6, 7,
            
                // Left face
                8, 9, 10, 8, 10, 11,

                // Right face
                12, 13, 14, 12, 14, 15,

                // Top face
                16, 17, 18, 16, 18, 19,

                // Bottom face
                20, 21, 22, 20, 22, 23
            };

            // Create the mesh and return it to the client code
            var boxMesh = new Mesh();
            boxMesh.vertices = boxVertexPositions;
            boxMesh.normals = boxVertexNormals;
            boxMesh.SetIndices(vertexIndices, MeshTopology.Triangles, 0);

            return boxMesh;
        }

建立後的靜態函式供我們需要繪製的時候呼叫。下面我們要設定Boxmesh 的位置,尤拉角以及縮放比。Unity中繪製mesh或者gl線段一般都在OnRenderObject函式中進行。

 protected void OnRenderObject()
    {
        Matrix4x4[] BoxWorldTransforms = GetLengthWorldTransform();
        DrawControlLineLengthAxis(BoxWorldTransforms);
    }

首先我們要設定boxmesh的Transform資訊給儲存到4*4矩陣中,

 private Matrix4x4[] GetLengthWorldTransform()
    {
        Matrix4x4[] worldTransforms = new Matrix4x4[2];
        Vector3[] localPosition = GetBoxesGizmoLocalPositions(CalculateGizmoScale());
        Quaternion[] localQuaternion = GetBoxesGizmoLocalRotations();

        // Loop through each axis
        for (int i = 0; i < 2; ++i)
        {
            Vector3 worldPosition = _gizmoTransform.position + localPosition[i];
            Quaternion worldRotation = localQuaternion[i];

            // Construct the world transform matrix
            worldTransforms[i] = new Matrix4x4();
            worldTransforms[i].SetTRS(worldPosition, worldRotation, Vector3.Scale(_gizmoTransform.lossyScale, new Vector3(_BoxWidth, _BoxHeight, _BoxDepth)));
        }
        return worldTransforms;
    }

首先來獲取boxmesh的位置。我們知道這個自制的座標軸要在圓柱的兩端。而且在上篇部落格我們看到我們的圓柱體的左右兩側就是圓柱的forward和-forward,所以獲取區域性座標程式碼如下(lastSelectedGameObject就是我們的圓柱體,tempLeftGizmoPos就是我們在上節所說的leftPos,我們可以在座標軸顯示的初始化函式中獲取圓柱體的長度Length,即tempLeftGizmoPos=leftPos=Length/2。gizmoScale就是我們根據攝像機和Boxmesh的距離來自動調節Boxmesh大小的比例係數):

 private Vector3[] GetBoxesGizmoLocalPositions(float gizmoScale)
    {
        return new Vector3[]
        {
                 lastSelectedGameObject.transform.forward*  tempLeftGizmoPos,
                -lastSelectedGameObject.transform.forward * tempRightGizmoPos,
        };
    }

CalculateGizmoScale函式程式碼如下:

float _gizmoBaseScale =0.77f;
protected float CalculateGizmoScale()
        {
            if (_camera.orthographic)
            {
               
                float scaleConstant = 0.02f;
                return _gizmoBaseScale * _camera.orthographicSize / (_camera.pixelRect.height * scaleConstant);
            }
            else
            {
                const float scaleConstant = 0.045f;
                Vector3 cameraPositionToGizmoOrigin = (this.transform.position - _cameraTransform.position);
                return _gizmoBaseScale * cameraPositionToGizmoOrigin.magnitude / (_camera.pixelRect.height * scaleConstant);
            }
        }
private Quaternion[] GetBoxesGizmoLocalRotations()
    {
        return new Quaternion[]
        {
                Quaternion.identity,
                Quaternion.identity
        };
    }

以上方法我們就可以獲取到座標軸的Transform資訊。接下來就將boxmesh的資訊給賦值上。

private void DrawControlLineLengthAxis(Matrix4x4[] worldTransforms)
    {
        Material material = MaterialPool.Instance.GizmoSolidComponent;
        material.SetInt("_ZWrite", 1);
        material.SetInt("_ZTest", 0);
        material.SetInt("_IsLit", 1);
        material.SetFloat("_LightIntensity", 1.5f);
        material.SetVector("_LightDir", _cameraTransform.forward);

        Mesh boxMesh = MeshPool.Instance.BoxMesh;
        for (int axisIndex = 3; axisIndex < 5; ++axisIndex)//這裡是因為列舉型別裡我多添加了兩種 X Y Z Left Right
        {
            Color axisColor = axisIndex == (int)selectGizmo ? SelectedAxisColor : _axesColors[2];//當座標軸被點選後 座標軸顏色設定高亮的顏色
            //Debug.Log(selectGizmo);
            material.SetColor("_Color", axisColor);
            material.SetInt("_StencilRefValue", _axesStencilRefValues[0]);
            material.SetPass(0);
            Graphics.DrawMeshNow(boxMesh, worldTransforms[axisIndex-3]);
        }
    }

以上就是對自己設計的座標軸chua建立,當然我們可以仿照unity的XYZ建立屬於我們的平移座標軸。原理類似。