在Unity中实现点云的效果
发表于2019-01-29
在unity中实现点云的效果可以丰富我们的场景,并且可以与kinect等rgbd相机结合,增加互动。
如果要在unity中实现点云的效果这里有两种方法,一种是用自带的粒子系统,将每个点云中的点对应于每个粒子,并把对应的坐标,颜色赋值给粒子。第二种是用游戏物体的MeshFilter组件,只渲染顶点而不Mesh,再重写shader,赋予每个顶点颜色。
每种方法各有利弊,下面是分享下实现点云效果的过程:
1. 利用Unity中的粒子系统
using UnityEngine; using System.Collections; using System.IO; public class GetPointCloudFromTXT : MonoBehaviour { ParticleSystem particleSystem; // 整个粒子系统 ParticleSystem.Particle[] allParticles; // 所有粒子的集合 int pointCount; // 粒子数目 // Use this for initialization void Start() { particleSystem = GetComponent<ParticleSystem>(); // 1. 读取数据 // 提前将点云存成txt文件放在Assert/StreamingAssets文件夹下,文本的每行代表一个点,由点的x,y,z,r,g,b六个float组成 string fileAddress = (Application.streamingAssetsPath + "/" + "jiao.txt"); FileInfo fInfo0 = new FileInfo(fileAddress); //Debug.Log(fileAddress); string s = ""; StreamReader r; ArrayList arrayListXYZ = new ArrayList(); ArrayList arrayListRGB = new ArrayList(); if (fInfo0.Exists) { r = new StreamReader(fileAddress); } else { Debug.Log("NO THIS FILE!"); return; } // 将文本中的点云数据读入分别存到xyz数组和rgb数组中 while ((s = r.ReadLine()) != null) { string[] words = s.Split(" "[0]); Vector3 xyz = new Vector3(float.Parse(words[0]), -float.Parse(words[1]), float.Parse(words[2])); arrayListXYZ.Add(xyz); Color colorRGB = new Color(float.Parse(words[3]) / 255.0f, float.Parse(words[4]) / 255.0f, float.Parse(words[5]) / 255.0f); arrayListRGB.Add(colorRGB); //Debug.Log(xyz.ToString() + "," + colorRGB.ToString()); } // 2. 设置粒子系统 particleSystem.startSpeed = 0.0f; // 设置粒子的初始速度为0 particleSystem.startLifetime = 1000.0f; // 粒子的生命周期尽量长 // 3. 渲染出来(动态加载点云的时候再改这部分代码) pointCount = arrayListRGB.Count; allParticles = new ParticleSystem.Particle[pointCount]; particleSystem.maxParticles = pointCount; // 设置粒子系统粒子数的最大值 particleSystem.Emit(pointCount); // 发射pointCount个粒子 particleSystem.GetParticles(allParticles); // 获取当前还存活的粒子数,不能少的一步,但是不是很清楚为什么 for (int i = 0; i < pointCount; i++) { allParticles[i].position = (Vector3)arrayListXYZ[i]; // 设置每个点的位置 allParticles[i].startColor = (Color)arrayListRGB[i]; // 设置每个点的rgb allParticles[i].startSize = 0.01f; // 设置点的大小,注意还要在unity界面上将粒子系统下面的Render模块中的Min/Max Particle Size改小--0.01差不多了 } particleSystem.SetParticles(allParticles, pointCount); // 将点云载入粒子系统 } }
2. 利用Mesh编写Shader
一次最多只能渲染出60000个点的模型所以要动态生成多个模型组合形成一个完整的点云
VertexColor.shader
Shader "Custom/VertexColor" { SubShader { Pass { LOD 200 CGPROGRAM #pragma vertex vert #pragma fragment frag struct VertexInput { float4 v : POSITION; float4 color: COLOR; }; struct VertexOutput { float4 pos : SV_POSITION; float4 col : COLOR; }; VertexOutput vert(VertexInput v) { VertexOutput o; o.pos = mul(UNITY_MATRIX_MVP, v.v); o.col = v.color; return o; } float4 frag(VertexOutput o) : COLOR { return o.col; } ENDCG } } }
using UnityEngine; using System.Collections; using System.IO; public class DisplayPointCloudByGpu : MonoBehaviour { int numPoints = 60000; void Start() { // 1. 读取数据 // 提前将点云存成txt文件放在Assert/StreamingAssets文件夹下,文本的每行代表一个点,由点的x,y,z,r,g,b六个float组成 string fileAddress = (Application.streamingAssetsPath + "/" + "test.txt"); FileInfo fInfo0 = new FileInfo(fileAddress); //Debug.Log(fileAddress); string s = ""; StreamReader r; ArrayList arrayListXYZ = new ArrayList(); ArrayList arrayListRGB = new ArrayList(); if (fInfo0.Exists) { r = new StreamReader(fileAddress); } else { Debug.Log("NO THIS FILE!"); return; } // 将文本中的点云数据读入分别存到xyz数组和rgb数组中 while ((s = r.ReadLine()) != null) { string[] words = s.Split(" "[0]); Vector3 xyz = new Vector3(float.Parse(words[0]), -float.Parse(words[1]), float.Parse(words[2])); arrayListXYZ.Add(xyz); Color colorRGB = new Color(float.Parse(words[3]) / 255.0f, float.Parse(words[4]) / 255.0f, float.Parse(words[5]) / 255.0f); arrayListRGB.Add(colorRGB); //Debug.Log(xyz.ToString() + "," + colorRGB.ToString()); } // 2. 渲染 int num = arrayListRGB.Count; int meshNum = num / numPoints; int leftPointsNum = num % numPoints; int i = 0; for (; i < meshNum; i++) { GameObject obj = new GameObject(); obj.name = i.ToString(); obj.AddComponent<MeshFilter>(); obj.AddComponent<MeshRenderer>(); Mesh tempMesh = new Mesh(); CreateMesh(ref tempMesh, ref arrayListXYZ, ref arrayListRGB, i * numPoints, numPoints); Material material = new Material(Shader.Find("Custom/VertexColor")); obj.GetComponent<MeshFilter>().mesh = tempMesh; obj.GetComponent<MeshRenderer>().material = material; } GameObject objLeft = new GameObject(); objLeft.name = i.ToString(); objLeft.transform.position = new Vector3(Random.Range(-10, 10), Random.Range(-10, 10), Random.Range(-10, 10)); objLeft.AddComponent<MeshFilter>(); objLeft.AddComponent<MeshRenderer>(); Mesh tempMeshLeft = new Mesh(); CreateMesh(ref tempMeshLeft, ref arrayListXYZ, ref arrayListRGB, i * numPoints, leftPointsNum); Material materialLeft = new Material(Shader.Find("Custom/VertexColor")); objLeft.GetComponent<MeshFilter>().mesh = tempMeshLeft; objLeft.GetComponent<MeshRenderer>().material = materialLeft; } void CreateMesh(ref Mesh mesh, ref ArrayList arrayListXYZ, ref ArrayList arrayListRGB, int beginIndex, int pointsNum) { Vector3[] points = new Vector3[pointsNum]; Color[] colors = new Color[pointsNum]; int[] indecies = new int[pointsNum]; for (int i = 0; i < pointsNum; ++i) { points[i] = (Vector3)arrayListXYZ[beginIndex + i]; indecies[i] = i; colors[i] = (Color)arrayListRGB[beginIndex + i]; } mesh.vertices = points; mesh.colors = colors; mesh.SetIndices(indecies, MeshTopology.Points, 0); } }
总结
利用粒子系统可以模拟出点云的效果,并且可以调整点的大小和形状,但是点一多起来就很很难渲染。而利用Mesh直接画点,不能调整大小,但是可以流畅得显示很多点。