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来操作(当然还有一些操作 ...
随机推荐
- Warfare And Logistics UVA - 1416
题目链接:https://vjudge.net/problem/UVA-1416 题解: 这是一个最短路的好题,首先我们考虑如果暴力弗洛伊德,显然时间复杂度不对,如果做n次spfa好像复杂度也不对,所 ...
- RF作用与目的
robotframework自动化原理:通过ride工具编写脚本,加载指定的UI测试库,再通过pybot程序去运行指定脚本,调用浏览器驱动,打开浏览器,操作浏览器页面元素,达到模拟用户操作的行为 为什 ...
- PowerSploit-CodeExecution(代码执行)脚本渗透实战
首先介绍一下国外大牛制作的Powershell渗透工具PowerSploit,上面有很多powershell攻击脚本,它们主要被用来渗透中的信息侦察.权限提升.权限维持. 项目地址:https://g ...
- shark恒破解笔记5-VB之rtcMsgBox
本次是来破解一个名为”系统提速精灵“的软件,方法是对rtcMsgBox下断点. 运行程序输入假码,会提示“注册码错“ 在oep上面可以看到大量vb引擎函数 明显的VB程序 载入OD对rtcMsgBo ...
- opencv::调整图像亮度与对比度
图像变换可以看作如下: - 像素变换 – 点操作 - 邻域操作 – 区域 调整图像亮度和对比度属于像素变换-点操作 //创建一张跟原图像大小和类型一致的空白图像.像素值初始化为0 Mat new_im ...
- 5分钟读懂Linux权限管理
权限管理: 本文用于初学者对Linux文件系统权限的快速了解!! 进程安全上下文: 进程对文件的访问权限应用模型: 进程的属主与文件的属主是否相同:如果相同,则应用属主权限: 否 ...
- linux使用jq工具解析json
jq类似一个awk或grep一样的神器,可以方便地在命令行操作json 这里我使用海南万宁的天气接口做演示,地址:http://t.weather.sojson.com/api/weather/cit ...
- Lambda函数及其用法
Lambda函数又称匿名函数,匿名函数就是没有名字的函数,函数没有名字也行? 当然可以啦.有些函数如果只是临时一用,而且它的业务逻辑也很简单时,就没必要非给它取个名字不可. 先来看个简单lambda函 ...
- Java根据参数返回相应类
问题初衷:如何根据参数变换方法的返回类型(参数为 类) 解决方案: 下面方法是放在工具类(例:YslRequestUtil) public <T> T response(Object re ...
- Jetcache
转存 Jetcache https://github.com/alibaba/jetcache/wiki/GettingStarted_CN