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画一个圆环的更多相关文章

  1. Unity中用Mesh画一个圆环(二)

    中目标-生成完整面 在之前的内容中我们已经成功生成了一个面,接下来我们要生成剩下的面就很容易了. 我们把之前生成的面当作顶面,接着我们来生成底面. 还记得前面说过\(\color{#1E90FF}{D ...

  2. Unity3D UGUI Shader画一个圆环

    Shader "Unlit/NewUnlitShader" { Properties { _MainTex ("Texture", 2D) = "wh ...

  3. 如何用Photoshop画一个发光金币(unity游戏素材教程)

    做好的发光金币预览图: 以下为如何用Photoshop画一个发光金币教程: [1]如上图1-2,新建,名称改为Coin,宽度20像素,高度20像素,分辨率72,背景白色: [2]使用Alt+Shift ...

  4. iOS圆形图片裁剪,以及原型图片外面加一个圆环

    废话不多说,直接上代码 #import "ViewController.h" @interface ViewController () @property (nonatomic,s ...

  5. Unity中Mesh分解与边缘高亮加上深度检测

    一个比较简单的需求,不过遇到些坑,记录下. 房间有多个模型,每个模型可能多个SubMesh,点击后,需要能具体到是那个SubMesh,并且在这个SubMesh上显示边缘高光,以及能个性这单个SubMe ...

  6. 关于Unity中Mesh网格的详解

    3D模型 通过3D建模软件所建出来的点和面,如以三角形为主的点和面,比如人的脑袋一个球,就是由各种各样的三角形组成的点和面. 点和面以及纹理坐标都是通过3D建模软件建模出来的. Unity会帮我们把模 ...

  7. unity, editable mesh

    一,需求 从fbx载入的模型是不可以在unity里编辑的. 我有一人特殊的需求就是想在unity里为mesh的各顶点K动画. 于是需要自己实现一个可编辑(其实只是顶点可以拖动)的mesh. 二,思路 ...

  8. iOS圆形图片裁剪,原型图片外面加一个圆环

    /** *  在圆形外面加一个圆环 */ - (void)yuanHuan{ //0.加载图片 UIImage *image = [UIImage imageNamed:@"AppIcon1 ...

  9. Directx11教程(5) 画一个简单的三角形(1)

    原文:Directx11教程(5) 画一个简单的三角形(1)       在本篇教程中,我们将通过D3D11画一个简单的三角形.在D3D11中,GPU的渲染主要通过shader来操作(当然还有一些操作 ...

随机推荐

  1. 编译 lame for iOS

    网上找了许多编译lame的教程,结果都是编译失败,多次尝试后发现是编译脚本放错路径了,记录下编译的过程,把编译脚本放到源码文件夹中和修改编译脚本中的目录是关键: 一.首先去Lame官网 http:// ...

  2. CountDownLatch源码及Javadoc阅览

    /** * A synchronization aid that allows one or more threads to wait until * a set of operations bein ...

  3. 【干货系列之萌新知识点】python与变量和运算符

    一.注释 注释一行:# 为注释符 注释多行:'或者"为注释符 二.print输出 print()函数,作用是打印一些信息语屏幕上. 例如:print("hello world!&q ...

  4. 微信小程序学习总结

    微信小程序开发环境安装以及相关设置配置 微信小程序前端页面书写 微信小程序前端样式WXSS书写 微信小程序中事件 微信小程序自定义组件 微信小程序发起请求 微信小程序登入流程 微信小程序路由跳转 微信 ...

  5. JAVA中JDK开发环搭的搭建,jvm jre

    1.JDK的下载与安装: www.oracle.com 安装需要注意的是:不能把jdk安装到有空格或中文的文件夹中,建议大家在某个目录下创建一个JavaWeb的文件夹,然后把所学的java所有内容(后 ...

  6. asp.net core mvc中自定义ActionResult

    在GitHub上有个项目,本来是作为自己研究学习.net core的Demo,没想到很多同学在看,还给了很多星,所以觉得应该升成3.0,整理一下,写成博分享给学习.net core的同学们. 项目名称 ...

  7. SSH通道来访问MySQL

     许多时候当要使用Mysql时,会遇到如下情况: 1. 信息比较重要,希望通信被加密.2. 一些端口,比如3306端口,被路由器禁用. 对第一个问题的一个比较直接的解决办法就是更改mysql的代码,或 ...

  8. python学习-多线程并发

    1.线程与进程 通俗解释: 对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进 ...

  9. MyBatis 概念

    简介 什么是 MyBatis? MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyB ...

  10. The usage of Markdown---表格

    更新时间:2019.09.14   谈到怎么在Markdown中插入表格,其实只要熟知以下几点就可以了: 使用管道符|进行内容的分割 使用冒号:和连号符-表示表格内容的对齐情况,连号符-在中间,冒号: ...