Unity网格篇Mesh(一)

Unity网格篇Mesh(一)

  • 本文的目标
  • 1.渲染
    • 仔细看下面的图你会发现,锯齿状
  • 2.创建网格顶点
    • 4 x 2网格
    • 网格的顶点
  • 3.创建网格
    • 网格只在Play模式下显示
    • 逆时针和顺时针三角形
    • 第一个三角面
    • 一个四边形由两个三角面组成
    • 第一个四边形
    • 填充剩余网格
  • 接下一篇文章

本文的目标

  • 创建网格坐标
  • 使用携程计算他们位置
  • 利用三角形确定一个面
  • 自动生成法线
  • 添加纹理坐标和切线

这篇教程中我们将利用顶点和三角面创建一个网格。
在这里插入图片描述
原英文篇

1.渲染

  • 如果你想要在Unity显示一些东西,你需要一个网格。他可以是一个3D模型从另一个程序倒入的(3dmax,maya)。它也可以是程序生成的网格。它可以是精灵、UI元素或者是粒子系统,它们一样都是使用unity网格,甚至是屏幕特效也是使用网格渲染的。
  • 什么是网格?概念上来说网格由图形硬件(GPU Graphics Processing Unit图形处理单元)构成来绘制复杂的材料。它至少包含一组在3D空间中位置明确的点再加一组三角形构成。
  • 最基础的2D图形,由点连接组成,面上的三角形就是此类网格的代表。
  • 因为三角形是平坦的并且拥有直边,所以他们可以完美地被用来显示平坦的和连续的东西,像一个立方体的面孔。曲面的或者是圆的面只能被大量小的三角形来接近组成。如果三角面足够的小(不大于一个像素),那么你就不会感觉曲面和圆是由三角面组成的。从实时性能角度来讲通常这种情况是不可能的,所以我们总能够在面的某个程度上发现锯齿。

仔细看下面的图你会发现,锯齿状

在这里插入图片描述
网格图如下
在这里插入图片描述
Unity自带的胶囊体,立方体和球体 着色vs线框

如何显示线框
再视图左上角选择Display Mode,前三个选项分别是Shaded(着色)、Wireframe(线框)和Shaded
Wireframe着色并带着线框

  • 如果你想要展示一个3D游戏物体,它必须拥有两个组件。
  • MeshFilter这个组件记录了你想要展示的网格数据
  • MeshRenderer使用这个组件告诉网格如何渲染,比如使用哪个材质球,是否接受阴影和其他设置。
    在这里插入图片描述

为什么是一个材质球数组?
一个网格渲染器可以有多个材质球,它通常被用来渲染多组三角面,也成为子网格。它通常在外界导入的模型中使用,本片文章不使用多个材质球。

  • 你可以完全改变网格的显示效果通过调整材质球。Unity默认的材质球是简单的白色立体。你可以自己创建一个新的材质球通过Assets->Create->Material并拖拽到你的游戏物体上来代替它。新的材质球默认为Unity Standard Shader,你可以调节这个材质球的属性得到你想要的模型表现,如果你会写Shader你可以创建自己的Shader。
  • 一个快速添加细节的方法是给你的网格提供一个反射贴图,这个纹理描绘了材质球的基础颜色。当然我们需要知道如何投射纹理到网格的三角面上。这需要添加2D纹理坐标到定点上。这二维纹理空间被称为U和V,也是常说的UV坐标。UV坐标通常在(0,0)到(1,1)之间,它覆盖了整个纹理。超出范围的坐标将造成clamped或者Tiling平铺的效果,这取决于纹理设置。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

2.创建网格顶点

如何创建自己的网格?
通过创建一个简单的矩形网格来了解一下。这个网格将包括方形瓷砖(单位长度的四边形)。创建一个新的C#脚本并加入水平和垂直尺寸。

using UnityEngine;
using System.Collections; //协程使用//依赖组件(使用时当该脚本挂载到游戏对象身上会自动添加下面两个组件,作为该脚本的依赖项)
[RequireComponent(typeof(MeshFilter),typeof(MeshRenderer))] 
public class Grid : MonoBehaviour  {/// <summary>/// 矩阵x,y位置/// </summary>public int xSize, ySize;
}

这时我们创建一个空物体并挂在组件,它将自动同时添加MeshFilter和MeshRenderer组件。设置MeshRender的材质并确保MeshFilter的mesh属性为未定义。并设置网格尺寸为x = 10和 y = 5。

在这里插入图片描述

  • 现在我们先考虑定点位置稍后处理三角面序号。我们需要一个数组存储3D顶点位置。顶点的数量取决于网格的尺寸。我们需要获取每个四边形的顶点,但是相邻的四边形可以共享相同的顶点。所以每个维度上顶点数量比网格数多一个。
    在这里插入图片描述

4 x 2网格

    /// <summary>/// 网格顶点坐标/// </summary>private Vector3[] vertices;private void Generate(){vertices = new Vector3[(xSize + 1) * (ySize + 1)];}
  • 我们在场景中绘制这些顶点,这样我们就可以正确地核对他们的位置。利用OnDrawGizmos方法来挥之顶点的位置,利用OnDrawGizmos在每个顶点绘制一个小球。
    private void OnDrawGizmos(){Gizmos.color = Color.black;if (vertices == null) return;for (int i = 0; i < vertices.Length; i++){Gizmos.DrawSphere(vertices[i],0.1f);}}
  • 什么是Gizmos?
  • 在编辑器模式下Gizmos可以提供可视化的提示。他们只在Scene场景中显示不在PLay模式下显示,但是你可以通过工具栏调整它们。Gizmos公共类允许你绘制图表,线条,和其他的东西。
  • Gizmos在OnDrawGizmos方法中执行绘制,它被Unity编辑器自动调用。另一个可选的方法是OnDrawGizmosSelected,他只能被可选的对象调用。
  • 上面的没有画出来是因为Vector3数组为空,所以我们将其Generate方法修改一下
    private void Generate(){vertices = new Vector3[(xSize + 1) * (ySize + 1)];for (int i = 0, y = 0; y <= ySize; y++){for (int x = 0; x <= xSize; x++, i++){vertices[i] = new Vector3(x, y);}}}
  • 生成真实的网格在物理被唤醒的时候,Awake方法将在我们游戏运行模式时自动调用。
  private void Awake(){Generate();}

在这里插入图片描述

网格的顶点

  • 为什么在Gizmos绘制不能被移动?
  • Gizmos直接使用世界坐标绘制,不是使用对象的本地坐标系,如果你想遵循对象Transform组件,你必须声明使用transform.TransformPoint(vertices[i])代替vertices[i].
  • 现在我们可以看见这些顶点,但是他们生成的顺序我们不能明显地看出来。我们可以使用颜色来标识,但是我们也可以使用协程减慢这个过程的速度。
    观察顶点逐个生成

3.创建网格

  • 现在我们知道顶点的位置是正确的,接下来我们就处理真正的网格。初次之外我们想要我们的组件持有这些顶点,我们必须要把顶点指定到MeshFilter.mesh中。我们处理过顶点之后,就可以网格存储在MeshFilter中。修改如下
private Mesh mesh;private IEnumerator Generate(){WaitForSeconds wait = new WaitForSeconds(0.05f);GetComponent<MeshFilter>().mesh = mesh = new Mesh();mesh.name = "Procedual Grid";vertices = new Vector3[(xSize + 1) * (ySize + 1)];for (int i = 0, y = 0; y <= ySize; y++){for (int x = 0; x <= xSize; x++, i++){vertices[i] = new Vector3(x, y);yield return wait;}}mesh.vertices = vertices;}
  • 如何让组件持有网格?
  • 大家可能发现MeshFilter组件的Mesh属性只有在Play模式下才能看得到,在Editor模式下是不存在的,这里我们需要如何才能将网格持久化保存呢。持久化

网格只在Play模式下显示

  • 到目前,在Play模式下我们可以持有一个网格,但是它依旧是不可见的,因为我们并没有给它任何三角面。三角面由定点数组索引决定。每一个三角面有三个顶点,三个连续的顶点绘制了一个三角形,让我们来先绘制第一个三角面。修改如下
    private IEnumerator Generate(){WaitForSeconds wait = new WaitForSeconds(0.05f);GetComponent<MeshFilter>().mesh = mesh = new Mesh();mesh.name = "Procedual Grid";vertices = new Vector3[(xSize + 1) * (ySize + 1)];for (int i = 0, y = 0; y <= ySize; y++){for (int x = 0; x <= xSize; x++, i++){vertices[i] = new Vector3(x, y);yield return wait;}}mesh.vertices = vertices;DrawFaces(mesh);}/// <summary>/// 绘制三角面/// </summary>private void DrawFaces(Mesh mh){int[] triangles = new int[3];triangles[0] = 0;triangles[1] = 1;triangles[2] = 2;mesh.triangles = triangles;}
  • 这时候我们发现绘制了一个三角面,但是由于这三个顶点在一条直线上,所以生成了一个失败的三角形,它是不可见的一条线。如下图
    在这里插入图片描述
  • 第三个顶点我们将其转到下一行的第一个顶点。
        triangles[0] = 0;triangles[1] = 1;triangles[2] = xSize + 1;

在这里插入图片描述

  • 通过以上操作,我们绘制了一个三角形,但是它只能在一个方向可见。这种情况下,只有Z轴的反方向可见,所以你可能需要旋转视角才能看得到。
    在这里插入图片描述
  • 三角形的哪一面可见是由定点序号的方向来确定的。默认情况下,如果定点顺序是顺时针方向的话那么三角形就是正面可见。逆时针(就是逆屏幕方向)的三角形是被抛弃的,所以我们不必花费时间去渲染这部分定点,以为他们通常都是不可见的。
    在这里插入图片描述

逆时针和顺时针三角形

  • 所以为了实线从Z轴负方向到正方向可见,我们必须改变顺序相反的顶点的位置。我们交换后两个顶点的序号即可。
        triangles[0] = 0;triangles[1] = xSize + 1;triangles[2] = 1;

在这里插入图片描述

第一个三角面

  • 现在我们绘制了一个三角面只覆盖了一个四方瓦片的一半,为了覆盖整个瓦片,我们需要第二个三角形面。
int[] triangles = new int[6];
triangles[0] = 0;
triangles[1] = xSize + 1;
triangles[2] = 1;
triangles[3] = 1;
triangles[4] = xSize + 1;
triangles[5] = xSize + 2;

在这里插入图片描述

一个四边形由两个三角面组成

  • 既然这些顶点共用两个顶点,我们就可以减少我们代码的行数,明确地提到每个顶点索引只有一次。
triangles[0] = 0;
triangles[3] = triangles[2] = 1;
triangles[4] = triangles[1] = xSize + 1;
triangles[5] = xSize + 2;

在这里插入图片描述
在这里插入图片描述

第一个四边形

  • 我们可以通过循环来创建剩余第一行的瓦片。尽管我们遍历所有的顶点和三角面序号,但是我们必须要保证顶点和三角面序号是按照顺序的。我们把Yield代码的声明放到循环里,我们就不需要等待顶点的出现了。
private IEnumerator Generate(){WaitForSeconds wait = new WaitForSeconds(0.05f);GetComponent<MeshFilter>().mesh = mesh = new Mesh();mesh.name = "Procedual Grid";vertices = new Vector3[(xSize + 1) * (ySize + 1)];for (int i = 0, y = 0; y <= ySize; y++){for (int x = 0; x <= xSize; x++, i++){vertices[i] = new Vector3(x, y);yield return wait;}}mesh.vertices = vertices;int[] triangles = new int[6 * xSize];for (int ti = 0, vi = 0, x = 0; x < xSize; x++, ti += 6, vi++){triangles[ti] = vi;triangles[ti + 3] = triangles[ti + 2] = vi + 1;triangles[ti + 4] = triangles[ti + 1] = vi + xSize + 1;triangles[ti + 5] = vi + xSize + 2;yield return wait;}mesh.triangles = triangles;yield return wait;}
  • 现在,Gizmos可以立刻渲染出顶点,并且所有的三角面在一段时间后统一出现。要想看到瓦片一个接一个的出现,我们必须每次循环都刷新网格代替掉执行完所有循环刷新。
mesh.triangles = triangles;
yield return wait;

填充剩余网格

  • 正如你所能看到的,所有的网格都被三角面填充,每一行都是同时填充,因为我们使用了协程。如果你对想过感到满意,你可以移除掉所有的协程代码,这样网格创建就没有任何的延迟了。
private void Awake () 
{Generate();
}private void Generate () 
{GetComponent<MeshFilter>().mesh = mesh = new Mesh();mesh.name = "Procedural Grid";vertices = new Vector3[(xSize + 1) * (ySize + 1)];for (int i = 0, y = 0; y <= ySize; y++) {for (int x = 0; x <= xSize; x++, i++) {vertices[i] = new Vector3(x, y);}}mesh.vertices = vertices;int[] triangles = new int[xSize * ySize * 6];for (int ti = 0, vi = 0, y = 0; y < ySize; y++, vi++) {for (int x = 0; x < xSize; x++, ti += 6, vi++) {triangles[ti] = vi;triangles[ti + 3] = triangles[ti + 2] = vi + 1;triangles[ti + 4] = triangles[ti + 1] = vi + xSize + 1;triangles[ti + 5] = vi + xSize + 2;}}mesh.triangles = triangles;
}
  • 为什么不使用单个四边形?
  • 当我们创建一个平面矩形,我们可以仅仅使用两个三角面。这没有问题。但是更多的顶点结构也可以提供更多的控制和表现。这里也只是一个实验!
    在这里插入图片描述在这里插入图片描述

接下一篇文章

Unity网格篇Mesh(一)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/576996.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【论文阅读笔记】SegVol: Universal and Interactive Volumetric Medical Image Segmentation

Du Y, Bai F, Huang T, et al. SegVol: Universal and Interactive Volumetric Medical Image Segmentation[J]. arXiv preprint arXiv:2311.13385, 2023.[代码开源] 【论文概述】 本文思路借鉴于自然图像分割领域的SAM&#xff0c;介绍了一种名为SegVol的先进医学图像分割模型…

2024年关于存储发展趋势的七大预言

本文介绍了2024年的七大存储发展趋势&#xff0c;涵盖网络安全、混合云存储、绿色IT、客户体验和容器化应用程序等方面&#xff0c;并阐述了如何腾出财务和运营资源、投入到人工智能&#xff08;AI&#xff09;及其他IT项目上&#xff0c;以及如何堵住IT技能缺口。 趋势1&#…

LLM之RAG理论(二)| RAG综述论文详解

论文地址&#xff1a;https://arxiv.org/pdf/2312.10997.pdf 大型语言模型&#xff08;LLMs&#xff09;展示了强大的能力&#xff0c;但在实际应用中仍面临挑战&#xff0c;如幻觉现象、知识更新缓慢&#xff0c;以及在回答中缺乏透明度。检索增强生成&#xff08;RAG&#xf…

制作TikTok获客脚本必备源代码!

在这个数字时代&#xff0c;TikTok已成为全球最受欢迎的社交媒体平台之一&#xff0c;无数的品牌和企业都试图通过这个平台吸引潜在客户。 但是&#xff0c;要想在TikTok上获得更多关注和粉丝&#xff0c;除了制作有趣、有创意的内容外&#xff0c;还需要借助一些自动化工具来…

路由器介绍和命令操作

先来回顾一下上次的内容&#xff1a; ip地址就是由32位二进制数组 二进位数就是只有数字0和1组成 网络位&#xff1a;类似于区号&#xff0c;表示区域作用 主机位&#xff1a;类似于号码&#xff0c;表示区域中编号 网络名称&#xff1a;网络位不变&#xff0c;主机位全为0 …

【svn】win11最新svn每天自动化定时update、commit,隐藏窗口,定时脚本编写

本文使用schtasks结合bat脚本实现全自动svn update以及commit操作。执行时隐藏cmd窗口&#xff0c;全自动后台执行。 执行脚本 写脚本参考了网上很多文章&#xff0c;但是这些文章的方法都有问题或者已经失效&#xff0c;比如&#xff1a; 老版本的bat脚本&#xff0c;使用v…

【hacker送书第11期】Python数据分析从入门到精通

探索数据世界&#xff0c;揭示未来趋势 《Python数据分析从入门到精通》是你掌握Python数据分析的理想选择。本书深入讲解核心工具如pandas、matplotlib和numpy&#xff0c;助您轻松处理和理解复杂数据。 通过matplotlib、seaborn和创新的pyecharts&#xff0c;本书呈现生动直…

本地部署Jellyfin影音服务器并实现远程访问内网影音库

文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 1. 前言 随着移动智能设备的普及&#xff0c;各种各样的使用需求也被开发出来&…

路径规划最全综述+代码+可视化绘图(Dijkstra算法+A*算法+RRT算法等)

路径规划综述 1. 背景介绍 路径规划是指在给定的环境中找到从起点到终点的最佳路径的过程。它在现实生活中有着广泛的应用&#xff0c;包括无人驾驶、物流配送、机器人导航等领域。随着人工智能和计算机技术的发展&#xff0c;路径规划技术也在不断地得到改进和应用。 路径规划…

C# Onnx yolov8 pokemon detection

目录 效果 模型信息 项目 代码 下载 C# Onnx yolov8 pokemon detectio 效果 模型信息 Model Properties ------------------------- date&#xff1a;2023-12-25T17:55:44.583431 author&#xff1a;Ultralytics task&#xff1a;detect license&#xff1a;AGPL-3.0 h…

高德地图_公共交通路径规划API,获取两地点之间的驾车里程和时间

import pandas as pd import requests import jsondef get_dis_tm(origin, destination,city,cityd):url https://restapi.amap.com/v3/direction/transit/integrated?key xxx #这里就是需要去高德开放平台去申请key,请在xxxx位置填写,web服务APIlink {}origin{}&desti…

[C/C++]数据结构: 链式二叉树的构建及遍历

一: &#x1f4ac;二叉树的概念 1.1:&#x1f6a9; 概念 二叉树是指树中节点的度不大于2的有序树,它是一种最简单且重要的树,二叉树的递归定义为:二叉树是一颗空树,或者是一颗由一个根节点和两颗互不相交的,分别称为跟的左孩子和右孩子树组成的非空树,其中左子树和右子树都是二…

YOLOv5 目标计数 | 图片上绘制计数结果

修改方法: 只需要改 detect.py for path, im, im0s, vid_cap, s in dataset: 下新增一行 class_counts = {} class_counts[int(c)] = class_counts.get(int(c

《数字图像处理-OpenCV/Python》连载:图像的阈值处理

《数字图像处理-OpenCV/Python》连载&#xff1a;图像的阈值处理 本书京东 优惠购书链接 https://item.jd.com/14098452.html 本书CSDN 独家连载专栏 https://blog.csdn.net/youcans/category_12418787.html 第 9 章 图像的阈值处理 图像的阈值处理简单、直观&#xff0c;计算…

网络监测之如何保障企业业务系统安全?

网络信息安全在网络时代的重要性不言而喻。随着互联网的普及和数字化进程的加速&#xff0c;网络已经成为人们生活、工作和学习的重要平台。在这个平台上&#xff0c;信息交流、数据存储、在线支付等都需要依赖于网络信息安全。其中企事业单位业务系统安全值得关注。 企事业单…

水印消除:三种简单方法教你如何去除图片水印

当需要使用在线素材图片作为头像或壁纸时&#xff0c;水印可能成为不便之处。需要解决如何去除水印的问题吗&#xff1f;以下是几种方法供参考&#xff1a; 如何去除水印方法一&#xff1a;水印云去除水印&#xff08;适用于电脑端和手机端&#xff09; 水印云专注于无痕去水印…

给零基础朋友的编程课07 - 代码

给零基础朋友的编程课07-初识色彩、初识变量、案例3讲解_哔哩哔哩_bilibili Code: // // 案例3 // //// -设定画面- // size(1000, 1000); // 设置画面大小 background(7, 119, 132); // 设置背景颜色// - 绘画 - //// 1 绘制垂线 // 设定线条风格 …

第三十六周:文献阅读+注意力/自注意力机制

目录 摘要 Abstract 文献阅读&#xff1a;锂离子电池RUL预测的SA-LSTM 现有问题 提出方法 提出方法的结构 SA-LSTM预测模型的结构 研究实验 研究贡献 注意力机制 Self-Attention&#xff08;自注意力机制&#xff09; 注意力与自注意力 代码实现attention、self-at…

[Linux] MySQL数据库之索引

一、索引的相关知识 1.1 索引的简介 索引是一个排序列表&#xff0c;包含索引值和包含该值的数据行的物理地址&#xff08;类似于 c 语言链表&#xff0c;通过指针指向数据记录的内存地址&#xff09;。 使用索引后可以不用扫描全表来定位某行的数据&#xff0c;而是先通过索…

大模型做实体识别任务的原理

1、背景 命名实体识别&#xff08;named entity recognition&#xff0c;NER&#xff09;&#xff1a;通常是一个序列标注的任务&#xff0c;常见的模型框架有&#xff1a;LSTM-CRF、BERTBILSTMCRF等&#xff0c;该种任务通常被成为flat NER即&#xff1a;每一个token只分配一…