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】地面网格特效的更多相关文章

  1. Unity3D 导航网格自动寻路(Navigation Mesh)

    NavMesh(导航网格)是3D游戏世界中用于实现动态物体自动寻路的一种技术,将游戏中复杂的结构组织关系简化为带有一定信息的网格,在这些网格的基础上通过一系列的计算来实现自动寻路..导航时,只需要给导 ...

  2. unity3d 摄像机抖动特效

    摄像机抖动特效 在须要的地方调用CameraShake.Shake()方法就能够  

  3. Unity3D图像后处理特效——Depth of Field 3.4

    Depth of Field 3.4 is a common postprocessing effect that simulates the properties of a camera lens. ...

  4. Unity3D特效-场景淡入淡出

    最近公司开始搞Unity3D..整个游戏..特效需求还是比较多的.关于UI部分的特效淡入淡出.看网上用的方法都是用个黑东东遮挡然后设置alpha这么搞....本大神感觉非常的low.而且很渣.故奋笔疾 ...

  5. [Unity3D]Unity资料大全免费分享

     都是网上找的连七八糟的资料了,整理好分享的,有学习资料,视频,源码,插件……等等 东西比较多,不是所有的都是你需要的,可以按  ctrl+F 来搜索你要的东西,如果有广告,不用理会,关掉就可以了,如 ...

  6. cocos2d-x 网格动画深入分析

    转自:http://www.2cto.com/kf/201212/179828.html 在TestCpp中的EffectsTest示例中展示了一些屏幕特效,它是将屏幕划分为多个格子,并对这些格子进行 ...

  7. 24、Cocos2dx 3.0游戏开发找小三之网格动作:高炫酷的3D动作

    重开发人员的劳动成果,转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/37596763 网格动作类似于动作特效,能够实现翻转. ...

  8. WebGL场景的两种地面构造方法

    总述:大部分3D编程都涉及到地面元素,在场景中我们使用地面作为其他物体的承载基础,同时也用地面限制场景使用者的移动范围,还可以在通过设置地块的属性为场景的不同位置设置对应的计算规则.本文在WebGL平 ...

  9. Unity塔防游戏开发

    Unity3D塔防开发流程 配置环境及场景搭建编程语言:C#,略懂些许设计模式,如果不了解设计模式,BUG More开发工具:Unity3D编辑器.Visual Studio编译器开发建议:了解Uni ...

  10. 手搓一个“七夕限定”,用3D Engine 5分钟实现烟花绽放效果

    七夕来咯!又到了给重要的人送惊喜的时刻. 今年,除了将心意融入花和礼物,作为程序员,用自己的代码本事手搓一个技术感十足"七夕限定"惊喜,我觉得,这是不亚于车马慢时代手写信的古典主义 ...

随机推荐

  1. 【js】 Object.prototype.toString.call()

    1,Object.prototype.toString这个方法的作用是什么  判断数据类型 2,为什么要用这个方法 是因为  js 中 一般的类型判断 对于 null,数组,对象 , 都会返回一样的结 ...

  2. 【Mysql系列】(二)日志系统:一条更新语句是如何执行的

    有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准https://blog.zysicyj.top 这篇文章是从Github ReadMe拷贝的,内容实践下载是没问题的,能够正 ...

  3. [转帖]Oracle与防火墙

    https://www.laoxiong.net/oracle_and_firewall.html 老熊 Oracle数据库管理 2009-04-20 最近有两次Oracle数据库故障与防火墙有关.这 ...

  4. [转帖]七. PostgreSQL逻辑结构(1)—数据库和模式

    https://www.jianshu.com/p/ee8b1bdfdb19 在PostgreSQL里,逻辑结构从高到底依次是:数据库.模式(又叫架构).表.行.当我们打开PostgreSQL官方自带 ...

  5. Chrome 下载地址

    今天同事找到一个网页 感觉非常好用 这里保存并且推荐一下 https://www.chromedownloads.net/chrome64win-stable/

  6. MySQL控制权限

    编写顺序和执行顺序是不一样的 编写顺序: SELECT 字段列表 FROM 表名列表 WHERE 条件列表 GROUP BY 分组字段列表 HAVING 分组后条件列表 ORDER BY 排序字段列表 ...

  7. React中函数组件与类组件的两种使用

    React 创建组件的两种方式 函数组件:使用js函数创建的组件 约定1:函数名称必须以大写字母开头 约定2:函数组件必须要有返回值. 如果返回值为null.表示不渲染任何内容. return nul ...

  8. vue如何获取动态添加的类

    动态添加的类.你在声明周期中的mounted中是拿不到的. 是有在updata这个声明周期中才可以拿到的. 因为此时数据才跟新完成

  9. 数据仓库(4)基于维度建模的数仓KimBall架构

      基于维度建模的KimBall架构,将数据仓库划分为4个不同的部分.分别是操作型源系统.ETL系统.数据展现和商业智能应用,如下图.   操作型源系统,指的就是面向用户的各类系统,如app.网站.E ...

  10. 自动部署(apb docker cicd gitlab)

    1.安装gitlab-runner docker pull gitlab/gitlab-runner:latest 2.运行镜像 docker run -d --name gitlab-runner ...