深度图建模
总结
深度图精度获取
地形生成算法
主要进展
- 获取深度图
- 获取深度图:主要是用这篇文章提供的工程生成
https://www.immersivelimit.com/tutorials/unity-depth-camera-simulation?rq=depth
- 获取深度图:主要是用这篇文章提供的工程生成
- 通过深度图获取点集
关键就是一个图坐标转换为世界坐标。
设图的像素中心为原点中心,从中心到像素坐标向量为一个轴,深度图的灰度值为一个轴,两轴经过一定比例放缩得到世界坐标。
for (int i = 0; i < width; i += 10)
{
for (int j = 0; j < height; j += 10)
{
//根据像素点与中心点偏移和深度值,得到实际点离摄像头的位移
Vector2 pixelOffsetV2 = new Vector2(i, j) - new Vector2(i / 2, j / 2);
float depthOffset = depthMap.GetPixel(i, j).r * 255;//深度图,r,g,b值都相同
print(i + " " + j + " " + depthOffset);
if (depthOffset < 250)
{
//转换三维向量
Vector3 pixelOffsetV3 = new Vector3(pixelOffsetV2.x, pixelOffsetV2.y, 0) * pixelK;
Vector3 depthOffsetV3 = new Vector3(0, 0, depthOffset * depthK);
//得到最终位移
Vector3 realOffset = pixelOffsetV3 + depthOffsetV3;
GameObject point = Instantiate(sphere, camera.transform.position + realOffset, Quaternion.identity, parent);
points[i, j] = point;
}
}
}
-
渲染点云
-
通过gameobject实现点云
最简单的方法,在每个点实例化一个小球,性能消耗较大,间隔取像素和剔除深度无穷远的像素,可较快并实时预览点云生成效果,实时调整轴放缩系数
-
通过mesh的点模式实现点云
主要参考下面资料
mesh结构的基本用法
https://docs.unity.cn/cn/current/Manual/Example-CreatingaBillboardPlane.html
mesh的点线渲染
https://blog.csdn.net/zhudaokuan/article/details/119609315
-
构建三角面mesh
关键在于三角索引的顺序,最近的像素点画三角形,距离信息由二维数组的索引号表示,通过遍历每个点和距离找到合适的三个点画三角形。
想象每个点作为四边形的左下角的那个点,每个四边形可以画两个三角形,所以每个点可以画两个三角形,先画右下角,再画左上角。直到把所有点遍历完。
/// <summary>
/// 根据深度图生成三角面
/// </summary>
void InitTriMesh2()
{
int width = depthMap.width;
int height = depthMap.height;
//将得到的点转化为二维数组,几何位置近的画四边形
int[,] pointsIndex = new int[width, height];
//算一下生成的点数
int num = 0;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
float depthOffset = depthMap.GetPixel(i, j).r * 255;//深度图,r,g,b值都相同
if (depthOffset < 250)//有效点
{
if (isOpsite)
depthOffset = 255 - depthOffset;//取反~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
num++;
}
}
}
positions = new Vector3[num];
indices = new int[num * 6];//三个点组成三角形的三个顶点,按最大复用率算
colors = new Color[num];
int index = 0;
//for (int i = 0; i < width; i += 10)
//{
// for (int j = 0; j < height; j += 10)
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
//根据像素点与中心点偏移和深度值,得到实际点离摄像头的位移
Vector2 pixelOffsetV2 = new Vector2(i, j) - new Vector2(i / 2, j / 2);
float depthOffset = depthMap.GetPixel(i, j).r * 255;//深度图,r,g,b值都相同
if (isOpsite)
depthOffset = 255 - depthOffset;//取反~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
print(i + " " + j + " " + depthOffset);
if (depthOffset < 250)
{
//转换三维向量
Vector3 pixelOffsetV3 = new Vector3(pixelOffsetV2.x, pixelOffsetV2.y, 0) * pixelK;
Vector3 depthOffsetV3 = new Vector3(0, 0, depthOffset * depthK);
//得到最终位移
Vector3 realOffset = pixelOffsetV3 + depthOffsetV3;
positions[index] = realOffset;
pointsIndex[i, j] = index;//存储索引
//indices[index] = index;
colors[index] = Color.gray;
index++;
}
}
}
//设置三角形索引
int triIndex = 0;
for (int i = 0; i < pointsIndex.GetLength(0); i++)
{
for (int j = 0; j < pointsIndex.GetLength(1); j++)
{
if (pointsIndex[i, j] != 0)//可能会抛弃掉第一个点 。分别找最近的点画两个三角形
{
//第一个三角形
indices[triIndex] = pointsIndex[i, j];
triIndex++;
//找到右上角最近的
int temI = i;
int temJ = j;
do
{
temI++;
temI %= pointsIndex.GetLength(0);
temJ++;
temJ %= pointsIndex.GetLength(1);
} while (pointsIndex[temI, temJ] == 0);//非有效索引就继续找
indices[triIndex] = pointsIndex[temI, temJ];
triIndex++;
//找到右边最近的
temI = i;
temJ = j;
do
{
temI++;
temI %= pointsIndex.GetLength(0);
} while (pointsIndex[temI, temJ] == 0);//非有效索引就继续找
indices[triIndex] = pointsIndex[temI, temJ];
triIndex++;
//第二个三角形
indices[triIndex] = pointsIndex[i, j];
triIndex++;
//找到上面最近的
temI = i;
temJ = j;
do
{
temJ++;
temJ %= pointsIndex.GetLength(1);
} while (pointsIndex[temI, temJ] == 0);//非有效索引就继续找
indices[triIndex] = pointsIndex[temI, temJ];
triIndex++;
//找到右上角最近的
temI = i;
temJ = j;
do
{
temI++;
temI %= pointsIndex.GetLength(0);
temJ++;
temJ %= pointsIndex.GetLength(1);
} while (pointsIndex[temI, temJ] == 0);//非有效索引就继续找
indices[triIndex] = pointsIndex[temI, temJ];
triIndex++;
}
}
}
mesh = pointMeshObj.GetComponent<MeshFilter>().mesh;
mesh.vertices = positions;
mesh.colors = colors;
mesh.SetIndices(indices, MeshTopology.Triangles, 0);
//MeshUtility.Optimize(mesh);
}
发现精度比较低,后来发现是unity会将导入的图像压缩导致的,将图片设为不压缩,效果会好一些。但还是没有光滑的效果。
- unity地形系统
unity 地形系统可以通过高度图去生成地形。但是由于图片格式和我的深度图与Unity高度图色差相反,以及一些未知的图片尺寸问题,我的深度图导入到地形中生成不了正常地形。
相反的,用适用于地形的高度图,放到我的方法里去跑,可以生成点云,但是生成不了正常的模型,更别提光滑,可见实现方法还有很大差距。
很多参考资料都涉及到shader相关知识,不仅为了渲染,还有大量的计算,感觉无论从基础实现还是优化,应该都绕不开shader。
参考资料
获取深度图
https://www.immersivelimit.com/tutorials/unity-depth-camera-simulation?rq=depth
点云实现
https://docs.unity.cn/cn/current/Manual/Example-CreatingaBillboardPlane.html
https://blog.csdn.net/zhudaokuan/article/details/119609315
https://blog.csdn.net/zhudaokuan/article/details/120135224?spm=1001.2014.3001.5501