[Unity3D] ground grid effects

1 Introduction

        This article implements ground grid effects, including the following two modes:

  • Real-time mode: Grid line width and spacing change in real time with the height of the camera;
  • Segmented Mode: Divide the camera height into segments, and the grid line width and spacing correspond to a value in each segment.

        For the complete resources of this article, see → Unity3D Ground Mesh Effects

2 Realization of ground grid

        SceneController.cs

using System;
using UnityEngine;

public class SceneController : MonoBehaviour {
    private static SceneController instance; // 单例
    private Action cameraChangedHandler; // 相机状态改变处理器
    private Transform cam; // 相机

    public static SceneController Instance() { // 获取实例
        return instance;
    }

    public void AddHandler(Action handler) { // 添加处理器
        cameraChangedHandler += handler;
    }

    private void Awake() {
        instance = this;
        cam = Camera.main.transform;
    }

    private void Update() { // 更新场景(Scroll: 缩放场景, Ctrl+Drag: 平移场景, Alt+Drag: 旋转场景)
        float scroll = Input.GetAxis("Mouse ScrollWheel");
        ScaleScene(scroll);
        if ((Input.GetMouseButton(0))) {
            if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) {
                float hor = Input.GetAxis("Mouse X");
                float ver = Input.GetAxis("Mouse Y");
                MoveScene(hor, ver);
            }
            if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)) {
                float hor = Input.GetAxis("Mouse X");
                float ver = Input.GetAxis("Mouse Y");
                RotateScene(hor, ver);
            }
        }
    }

    private void ScaleScene(float scroll) { // 缩放场景
        if (Mathf.Abs(scroll) > Mathf.Epsilon) {
            cam.position += cam.forward * scroll * 50;
            cameraChangedHandler?.Invoke();
        }
    }

    private void MoveScene(float hor, float ver) { // 平移场景
        if (Mathf.Abs(hor) > Mathf.Epsilon || Mathf.Abs(ver) > Mathf.Epsilon) {
            cam.position -= (cam.right * hor * 3 + cam.up * ver * 3);
            cameraChangedHandler?.Invoke();
        }
    }

    private void RotateScene(float hor, float ver) { // 旋转场景
        if (Mathf.Abs(hor) > Mathf.Epsilon || Mathf.Abs(ver) > Mathf.Epsilon) {
            cam.RotateAround(Vector3.zero, Vector3.up, hor * 3);
            cam.RotateAround(Vector3.zero, -cam.right, ver * 3);
            cameraChangedHandler?.Invoke();
        }
    }
}

        Note: The SceneController script component is hung on the camera object, and the rotation center is fixed here. If you want to set it to change automatically with the camera focus, you can refer to  zooming, panning, and rotating the scene .

        GridPlane.cs

using UnityEngine;

public class GridPlane : MonoBehaviour {
    public GridType gridType = GridType.REALTIME; // 网格类型
    private const float lineGapFactor = 0.2f; // 线段间距因子(相机距离单位长度变化时线段间距的变化量)
    private const float lineWidthFactor = 0.01f; // 线段宽度因子(相机距离单位长度变化时线段宽度的变化量)
    private const int segmentFactor = 100; // 分段因子(每隔多远分一段)
    private Transform cam; // 相机
    private float camDist; // 相机距离
    private float lineGap = 1; // 线段间距
    private float lineWidth = 0.05f; // 线段宽度
    private Vector4 planeCenter = Vector4.zero; // 地面中心
    private Material material; // 网格材质

    private void Start() {
        SceneController.Instance().AddHandler(UpdateGrid);
        cam = Camera.main.transform;
        material = Resources.Load<Material>("GridPlaneMat");
        material.SetVector("_PlaneCenter", planeCenter);
        UpdateGrid();
    }

    private void UpdateGrid() { // 更新网格
        camDist = Mathf.Abs(cam.position.y - planeCenter.y);
        if (gridType == GridType.REALTIME) {
            RealtimeUpdateGrid();
        } else if (gridType == GridType.SEGMENTED) {
            SegmentedUpdateGrid();
        }
    }

    private void RealtimeUpdateGrid() { // 实时更新网格
        lineGap = camDist * lineGapFactor;
        lineWidth = camDist * lineWidthFactor;
        UpdateMatProperties();
    }

    private void SegmentedUpdateGrid() { // 分段更新网格
        int dist = (((int) camDist) / segmentFactor + 1) * segmentFactor;
        lineGap = dist * lineGapFactor;
        lineWidth = dist * lineWidthFactor;
        UpdateMatProperties();
    }

    private void UpdateMatProperties() { // 更新材质属性
        lineGap = Mathf.Max(lineGap, lineGapFactor);
        lineWidth = Mathf.Max(lineWidth, lineWidthFactor);
        material.SetFloat("_LineGap", lineGap);
        material.SetFloat("_LineWidth", lineWidth);
    }
}

public enum GridType { // 网格类型
    REALTIME, // 实时模式(网格随相机高度实时变化)
    SEGMENTED // 分段模式(网格随相机高度分段变化)
}

        Description: The GridPlane script component hangs on the ground object.

        GridPlane.shader

Shader "MyShader/GridPlane"  { // 路径上的节点移动特效
    Properties {
        _PlaneColor("Plane Color", Color) = (1, 1, 1, 1) // 地面颜色
        _LineColor("Line Color", Color) = (1, 1, 1, 1) // 线条颜色
        _LineGap("Line Gap", Int) = 1 // 线段间距
        _LineWidth("Line Width", Range(0, 1)) = 0.1 // 线段宽度
        _PlaneCenter("Plane Center", Vector) = (0, 0, 0, 0) // 地面中心
    }

    SubShader {
        Pass {
            cull off
            CGPROGRAM
 
            #include "UnityCG.cginc"
            #pragma vertex vert
            #pragma fragment frag

            float4 _PlaneColor; // 地面颜色
            float4 _LineColor; // 线条颜色
            int _LineGap; // 线段间距
            float _LineWidth; // 线段宽度
            float4 _PlaneCenter; // 地面中心

            struct v2f {
                float4 pos : SV_POSITION; // 裁剪空间顶点坐标
                float2 worldPos : TEXCOORD0; // 世界空间顶点坐标(只包含xz)
            };
 
            v2f vert(float4 vertex: POSITION) {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, vertex)
                o.worldPos = mul(unity_ObjectToWorld, vertex).xz; // 将模型空间顶点坐标变换到世界空间
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                float2 vec = abs(i.worldPos - _PlaneCenter.xz);
                float2 mod = fmod(vec, _LineGap);
                float2 xz = min(mod, _LineGap - mod);
                float dist = min(xz.x, xz.y);
                float factor = 1 - smoothstep(0, _LineWidth, dist);
                fixed4 color = lerp(_PlaneColor, _LineColor, factor);
                return fixed4(color.xyz, 1);
            }

            ENDCG
        }
    }
}

        Instructions: Create a new Resources directory in the Assets window, then create a material under the Resources directory, rename it to GridPlaneMat, and bind GridPlane.shader to the GridPlaneMat material.

3 running effect

        1) Real-time mode

        2) Segmentation mode

Guess you like

Origin blog.csdn.net/m0_37602827/article/details/131870647