Unity中用Mesh画一个圆环
Probuider
前几天在做一个小项目的时候,用到了Unity自带的一个包ProBuilder其中的Arch生成1/4圆。
挺好玩的,可以在直接Unity中根据需要用Mesh定制生成图形,而不用建模软件。
但是存在一个小问题,就是在使用的时候他的中心点是在生成图形的左下角。
旋转的时候不符合我的需求,我想要的是生成的时候旋转中心在圆心的位置,所以准备自己定制一个。
目标
关于Mesh生成图形的原理可以参考这篇文章,讲得虽然不算很详细,但足够了解基本概念了。
目标是生成下面图中的一个1/4空心圆柱体
我们切换到Wireframe模式下,可以看出它是有一个一个的顶点,并通过一条条的直线连接起来。那么我们如何确定这些顶点和线的位置呢?
小目标-生成一个面
其实很简单的,我们一步一步慢慢来。一次生成一整个会有点麻烦,我们可以一面一面来。只要生成了第一个面,其他的面也是类似的方法生成就好。
在前面我们提到了我们要的是生成一个圆柱体,圆柱体一个的重要性质就是可以由一个圆形叠加产生,也就是只要我们生成一个圆形,就完成了大部分的工作。
我们知道3D建模就是由一个一个的三角形组合成的,所以我们要用三角形来模拟来一个空心的圆。
在Probuilder中生成这样一个空心圆柱体用的是Arch,它有几个参数,分别是
\(\color{#1E90FF}{Radius}\) 半径,圆心到最外圈的距离
\(\color{#1E90FF}{Thickness}\) 厚度,圆心到最外圈的距离-圆心到最内圈的距离
\(\color{#1E90FF}{Depth}\) 深度
\(\color{#1E90FF}{NumberOfSides}\) 由多少个面组成,面越多越平滑,性能也越差
\(\color{#1E90FF}{DrawArchDegrees}\) 总共绘制的角度
\(\color{#1E90FF}{NumberOfSides}\)中的面是指由两个三角形一头一尾拼成的梯形,多个头大脚小的梯形拼在一起便成了我们需要的圆形。
原理已经知道了,那下一步只要确定三角形顶点的位置就OK了。至于如何确定三角形顶点的位置,我们可以再看下这张图。

是不是瞬间清晰明了,红线的交汇处就是圆心的位置,数字则是每个顶点的编号。
我们假设圆心在原点,数字0-1所在的线为180度线。\(\color{#1E90FF}{Increment}\) = \(\color{#1E90FF}{DrawArchDegrees}\)/\(\color{#1E90FF}{NumberOfSides}\)就是线与线之间的角度。每条线的角度可以由\(\color{#1E90FF}{180-Increment*i}\)得到。i为第几条线。
线上的点可以由\(\color{#1E90FF}{y = r* sinθ, y = r* cosθ}\)得到。
//顶点坐标
vertexList.Clear();
float incrementAngle = DrawArchDegrees / NumberOfSides;
//小于等于是因为n+1条线才能组成n个面
for (int i = 0; i <= NumberOfSides; i++)
{
float angle = 180 - i * incrementAngle;
float innerX = (Radius - Thickness) * Mathf.Cos(angle * Mathf.Deg2Rad);
float innerY = (Radius - Thickness) * Mathf.Sin(angle * Mathf.Deg2Rad);
vertexList.Add(new Vector3(innerX, innerY, 0));
float outsideX = Radius * Mathf.Cos(angle * Mathf.Deg2Rad);
float outsideY = Radius * Mathf.Sin(angle * Mathf.Deg2Rad);
vertexList.Add(new Vector3(outsideX, outsideY, 0));
}
在上面的代码中我们已经计算出了顶点的位置,下一步我们要做的是按顺序插入三角形顶点的位置。从Mesh这篇文章中我们可以知道,只有是三角形是正面的情况下才会被渲染。
而正反面可以通过法线的朝向进行判断,向外的面就是正面,相反的就是背面。
在Unity中,法线的朝向可以由左手法则得到。拿出左手,伸直,拇指与其他四个指头垂直,然后四指弯曲,指尖朝向循环的方向,拇指就指向法线的方向。
也就是说在上图中,我们想渲染三角形,顺序应该是类似这样的012,321, 234, 543。
//三角形索引
triangleList.Clear();
int direction = 1;
for (int i = 0; i < NumberOfSides * 2; i++)
{
int[] triangleIndexs = getTriangleIndexs(i, direction);
direction *= -1;
for (int j = 0; j < triangleIndexs.Length; j++)
{
triangleList.Add(triangleIndexs[j]);
}
}
\(\color{#F08080}{getTriangleIndexs}\)代码如下
int[] getTriangleIndexs(int index, int direction)
{
int[] triangleIndexs = new int[3] { 0,1,2};
for (int i = 0; i < triangleIndexs.Length; i++)
{
triangleIndexs[i] += index;
}
if (direction == -1)
{
int temp = triangleIndexs[0];
triangleIndexs[0] = triangleIndexs[2];
triangleIndexs[2] = temp;
}
return triangleIndexs;
}
至于uv坐标就更简单了,把内圈顶点uv坐标中的Y固定为0,外圈顶点uv坐标中的Y固定为1,而x坐标由\(\color{#1E90FF}{1/NumberOfSides}\)得到:
//UV索引
uvList.Clear();
for (int i = 0; i <= NumberOfSides; i++)
{
float angle = 180 - i * incrementAngle;
float littleX = (1.0f / NumberOfSides) * i;
uvList.Add(new Vector2(littleX, 0));
float bigX = (1.0f / NumberOfSides) * i;
uvList.Add(new Vector2(bigX, 1));
}
完整代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//[RequireComponent(typeof(MeshFilter))]
//[RequireComponent(typeof(MeshRenderer))]
//[ExecuteInEditMode]
public class DrawArch : MonoBehaviour
{
public float Radius = 20.0f; //外圈的半径
public float Thickness = 10.0f; //厚度,外圈半径减去内圈半径
public float Depth = 1.0f; //厚度
public float NumberOfSides = 30.0f; //由多少个面组成
public float DrawArchDegrees = 90.0f; //要绘画多长
public Material archMaterial = null;
private List<Vector3> vertexList = new List<Vector3>();
private List<int> triangleList = new List<int>();
private List<Vector2> uvList = new List<Vector2>();
// Start is called before the first frame update
void Start()
{
GenerateVertex();
}
void GenerateVertex()
{
//顶点坐标
vertexList.Clear();
float incrementAngle = DrawArchDegrees / NumberOfSides;
//小于等于是因为n+1条线才能组成n个面
for (int i = 0; i <= NumberOfSides; i++)
{
float angle = 180 - i * incrementAngle;
float innerX = (Radius - Thickness) * Mathf.Cos(angle * Mathf.Deg2Rad);
float innerY = (Radius - Thickness) * Mathf.Sin(angle * Mathf.Deg2Rad);
vertexList.Add(new Vector3(innerX, innerY, 0));
float outsideX = Radius * Mathf.Cos(angle * Mathf.Deg2Rad);
float outsideY = Radius * Mathf.Sin(angle * Mathf.Deg2Rad);
vertexList.Add(new Vector3(outsideX, outsideY, 0));
}
//三角形索引
triangleList.Clear();
int direction = 1;
for (int i = 0; i < NumberOfSides * 2; i++)
{
int[] triangleIndexs = getTriangleIndexs(i, direction);
direction *= -1;
for (int j = 0; j < triangleIndexs.Length; j++)
{
triangleList.Add(triangleIndexs[j]);
}
}
//UV索引
uvList.Clear();
for (int i = 0; i <= NumberOfSides; i++)
{
float angle = 180 - i * incrementAngle;
float littleX = (1.0f / NumberOfSides) * i;
uvList.Add(new Vector2(littleX, 0));
float bigX = (1.0f / NumberOfSides) * i;
uvList.Add(new Vector2(bigX, 1));
}
Mesh mesh = new Mesh()
{
vertices = vertexList.ToArray(),
uv = uvList.ToArray(),
triangles = triangleList.ToArray(),
};
mesh.RecalculateNormals();
gameObject.AddComponent<MeshFilter>().mesh = mesh;
gameObject.AddComponent<MeshRenderer>().material = archMaterial;
}
int[] getTriangleIndexs(int index, int direction)
{
int[] triangleIndexs = new int[3] { 0,1,2};
for (int i = 0; i < triangleIndexs.Length; i++)
{
triangleIndexs[i] += index;
}
if (direction == -1)
{
int temp = triangleIndexs[0];
triangleIndexs[0] = triangleIndexs[2];
triangleIndexs[2] = temp;
}
return triangleIndexs;
}
}
未完待续。。。
Unity中用Mesh画一个圆环的更多相关文章
- Unity中用Mesh画一个圆环(二)
中目标-生成完整面 在之前的内容中我们已经成功生成了一个面,接下来我们要生成剩下的面就很容易了. 我们把之前生成的面当作顶面,接着我们来生成底面. 还记得前面说过\(\color{#1E90FF}{D ...
- Unity3D UGUI Shader画一个圆环
Shader "Unlit/NewUnlitShader" { Properties { _MainTex ("Texture", 2D) = "wh ...
- 如何用Photoshop画一个发光金币(unity游戏素材教程)
做好的发光金币预览图: 以下为如何用Photoshop画一个发光金币教程: [1]如上图1-2,新建,名称改为Coin,宽度20像素,高度20像素,分辨率72,背景白色: [2]使用Alt+Shift ...
- iOS圆形图片裁剪,以及原型图片外面加一个圆环
废话不多说,直接上代码 #import "ViewController.h" @interface ViewController () @property (nonatomic,s ...
- Unity中Mesh分解与边缘高亮加上深度检测
一个比较简单的需求,不过遇到些坑,记录下. 房间有多个模型,每个模型可能多个SubMesh,点击后,需要能具体到是那个SubMesh,并且在这个SubMesh上显示边缘高光,以及能个性这单个SubMe ...
- 关于Unity中Mesh网格的详解
3D模型 通过3D建模软件所建出来的点和面,如以三角形为主的点和面,比如人的脑袋一个球,就是由各种各样的三角形组成的点和面. 点和面以及纹理坐标都是通过3D建模软件建模出来的. Unity会帮我们把模 ...
- unity, editable mesh
一,需求 从fbx载入的模型是不可以在unity里编辑的. 我有一人特殊的需求就是想在unity里为mesh的各顶点K动画. 于是需要自己实现一个可编辑(其实只是顶点可以拖动)的mesh. 二,思路 ...
- iOS圆形图片裁剪,原型图片外面加一个圆环
/** * 在圆形外面加一个圆环 */ - (void)yuanHuan{ //0.加载图片 UIImage *image = [UIImage imageNamed:@"AppIcon1 ...
- Directx11教程(5) 画一个简单的三角形(1)
原文:Directx11教程(5) 画一个简单的三角形(1) 在本篇教程中,我们将通过D3D11画一个简单的三角形.在D3D11中,GPU的渲染主要通过shader来操作(当然还有一些操作 ...
随机推荐
- Huffman树及其编解码
Huffman树--编解码 介绍: Huffman树可以根据输入的字符串中某个字符出现的次数来给某个字符设定一个权值,然后可以根据权值的大小给一个给定的字符串编码,或者对一串编码进行解码,可以用于 ...
- 为什么要实现 IDisposable 接口?
一.背景 最近在精读 <CLR Via C#>和 <Effective C#> 的时候,发现的一个问题点.一般来说,我们实现 IDisposable 接口,是为了释放托管资源和 ...
- 宝塔面板6.x版本前台存储XSS+后台CSRF组合拳Getshell
对于宝塔漏洞复现和练习 0×00 什么是宝塔面板 宝塔面板是一款使用方便,功能强大且终身免费的服务器管理软件,支持Linux 与Windows 系统.一键配置:LAMP / LNMP ,网站,数据库 ...
- Unity 场景中看不到物体或者OnDrawGizmos画的线看不到
有时候,Unity中的场景里面,物体突然看不见了,可以这样做: 首先,在 Hierarchy 面板选择看不见的物体,按下快捷键 f.如果物体还是看不见,见下图: 看看图中圈红的地方.如果,如果 ...
- 深入了解一下HTTP缓存机制
HTTP 缓存机制作为 web 性能优化的重要手段,是Web 开发知识体系库中的一个基础环节,但是对于很多学习者来说,仅仅只是知道浏览器会对请求的静态文件进行缓存,但是为什么被缓存,缓存是怎样生效的, ...
- [NOIp2011] luogu P1311 选择客栈
我妈的抽象歌曲真 nb. 题目描述 给你 nnn 个点,每个点有两个参数 ci,dic_i,d_ici,di,给你一个数 DDD.定义一种方案合法,当且仅当你选出整数 i,j∈[1,n],i< ...
- win10-搭建git工具
.下载安装 git .生成 SSH 密钥 ssh-keygen -t rsa -C "email@com" -b 4096 .配置gitlab 增加 SSH 密钥. .配置 git ...
- 一文详解CentOS6.5搭建DNS服务
本文详细介绍DNS服务在Linux Operation System 中的搭建过程 一.DNS服务器的工作原理 客户机提出域名解析请求,并将该请求发送给本地的域名服务器.当本地的域名服务器收到请求后, ...
- PHP代码审计基础-中级篇
初级篇更多是对那些已有的版本漏洞分析,存在安全问题的函数进行讲解,中级篇更多是针对用户输入对漏洞进行利用 中级篇更多是考虑由用户输入导致的安全问题. 预备工具首先要有php本地环境可以调试代码 总结就 ...
- UNCTF杂项题Hidden secret 之NTFS交换数据流隐写
---恢复内容开始--- 做这道题目的经历比较坎坷,题目中用于隐藏flag的jpg文件出了问题,导致不能被交换数据流隐写所以出题人换了一次题目,最后做法也换了,不过出题人一开始的考察点还是基于NTFS ...