在Unity中实现点云的效果

发表于2019-01-29
评论0 8k浏览
在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直接画点,不能调整大小,但是可以流畅得显示很多点。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引