美文网首页Unity3D
【Unity3D】地面网格特效

【Unity3D】地面网格特效

作者: LittleFatSheep | 来源:发表于2023-10-13 19:07 被阅读0次

    1 前言

    本文实现了地面网格特效,包含以下两种模式:

    • 实时模式:网格线宽度和间距随相机的高度实时变化;
    • 分段模式:将相机高度分段,网格线宽度和间距在每段中对应一个值。

    本文完整资源见→Unity3D地面网格特效

    2 地面网格实现

    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();
            }
        }
    }
    

    说明:SceneController 脚本组件挂在相机对象上,这里旋转中心是固定的,如果想设置为随相机焦点自动变化,可以参考 缩放、平移、旋转场景

    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 // 分段模式(网格随相机高度分段变化)
    }
    

    说明:GridPlane 脚本组件挂在地面对象上。

    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
            }
        }
    }
    

    说明:在 Assets 窗口新建 Resources 目录,接着在 Resources 目录下面创建材质,重命名为 GridPlaneMat,将 GridPlane.shader 与 GridPlaneMat 材质绑定。

    3 运行效果

    1)实时模式

    2)分段模式

    声明:本文转自【Unity3D】地面网格特效

    相关文章

      网友评论

        本文标题:【Unity3D】地面网格特效

        本文链接:https://www.haomeiwen.com/subject/nxsabdtx.html