上一篇文章:在unity向量空间内通过将极坐标转换为直角坐标,绘制阿基米德螺线,对数螺线与玫瑰线等几何图形,研究了如何通过极坐标寻找2D平面中一些几何图形上的连续坐标点。

今天进一步研究一下如何获取一个3D球面表面上的连续坐标点。关键词:极坐标,黑客帝国,五毛。

Deus Ex Machina

在电影Matrix黑客帝国第三部“矩阵革命”的最后部分,有一段Neo独闯机器城与机器世界首领交涉的情节。

(电影截图:Neo独面机器世界主宰)

这个机器首领的名字叫做Deus Ex Machina,拉丁文翻译成英文是God out of the machine,是机器城市的中枢,控制着未来世界中的所有机器。外形简单来说就是一个胖球加一堆刺。

下文将会尝试在Unity向量空间内,通过将球面的极坐标公式转化成代码模拟重构这个“上帝机器”。

通过极坐标求球体表面坐标点

首先先了解三维空间内的球面的极坐标公式,假如有以下三维空间坐标系:

那么,球面sphere上的任意一点的三维坐标x,y,z,可以用以下极坐标公式表示:
x=r∗sin⁡θ∗cos⁡ϕx=r*\sin\theta*\cos\phix=r∗sinθ∗cosϕ
y=r∗sin⁡θ∗sin⁡ϕy=r*\sin\theta*\sin\phiy=r∗sinθ∗sinϕ
y=r∗cos⁡θy=r*\cos\thetay=r∗cosθ
(公式-1)

有了上一篇文章的基础,我们用相似的思想把公式转换为一个可以返回连续xy坐标点的C#函数:

 public Vector3[] GenerateSSphereVectro3(int radius,Vector3 centre,int thetaPortion,int phiPortion){Vector3[] coordinates=new Vector3[thetaPortion*phiPortion];float thetaMultiplier=360f/(float)thetaPortion;float phiMultiplier=360f/(float)phiPortion;for(int i=0,index=0;i<thetaPortion;i++){for(int j=0;j<phiPortion;j++){coordinates[index]=new Vector3(radius*Mathf.Sin(i*thetaMultiplier)*Mathf.Cos(j*phiMultiplier),radius*Mathf.Sin(i*thetaMultiplier)*Mathf.Sin(j*phiMultiplier),radius*Mathf.Cos(i*thetaMultiplier));index+=1;}}return coordinates;}

参数说明:
int radius:球体半径。
Vector3 centre:球体圆心。
int thetaPortion:将θ\thetaθ角平均分成的份数。
int phiPortion:将ϕ\phiϕ角平均分成的份数。

返回值说明:
Vector3 coordinates:一组分布在球面的三维坐标向量。数量为thetaPortion*phiPortion。

函数解析:
1,根据传进的thetaPortion与phiPortion参数声明一组三维坐标数组

Vector3[] coordinates=new Vector3[thetaPortion*phiPortion];

2,根据传进的thetaPortion与phiPortion参数计算坐标点在极坐标公式中相差的角度。

float thetaMultiplier=360f/(float)thetaPortion;
float phiMultiplier=360f/(float)phiPortion;

3,通过两个for循环,依次寻找球面上的每个坐标点。

for(int i=0,index=0;i<thetaPortion;i++){for(int j=0;j<phiPortion;j++){coordinates[index]=new Vector3(radius*Mathf.Sin(i*thetaMultiplier)*Mathf.Cos(j*phiMultiplier),radius*Mathf.Sin(i*thetaMultiplier)*Mathf.Sin(j*phiMultiplier),radius*Mathf.Cos(i*thetaMultiplier));index+=1;}
}

在Unity Editor内生成锥形体


(图1:CreateCone生成的锥形体)

Unity自带的几何模型中没有圆锥体。以下是生成圆锥体的代码,搬的砖,源码地址http://wiki.unity3d.com/index.php?title=CreateCone,作者Wolfram Kresse。将此脚本放在/Assets/Editor 文件夹下。然后上方菜单的GameObject/CreatOther里会出现cone。

using UnityEngine;
using UnityEditor;
using System.Collections;// an Editor method to create a cone primitive (so far no end caps)
// the top center is placed at (0/0/0)
// the bottom center is placed at (0/0/length)
// if either one of the radii is 0, the result will be a cone, otherwise a truncated cone
// note you will get inevitable breaks in the smooth shading at cone tips
// note the resulting mesh will be created as an asset in Assets/Editor
// Author: Wolfram Kresse
public class CreateCone : ScriptableWizard {public int numVertices = 10;public float radiusTop = 0f;public float radiusBottom = 1f;public float length = 1f;public float openingAngle = 0f; // if >0, create a cone with this angle by setting radiusTop to 0, and adjust radiusBottom according to length;public bool outside = true;public bool inside = false;public bool addCollider = false;[MenuItem ("GameObject/Create Other/Cone")]static void CreateWizard(){ScriptableWizard.DisplayWizard("Create Cone", typeof(CreateCone));}void OnWizardCreate(){GameObject newCone=new GameObject("Cone");if(openingAngle>0&&openingAngle<180){radiusTop=0;radiusBottom=length*Mathf.Tan(openingAngle*Mathf.Deg2Rad/2);}string meshName = newCone.name + numVertices + "v" + radiusTop + "t" + radiusBottom + "b" + length + "l" + length + (outside?"o":"") + (inside?"i":"");string meshPrefabPath = "Assets/Editor/" + meshName + ".asset";Mesh mesh = (Mesh)AssetDatabase.LoadAssetAtPath(meshPrefabPath, typeof(Mesh));if(mesh==null){mesh=new Mesh();mesh.name=meshName;// can't access Camera.current//newCone.transform.position = Camera.current.transform.position + Camera.current.transform.forward * 5.0f;int multiplier=(outside?1:0)+(inside?1:0);int offset=(outside&&inside?2*numVertices:0);Vector3[] vertices=new Vector3[2*multiplier*numVertices]; // 0..n-1: top, n..2n-1: bottomVector3[] normals=new Vector3[2*multiplier*numVertices];Vector2[] uvs=new Vector2[2*multiplier*numVertices];int[] tris;float slope=Mathf.Atan((radiusBottom-radiusTop)/length); // (rad difference)/heightfloat slopeSin=Mathf.Sin(slope);float slopeCos=Mathf.Cos(slope);int i;for(i=0;i<numVertices;i++){float angle=2*Mathf.PI*i/numVertices;float angleSin=Mathf.Sin(angle);float angleCos=Mathf.Cos(angle);float angleHalf=2*Mathf.PI*(i+0.5f)/numVertices; // for degenerated normals at cone tipsfloat angleHalfSin=Mathf.Sin(angleHalf);float angleHalfCos=Mathf.Cos(angleHalf);vertices[i]=new Vector3(radiusTop*angleCos,radiusTop*angleSin,0);vertices[i+numVertices]=new Vector3(radiusBottom*angleCos,radiusBottom*angleSin,length);if(radiusTop==0)normals[i]=new Vector3(angleHalfCos*slopeCos,angleHalfSin*slopeCos,-slopeSin);elsenormals[i]=new Vector3(angleCos*slopeCos,angleSin*slopeCos,-slopeSin);if(radiusBottom==0)normals[i+numVertices]=new Vector3(angleHalfCos*slopeCos,angleHalfSin*slopeCos,-slopeSin);elsenormals[i+numVertices]=new Vector3(angleCos*slopeCos,angleSin*slopeCos,-slopeSin);uvs[i]=new Vector2(1.0f*i/numVertices,1);uvs[i+numVertices]=new Vector2(1.0f*i/numVertices,0);if(outside&&inside){// vertices and uvs are identical on inside and outside, so just copyvertices[i+2*numVertices]=vertices[i];vertices[i+3*numVertices]=vertices[i+numVertices];uvs[i+2*numVertices]=uvs[i];uvs[i+3*numVertices]=uvs[i+numVertices];}if(inside){// invert normalsnormals[i+offset]=-normals[i];normals[i+numVertices+offset]=-normals[i+numVertices];}}mesh.vertices = vertices;mesh.normals = normals;      mesh.uv = uvs;// create triangles// here we need to take care of point order, depending on inside and outsideint cnt=0;if(radiusTop==0){// top conetris=new int[numVertices*3*multiplier];if(outside)for(i=0;i<numVertices;i++){tris[cnt++]=i+numVertices;tris[cnt++]=i;if(i==numVertices-1)tris[cnt++]=numVertices;elsetris[cnt++]=i+1+numVertices;}if(inside)for(i=offset;i<numVertices+offset;i++){tris[cnt++]=i;tris[cnt++]=i+numVertices;if(i==numVertices-1+offset)tris[cnt++]=numVertices+offset;elsetris[cnt++]=i+1+numVertices;}}else if(radiusBottom==0){// bottom conetris=new int[numVertices*3*multiplier];if(outside)for(i=0;i<numVertices;i++){tris[cnt++]=i;if(i==numVertices-1)tris[cnt++]=0;elsetris[cnt++]=i+1;tris[cnt++]=i+numVertices;}if(inside)for(i=offset;i<numVertices+offset;i++){if(i==numVertices-1+offset)tris[cnt++]=offset;elsetris[cnt++]=i+1;tris[cnt++]=i;tris[cnt++]=i+numVertices;}}else{// truncated conetris=new int[numVertices*6*multiplier];if(outside)for(i=0;i<numVertices;i++){int ip1=i+1;if(ip1==numVertices)ip1=0;tris[cnt++]=i;tris[cnt++]=ip1;tris[cnt++]=i+numVertices;tris[cnt++]=ip1+numVertices;tris[cnt++]=i+numVertices;tris[cnt++]=ip1;}if(inside)for(i=offset;i<numVertices+offset;i++){int ip1=i+1;if(ip1==numVertices+offset)ip1=offset;tris[cnt++]=ip1;tris[cnt++]=i;tris[cnt++]=i+numVertices;tris[cnt++]=i+numVertices;tris[cnt++]=ip1+numVertices;tris[cnt++]=ip1;}}mesh.triangles = tris;      AssetDatabase.CreateAsset(mesh, meshPrefabPath);AssetDatabase.SaveAssets();}MeshFilter mf=newCone.AddComponent<MeshFilter>();mf.mesh = mesh;newCone.AddComponent<MeshRenderer>();if(addCollider){MeshCollider mc=newCone.AddComponent<MeshCollider>();mc.sharedMesh=mf.sharedMesh;}Selection.activeObject = newCone;}
}

接着实例化thetaPortion*phiPortion个圆锥体,把球面的三维坐标依次赋值给它们的position,调整角度,为了达成动画效果,构造一些空物体作为圆锥体们的父物体。并在Update里面让父物体们进行旋转。

     if(rotateFlag){for(int i=0;i<thetaPortion;){parents[i].transform.Rotate(Vector3.forward,rotateSpeed);i+=2;}for(int i=1;i<thetaPortion;){parents[i].transform.Rotate(Vector3.back,rotateSpeed);i+=2;}}


(图2:立方体版本)

(图3:圆锥体版本)

放进去一些雷电的特效,闪电的中心点在圆心,闪电的终点随机寻找各个圆锥体的尖部。如果有对这个特效感兴趣的,可在AssetStore内搜索Procedural Examples,免费。

 IEnumerator lightingMove(){for(int i=0;i<100;i++){for(int j=0;j<21;j++){lightingEmitters[j].transform.position=Ball[Random.Range(0,1296)];yield return new WaitForSeconds(0.2f);}}}


(图4,加上雷电后)

加些音效,利用DoTween写个摄像机移动路径,录制视频,完成。

 private Vector3[] circlePoints=new Vector3[200];// Use this for initializationvoid Start () {for(int i=0;i<200;i++){circlePoints=Circle(200,18,new Vector3(0f,5f,0f));}transform.position=circlePoints[0];transform.DOPath(circlePoints,130f);}// Update is called once per framevoid Update () {transform.LookAt(Vector3.zero);}



(图5/6,最终五毛特效视频截图)

做好的五毛特效视频地址:http://v.youku.com/v_show/id_XMTU5MTI5OTU0OA==.html


维护日志:
2020-8-2:review

在unity向量空间内绘制几何(2):计算球体的表面坐标相关推荐

  1. 在unity向量空间内绘制几何(3):通过三角形重心坐标寻找三角形内任意点的位置

    文章目录 1,三角形的重心坐标(面积坐标) 2,演示函数:随机选择网格上的一个三角形并计算一个随机重心坐标点最后返回它的相对位置坐标 3,演示函数:输入重心坐标与三个顶点位置,返回相对坐标 1,三角形 ...

  2. 在unity向量空间内绘制几何(4): 利用平面几何知识画像素直线

    这两年像素很火,<我的世界>也要进入国内了. 问题:假如有一个像素平面,既是一个充满四方格子平面,给出任意两个格子的坐标,画出两个格子间的直线. 接下来,尝试在平面几何的角度下思考并解决这 ...

  3. 在unity向量空间内绘制几何(1):通过将极坐标转换为直角坐标,绘制阿基米德螺线,对数螺线与玫瑰线等几何图形

    一些基础几何图形的极坐标公式 极坐标内的每个点都有两个参数: r, 与 θ\thetaθ.r为此点到极点(中心点)的距离,θ\thetaθ 为此点到极点的线段与极轴(类似x轴)的夹角. 很多几何图形公 ...

  4. chatgpt赋能python:Python计算球体表面积和体积

    Python计算球体表面积和体积 如果您需要计算球体的表面积和体积,那么Python可以成为您的助手.Python在科学计算领域中越来越受欢迎,因为它是一个灵活且易于使用的语言.Python拥有大量的 ...

  5. 《分布式虚拟现实系统(DVR)》(Yanlz+Unity+SteamVR+分布式+DVR+人工智能+边缘计算+人机交互+云游戏+框架编程+立钻哥哥+)

    <分布式虚拟现实系统(DVR)> <分布式虚拟现实系统(DVR)> 版本 作者 参与者 完成日期 备注 YanlzVR_DVR_V01_1.0 严立钻 2019.07.11 # ...

  6. unity urp内置lit材质源码解析(上)

    之前我发布过一篇对urp的内置shader lit的结构解析,发现自己说的也不完善,这次直接对源码进行一个解析,并提升一下自己的记忆. 如果你找不到这个shader,那么就有可能你不是urp渲染管线. ...

  7. 计算梯形面积的程序html,如何利用几何画板计算梯形的面积

    作为一种几何绘图软件,不仅可以利用几何画板绘制很多图形,还可以计算图形的面积.本文就向大家介绍如何利用几何画板计算梯形的面积. 1.绘制上底.打开几何画板,使用线段工具绘制线段AB.使用点工具,在线段 ...

  8. Unity 使用LineRenderer绘制贝塞尔曲线

    Unity 使用LineRenderer绘制贝塞尔曲线 LineRenderer介绍 LineRenderer线渲染器,在三维空间中渲染线段和曲线段. 贝塞尔曲线介绍 通过很少的控制点,生成复杂的平滑 ...

  9. Opencv-Python图像像素均值、方差,绘制几何形状

    图像像素均值.方差 计算数组元素的均值和方差函数cv.meanStdDev().传入数组数据,返回数组数据的均值和方差.在图像数组中,可以计算出图像数组的像素均值和方差. 使用np.min和np.ma ...

最新文章

  1. Android中网络使用
  2. BEBLID:增强的高效局部图像特征描述符
  3. 选择之后触_如果有朋友在做选择时左右为难,我应该该给出什么样的建议
  4. tf 从RNN到BERT
  5. 前端学习(2861):简单秒杀系统学习之前端优化css
  6. ttc文件linux安装,centos系统安装中文字体几种方法
  7. raid5用户mbr还是gpt_系统硬盘gpt转换的操作方法
  8. c语言退格键ascii码,【回车键的ASCII码是多少】
  9. 参数篡改(Parameter Tampering)
  10. SAP SM36如何设置后台作业 每月最后一天运行
  11. DRM in Android详解
  12. 学术篇 | 面向分类的脑电接口Fuzzy-Rough特征选择
  13. C++一本通题库1021
  14. 线程与全局解释器锁(GIL)
  15. linux新建挂载目录命令,告诉你Ubuntu添加新分区并设置挂载点的方法及命令
  16. 手机优酷下载视频怎么保存到手机
  17. SecureFX 中文乱码
  18. python 读取合并单元格的数据_Python使用xlrd实现读取合并单元格
  19. 没有对象的进来找个对象吧
  20. 查看mysql的sql运行记录

热门文章

  1. Fisher线性判别(*)
  2. oracle10 64位odbc,图文教你64位win10添加oracle odbc驱动时提示无法加载oracle如何解决...
  3. java foreach6_Java foreach循环是否会创建一个新对象?
  4. 睡眠多少分钟一个循环_睡眠分为几个阶段每个阶段大概多少时间?
  5. java 安全认证_restful安全认证
  6. 3_python基础—运算符 1
  7. 将某一类型文件还原为无默认打开方式
  8. Linux(五):Ubuntu 16.04 更改系统语言为简体中文(Chinese simplified)
  9. 400集python入门到精通_2020年最强Python学习路线+教程,400集带你从入门到精通
  10. linux怎么获取目录名,linux下如何获取目录名?(四种方法)