Unity3D C#数学系列之创建圆柱体


我们知道3D Max中可以很方便的创建出一个圆柱体,那么在Unity中能否用代码创建出一个圆柱体呢,当然可以的,在Unity里面想怎么玩都行的。我们今天来看看在Unity中用代码创建一个圆柱体。
废话少说,先看效果。
代码圆柱体效果

1 逻辑梳理

用代码创建圆柱体逻辑其实很简单,关键点就两个:
1.生成圆柱体的网格
2.将网格用合适的Shader渲染出来

1.1 生成圆柱体网格

unity中要渲染一个物体,需要先添加两个组件,一是MeshFilter,二是MeshRenderer。

MeshFilter 网格过滤器
其唯一的作用就是用来指定Mesh。
它有两个属性mesh,sharedMesh;可以把mesh理解为值传递,修改网格只会影响到这一个物体;而shardeMesh是引用传递,修改网格会应用到所有使用这个网格的物体,并且会持久化。

MeshRenderer 网格渲染器

MeshRender 负责渲染 MeshFilter 指定的 Mesh,在 Transform 的位置渲染这个Mesh。
MeshRender参数主要指定是否产生和接受阴影,以及使用的Meterial。

当然光有这两个组件还不行,我们还得生成网格,并将网格幅值给MeshFilter组件。
unity中创建一个网格的步骤如下(这里直接以圆柱体网格为例了):
①先new一个Mesh

Mesh mesh = new UnityEngine.Mesh();

有个问题,我们能不能只使用一个Mesh,然后用不同的材质去渲染Mesh的不同部位呢,就像上面Demo中的网格线和圆柱体表面?当然可以的。
一个Mesh只有一份顶点,但是可以有多个submesh,通过Mesh的subMeshCount属性指定。每个submesh有自己的material,各个submesh的材质通过Mesh的materials(材质数组来确定)。代码类似如下。

mesh.subMeshCount = 2;		// 这里指定2个submesh
meshRenderer.materials = new Material[]{
	mat1, // 材质球1
	mat2, // 材质球2
};

②计算出Mesh的所有顶点坐标,并指定给Mesh
Mesh的顶点坐标就是模型坐标,是相对于物体的位置的。
Demo中圆柱体的顶点只计算了下底面和上底面圆周的顶点,上底面、下底面各20个顶点。
顶点编号

List<Vector3> vertices = new List<Vector3>();
// 计算圆柱体上下底面顶点(上下各20个)
const int cnt = 20;
float deltaRad = Mathf.PI * 2 / cnt;
for (int i = 0; i < cnt; i++)
{
    float rad = i * deltaRad;
    float x = radius * Mathf.Sin(rad);
    float z = radius * Mathf.Cos(rad);

    vertices.Add(new Vector3(x, 0, z));                 // 下底面顶点
    vertices.Add(new Vector3(x, height, z));            // 上底面顶点
}
// 将顶点指定给网格
mesh.SetVertices(vertices);

指定Mesh的顶点索引数组,及MeshTopology(网格的拓扑结构,不知道这样翻译对不对)

计算出网格所有顶点后,这些顶点该如何使用?是组成三角面还是组成连接成线?如果组合成三角面,那么哪几个顶点组合呢?这就需要我们设定顶点索引数组和MeshTopology。顶点索引数组是和MeshTopology对应的,MeshTopology变了,顶点索引数组也要跟着调整。

Unity中MeshTopology有五种。

  public enum MeshTopology
  {
    // 三角面
    Triangles = 0,
    // 四边形
    Quads = 2,
    // 线段
    Lines = 3,
    // 线带
    LineStrip = 4,
    // 点
    Points = 5,
  }

比如说我们打算将MeshTopology设置为三角面,那么我们的顶点索引数组长度应该就是3的倍数(因为一个面要3个顶点嘛)。顶点索引数组中,第0、1、2组成第一个三角面,第3、4、5组成第二个三角面,以此类推。
但是要注意顶点的排列顺序,默认情况下,Unity中的所有Shader都是单面的,它都把反面的渲染给关闭掉了(Cull On)。所以顶点索引顺序不对,Shader中又没有Cull Off的话,我们可能就看不见那个面(因为是反面,被剔除啦)。
那怎样的顶点索引顺序才是正面呢?
很简单,三角面的法线方向和我们的视线方向相反,那么我们看到的这个面就是正面,反之为反面。
那么如何判断三角面的法线方向呢?
也很简单,假设我们的顶点索引排列顺序是ABC。
由于Unity局部坐标使用的是左手坐标系,那么举起你的左手,大拇指竖着,食指和小指并拢,由A朝向B弯曲,大拇指的方向就是这个面的法线方向,所以下图中ABC的的法线是朝屏幕外的。而我们是看向屏幕的,即我们的视线方向与面的法线方向相反,所以ABC这个面为正面。
在这里插入图片描述

核心方法就是Mesh的SetIndices。

// 最后的0是subMesh的索引值,从0开始
mesh.SetIndices(indexList.ToArray(), MeshTopology.Triangles, 0);

Demo中绘制侧面的方法。

// 侧面
List<int> indexList = new List<int>();
for (int i = 0; i < cnt; i++)
{
    if (i == cnt - 1)
    {
    	// 最后一个点和第一个点连接起来
        indexList.Add(2 * i);
        indexList.Add(2 * i + 1);
        indexList.Add(0);

        indexList.Add(0);
        indexList.Add(2 * i + 1);
        indexList.Add(1);
    }
    else
    {
        // 要注意顶点的排列顺序,如果shader没有Cull Off,这个面可能不可见
        indexList.Add(2 * i);
        indexList.Add(2 * i + 1);
        indexList.Add(2 * (i + 1));

        indexList.Add(2 * (i + 1));
        indexList.Add(2 * i + 1);
        indexList.Add(2 * i + 3);
    }
}
mesh.SetIndices(indexList.ToArray(), MeshTopology.Triangles, 0);

MeshTopology中的Lines和LineStrap都可以用来画线,但是顶点索引数组有点不同。
假设顶点索引数组为[0, 1, 2, 3, 4,5]。
那么MeshTopology为Lines时,0-1、2-3、4-5将分别组成一条线段,共3条线段。
而MeshTopology为LineStrap时,0-1、1-2、2-3、3-4、4-5将分别组成一条线段,共5条线段。

Demo中使用Lines模式来画网格线。

// 网格线
List<int> gridIndexList = new List<int>();
for (int i = 0; i < cnt; i++)
{
    if (i == cnt - 1)
    {
        gridIndexList.Add(2 * i);
        gridIndexList.Add(0);

        gridIndexList.Add(2 * i + 1);
        gridIndexList.Add(1);

        gridIndexList.Add(2 * i);
        gridIndexList.Add(2 * i + 1);

        gridIndexList.Add(2 * i + 1);
        gridIndexList.Add(0);
    }
    else
    {
        gridIndexList.Add(2 * i);
        gridIndexList.Add(2 * (i + 1));

        gridIndexList.Add(2 * i + 1);
        gridIndexList.Add(2 * i + 3);

        gridIndexList.Add(2 * i);
        gridIndexList.Add(2 * i + 1);

        gridIndexList.Add(2 * i + 1);
        gridIndexList.Add(2 * (i + 1));
    }
}
// 这里subMesh为1了
mesh.SetIndices(gridIndexList.ToArray(), MeshTopology.Lines, 1);

指定Mesh的法线和uv坐标
如果网格需要贴上贴图和光照则需要设置Mesh的法线和uv坐标,每个顶点都需要设置。

// 法线
mesh.normals
// 贴图
mesh.uv

法线也可通过mesh.RecalculateNormals()自动计算出来,但MeshTopology为Lines或Points会报错。
由于Demo中不需要给圆柱体计算光照和贴上贴图,所以Demo中没设置它们。

1.2 一个简单的Shader

由于Shader是透明的,所以关闭深度写入ZWrite Off。由于要两面都要可见,所以关闭剔除Cull Off

Shader "Custom/Cylinder"
{
    Properties
    {
		_MainColor ("Main Color", Color) = (1, 1, 1, 0.5)
	}
	
	SubShader
	{
		Tags { "Queue" = "Transparent" "RenderType" = "Transparent" }
		LOD 100

		Pass
		{
			ZWrite Off
			Cull Off
			Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"
			#include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

			fixed4 _MainColor;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
				return _MainColor;
            }
            ENDCG
        }
    }
}

1.3 相机操控

Demo中我们可以360拖动、放大缩小、旋转查看物体。
之前已经实现过了,具体见这篇文章《Unity3D相机操控(完整模拟Scene视图操作)》。

2 源码

源码
项目放到这儿了。
链接:https://pan.baidu.com/s/1DgHNuG-nBPSrE5bh4f03kA
提取码:6yg8

博主个人博客本文链接。

  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值