请选择 进入手机版 | 继续访问电脑版

开源计算机图形学社区(Open Source Computer Graphics Community) |OpenGPU Forum (2007-2013)| OpenGPU Project

 找回密码
 注册
搜索
查看: 1741|回复: 6

体素(Voxel)显示学习中 [复制链接]

Rank: 12Rank: 12Rank: 12

注册时间
2012-7-21
积分
733
发表于 2016-12-23 10:47:18 |显示全部楼层
本帖最后由 quadpixels 于 2016-12-30 10:33 编辑

大家好,我又肥来了
我想知道类似 Minecraft 那样的体素世界是如何显示的。
我现在能有的线索只是“Octree”这个关键词。因为它能够在 GPU 上方便高效地进行处理,所以掌握了应该能够受用很久。
近日的一些探索的过程就顺便记录在这里吧。

12月的前两个星期:从最基本的数据结构 到 CPU 上的 Raycast (是个未完并且抛弃了的坑)
首先看了看《How to make a Voxel Engine》,知道应该把一个大世界分成小的 Chunk 来处理。这样的好处可能是进行视锥剔除时比较方便。
所以就定义了数据结构和用 glutSolidCube() 来画 Chunk 的方法…
同时又经历了一遍复习 OpenGL 的变换流程的过程,知道了怎么将屏幕上鼠标的点击转换成世界坐标系中的射线。
有了射线之后,就完成了用 DDA 的方法遍历一个 Chunk 的方法,以及选取一个体素的方法。
有了这个方法,其实已经就可以 Raycast 一个 Chunk 了!但是还是先把 Octree 做出来罢。
于是就把 Octree 的插入、查找和迭代完成了。都不是最优的,后来发现用于 Raycast 还是很慢。但总之还是完成了。
在做 Raycasting 的时候,左边显示用 OpenGL 画出的画面,右边显示 Raycast 出来的画面。嗯。方块对应的位置都差不多。说明大概位置的计算应该没有问题(虽然速度不怎么样。)
想试试看这个 Octree 最大能够处理多大的数据,就去 gltracy 的 Github 上下载了他的一些 SVO 体素数据。
我的导入是每读到一个体素都通过 Octree 自根结点向下插入一个 Voxel 来做的,所以速度很慢,要几分钟才能导入 1024^3 的数据。所以就最多只尝试了 512^3 的数据。
结果:在 Core 2 T9600 的 CPU 上,如果开 4 个线程,在 640x480 的画面下能达到 3~7 FPS。(依显示的角度不同而不同)(如果拿一个强力的 CPU,比如 i5-4570,能达到 20~30 FPS)
这个性能距离网上的人达成的结果有差距。因为有人用 迅驰 1.66 GHz 达成了 1280x720 画面、1024^3 的体素下 7 FPS 的成绩。所以我的实现中有很多不够快的地方。
后来还尝试了两种更不成熟的优化。第一是将原先递归式的 Octree 遍历换成了非递归的(用一个 std::stack 模拟栈),性能变差了。第二是尝试写出 Ray Packet 的方式,性能又变差了。现在我还不清楚为什么会变差,但是以后有时间一定要再检查一下。
那么,经历这些不成熟的优化之后我感觉目前的这摊东西可能不能满足实时的 Raycast 的需求,那不如先去解决最初的问题:怎么才能显示体素呢?现在看来,在体素世界比较小的情况下 Raycast 比直接用 OpenGL 显示要慢太多了,那不如先暂放弃 Raycast 的想法得了。
其实更值得尝试的优化方法是利用体素原本的特点进行 LOD,但是既然这个 LOD 在用 OpenGL 进行显示时也必然会在某个时候遇到,所以也可以到那时再研究。
用一张 Raycast 的结果纪念目前的状态


12月的后两个星期:改为直接用 OpenGL 显示并加入 SSAO
有了之前在 Raycasting 上撞墙的经历,一些用到 Octree 的地方,就因为先前的经验而变简单了。比如:视锥剔除。
虽然之前的 Raycast 的部分全被暂时放弃了,但是用 Octree 来组织所有的 Chunk 的结构还是保留了下来。所以在显示时也是遍历 Octree 的。
在遍历时做视锥剔除就是很自然的,因为可以直接与 Octree 结点所对应的 AABB 来进行求交。有了视锥剔除,帧率就更加稳定了一点。

然后就想搜罗一些已有的 OpenGL 特效加入进来。
在 Github 上找到了一个 SSAO 的演示,大概明白了其原理。
结果发现它是用 OpenGL 3.3 写成的,用到了 GLSL 3.3。而我到目前为止用的都是 Legacy OpenGL,必须将所有代码都替换成 OpenGL 3.3 的。
替换的过程经历了三天。
那个演示中的投影矩阵和我的不太一样,他的 Y 在 Shader 中与我是相反的。
把这个地方改正了之后,就有了如下的画面。


接下来,我想加入颜色与阴影,以及现有模型的导入(我主要知道的体素工具是 MagicaVoxel,所以打算就先导入 MagicaVoxel 的数据。看起来它的格式是以命令的形式组织而成的格式)

2016-12-27:加了一点点颜色与阴影。做出来的画质还很差,打算找个时间一起修理


2016-12-28:搭了一些 deferred shading 的基本结构(其实也就是 G Buffer) … 不过在这个图里可能看不出来。
现在烦恼的事情是帧速太低了,需要 profile 一下找到速度慢的原因。


未完待续


Rank: 12Rank: 12Rank: 12

注册时间
2012-7-21
积分
733
发表于 2017-1-27 07:56:23 |显示全部楼层
啊?不能编辑了吗。

2017-01-26
最近需要将 MagicaVoxel 模型导入作为精灵(sprites),每个精灵可以有自己的旋转、位移、缩放和锚点属性。

于是我才明白为什么 Model Matrix 是必要的,而不能总是和 View Matrix 一起合并成 Model View Matrix:那就是因为 Model Matrix 能得到每个模型的局部转换完成之后的世界坐标,这样对于需要直接取世界坐标的函数来说就会方便。而这世界坐标可能在 CPU 侧才知道。在 CPU 侧知道了之后,想要让 Shader 侧也知道,达成这个目的方法就是把 Model Matrix 也传进去 —— 如果在 VBO 里存的数据只是局部坐标系中的数据的话。
譬如说 shadow map 的生成与查询 和 体素的渲染本身 就是两个不同的 View 。

加了这个功能后影响了阴影的生成和旋转位移位置的确定,所以又不得不把代码翻来覆去改了一遍。


使用道具 举报

Rank: 12Rank: 12Rank: 12

注册时间
2012-7-21
积分
733
发表于 2017-2-8 00:19:09 |显示全部楼层
本帖最后由 quadpixels 于 2017-2-8 00:31 编辑

2017-02-07

目前为止的 meshing 都没有优化,一个体素中没被遮挡住的面都会被 mesh ,一个体素就会最多有六个面。
目前为止的问题则是相邻的同色体素会有两个正方形的面,而不是一个长方形的面。这其实是很低效的,因为会导致顶点数据增加很多,很快就把显存占满了。
于是在 meshing 的过程中将每一行中连续的同色体素合成一个长方形。
这可能是叫做 RLE(Run-Length Encoding)吧。
这样就能把面数缩减成原先的二分之一至四分之一不等。

比如以下场景,是 512 x 512 x 128 的。(我知道 shadow mapping 还有问题, 打算留待稍后再修理…)
低效的 meshing:4332768 面,17.76 fps
RLE 的 meshing:1080490 面,33.00 fps

只有原来的四分之一多面了。应该还可以再缩减的。
缩减的话题在网上的这篇文章里也提到了: https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/

这最大的好处,就是能够显示的世界的尺寸提高了吧。



使用道具 举报

Rank: 13Rank: 13Rank: 13Rank: 13

注册时间
2011-11-15
积分
1301
发表于 2017-2-13 06:33:59 |显示全部楼层
不知道你有没有考虑试试GPU处理,GPU处理一下速度没准更好。
https://github.com/charlesgriffiths/VoxelPerformance

使用道具 举报

Rank: 13Rank: 13Rank: 13Rank: 13

注册时间
2011-11-15
积分
1301
发表于 2017-3-13 00:03:30 |显示全部楼层
无意中在github找到这么个体素引擎或许你用得上。
跨平台,Opengl,C++
https://github.com/AlwaysGeeky/Vox

使用道具 举报

Rank: 12Rank: 12Rank: 12

注册时间
2012-7-21
积分
733
发表于 2017-3-26 07:21:43 |显示全部楼层
learn3d 发表于 2017-3-13 00:03
无意中在github找到这么个体素引擎或许你用得上。
跨平台,Opengl,C++
https://github.com/AlwaysGeeky/Vo ...

谢谢!我最近发现我的代码在 NVidia 显卡上好像效率非常非常低(相比 MineTest)。理论性能差两倍,但帧数却只有二三十分之一 … 所以怀疑是自己的代码中有些问题,在 Intel 卡上不出问题,在 N 卡上就出问题。
Minetest 用了一个叫 Irrilicht 的框架,不是很清楚怎么渲染的。连 NVidia Graphics Profiler 都不能抓到它的每帧的事件。所以我也想看看别的引擎 ✔

使用道具 举报

Rank: 12Rank: 12Rank: 12

注册时间
2012-7-21
积分
733
发表于 2017-3-28 10:29:12 |显示全部楼层
本帖最后由 quadpixels 于 2017-3-28 10:31 编辑

我现在怀疑是 draw call 过多。因为我是每个 16x16x16 的块分配一个 VBO,然后在画的时候给一个 draw call 的 … 但是看网上的文章,科学的办法应当是将所有的顶点弄成一个大的 VBO。打算试试看。

而有一个现象可以初步验证以上怀疑,那就是以下场景如果将块大小改成 32x32x32,帧率就能从 10 变成 40 (在一台配置低一点的电脑上,是 Intel HD 4000)
而至于为什么低端的 N 卡更慢 …… 可能是因为低端 N 卡对于 draw call 数更敏感?

以及 …… 请无视截图中的 FPS 数(因为目测没有 60),我怀疑我算错了 :(

使用道具 举报

最近看过此主题的会员

您需要登录后才可以回帖 登录 | 注册

‹‹
我的工具栏

关于我们|手机版|Archiver|开源计算机图形学社区(Open Source Computer Graphics Community) | OpenGPU Project | OpenGPU Forum (2007-2013)

GMT+8, 2017-5-27 17:56 , Processed in 0.078804 second(s), 12 queries .

Powered by Discuz! X2

© 2001-2011 Comsenz Inc.

回顶部