Cesium Volumn 体渲染
Cesium中的体渲染
上篇介绍了Cesium中的BoxGeometry的本地坐标获取方法,获取了本地坐标后,我们就可以开始做体渲染相关的东西了。将相机坐标也换算到模型本地坐标,即可计算得到以相机为起点的到立方体的射线。体渲染相关的内容参看这篇文章,里面说明了ThreeJS中体渲染的相关内容。
先上图
模型本地坐标
Cesium中的BoxGeometry渲染流程,及模型本地坐标
这次模型使用自定义的primitive
来实现,直接通过Cesium内置的position
来获取本地坐标,不再通过编码后的变量计算。
相机本地坐标
Cesium中内置的变量中提供相机对于模型的本地坐标,czm_encodedCameraPositionMCHigh
,czm_encodedCameraPositionMCLow
,两个为编码后的相机模型坐标。射线起点即为相机的模型坐标
vOrigin=czm_encodedCameraPositionMCHigh+czm_encodedCameraPositionMCLow;
每条射线朝向
有了模型本地坐标和相机本地坐标,即可计算,相机到每个顶点的朝向。
vDirection=position-vOrigin;
体渲染,正式开始
核心思想和在ThreeJS中实现体渲染是一样的,通过射线计算同射线相交的体数据,这里采用ThreeJS官方例子中的体数据生成的方法,柏林噪声的体渲染例子。在ThreeJS中我们可以通过3D纹理的方式直接提交体数据,然后通过采样函数直接采样3D纹理。
但是在Cesium里面,我目前没有发现可以直接使用sampler3D
方法,查看了Texture.js里面的代码发现没有支持构造3D纹理的函数,仅支持2D纹理和纹理数组。
不支持sampler3D
虽然目前不支持使用smapler3D
,但是,看到常量文件里面已经列出了WebGL2的一些内容,应该在不久之后就可以支持直接使用3D纹理了。
粗略看了一下代码,Cesium是支持通过requestWebgl2
来使用glsl 3.0的。其中modernizeShader.js中实现了从glsl 1.0迁移到3.0的方法。这一部分还没看完,后续看完后,试试支持一下3D纹理。先埋个坑,有时间再填。
既然不支持3D纹理,只能将生成的3D纹理通过2D纹理传入GPU,来进行采样。有如下两个方法:
- 使用纹理数组,将体数据以切片方式传入GPU中,如128 * 128 * 128的体数据,构造128张128 * 128 的纹理传入显卡中
- 将体数据存入单张二维纹理中
以上两种方法,第一种方法,很容易就遇到纹理单元不够用的情况,除非数据量非常小,不然没法使用。考虑使用第二种方法,直接构造一张二维纹理,存储所有体数据。
体数据 --> 2D纹理
三维数据存储到二维中,我选择逐各方式存储到纹理中,即逐切片遍历,将每个切片依次逐行存储。如下伪代码。
for z to sizefor y to sizefor z to sizedata[i++]=volumn[x,y,z]
二维纹理的尺寸通过三维数据的个数开根号来计算得到,保证宽高一样,简化计算。
ceil(sqrt(size * size *size))
数据准备好后,只要在shader中实现采样即可在Cesium中实现体渲染。
代理几何体
代理几何体通过自定义primitive
方式实现。自定义primitive
的内容可以参看[这篇文章](Cesium 高性能扩展之DrawCommand(一):入门 - 知乎 (zhihu.com)),文章也介绍了drawcommand
相关的东西,我这里就不在啰嗦了。
这里重点说一下shader中的采样方法。顶点着色器中没什么好说的了,计算射线起点,射线朝向,就没了。重点看片元着色器。
几何体提供的纹理一定不要开mipmap,我们只使用原始的像素值,因为我们将三维数据放到二维纹理中,GPU对纹理多级缓存后,混合后的像素会和我们预期的效果差很多。因此一定要设置Filter
为NEAREST, LINEAR
这两种,也不要提供miplevel
,防止使用mipmap。
FragmentShader中采样体数据
这里简化计算,代理几何体采用的边长为1,且为轴对齐立方体,同样采用AABB,计算射线进入射出的位置。
迭代步长计算,和采样后着色都使用ThreeJS里面的代码不做修改。
射线相交可得到立方体上某一个点的三维坐标,这里需要通过三维坐标从二维纹理中取到对应的体数据。下面代码中``p为射线相交的立方体上某点,
slice_size`为体数据的一个维度的尺寸。
- 第一步先将[0,1]之间的数据放大到同体数据相同的维度,所以这里乘以
slice_size
,尤其要注意**clamp
**,这个方法不能忽略,对于0附近的值,其真实值可能是各负数,导致计算得到的数值不在[0,slice_size) 范围内,对应的就会出现类似z-filghting一样的现象,同一个平面,有些是0,有些是负数,如下类似雪花一样的结果。所以一定要用clamp
将计算后的坐标限制到[0,slice_size-1]范围内。
vec3 p=clamp(floor(pos*slice_size),0.,slice_size-1.);
- 接下来需要计算三维中这个点对应二维纹理中的第几个像素,直接乘一乘就好了。
float idx=p.x+p.y*slice_size+p.z*slice_size*slice_size;
- 知道了是第几个像素,就可以计算这个像素所在的行列,取模就是一行中的第几个,除一下就是第几行,然后再归一化一下,下面
st
即为二维的纹理坐标。
vec2 st=vec2(0.0);
st.s=floor(mod(idx,lxs_tex_size));
st.t=floor(idx / lxs_tex_size);
st/=(lxs_tex_size-1.);
到这里所有的工作基本就做完了,复用ThreeJS中的采样方法,这里还有个坑,WebGL中循环必须是固定次数的,即循环上限必须是一个常量。这里我固定写了500,然后循环中判断射线是否出了立方体,来结束循环。代码中的normal
为ThreeJS中的着色方法。
//循环最多执行500次
for(float i=0.;i<500.0;i+=1.){float d=getData(p+0.5);if(d>0.6){color.rgb=normal(p+0.5)*0.5+(p*1.5+0.25);color.a=1.;break;}p+=rayDir*delta;bounds.x+=delta;if(bounds.x>bounds.y) break; //当射线出立方体时,结束循环,停止采样
}
到此体渲染就介绍完成了。我们会发现,这里渲染结果和ThreeJS中有很大的不同,不够平滑。因为我们直接采样体数据,相当于找最近的体数据来表达一片范围,导致出现明显的马赛克,实际上做一次三线性插值就会好很多了,这里就懒得写了,后续能用sampler3D
后直接用GPU内置的插值就好了。
控制代理几何体维度
上面代码中使用的代理几何体为单位立方体,使用单位立方体可以简化很多计算。实际的体数据长宽高三个维度需要各自指定。
在fs中增加halfdim
uniform变量通过参数方式传入立方体维度即可。
第一步,修改hitBox
中的box_min
和box_max
分别为最大最小维度。
vec3 box_min = vec3( -halfdim );vec3 box_max = vec3( halfdim );
第二步,main
方法中在开始步进计算射线相交的体素时,对立方体相交位置p
加了0.5,用来将坐标计算到大于0。这里要加一半的维度。
float d = getData(p+halfdim);
第三步,在getData
中为了简化取体素的计算,还是将p
归一化。
vec3 pos = pox_lxs/(halfdim*2.);
最后,在自定义primitive
中的uniformmap
中增加对应的halfdim
即可。
代码库
代码库地址
Cesium Volumn 体渲染相关推荐
- Cesium中使用Sampler3D,3D纹理,实现体渲染
Cesium中使用Sampler3D,3D纹理,实现体渲染 Cesium目前(20221231)还不支持直接使用3D纹理,但是其实内部已经可以WebGL2,而且内置常量也有3DTexture.所以,可 ...
- Cesium体渲染,去除Volume中的马赛克
Cesium中体渲染,去除Volume中的马赛克 产生马赛克的原因是所有数据都是真实数据,未对采样结果进行插值处理,上一篇文中采用的是Nearest,所有采样结果都是基于真实数据的,即在不同位置处进行 ...
- Cesium中实现体渲染
体渲染 Volume Rendering 传统意义上我们构建模型都是通过构建物体的外表面去实现的,例如通过三角面构建模型,或者通过方程的形式构建隐式的表面模型. 而体渲染则是通过 3d 数据集渲染物体 ...
- 斯图加特大学GPU光线投射体渲染技术提携
斯图加特大学GPU光线投射体渲染技术介绍 前言:在以往人们的印象中,美国的CG技术是一世界第一流的,而没有注意德国CG技术的发展.事实上,德国大学的CG是相当高的,与美国第一流的大学学术交往非常频繁. ...
- GPU Gems1 - 9 有效的阴影体渲染
这章全面讲述了用于实时阴影渲染中常见两种流派之一的阴影体(Shadow Volumes)技术,又称模板阴影(Stencil Shadows)技术,重点是得到正确的角度的情形,减少几何图形和填充率的消耗 ...
- [译]基于GPU的体渲染高级技术之raycasting算法
[译]基于GPU的体渲染高级技术之raycasting算法 PS:我决定翻译一下<Advanced Illumination Techniques for GPU-Based Volume Ra ...
- Q128:PBRT-V3,“体渲染”积分器的“传播方程”(15.1章节)
对比"路径追踪"积分器和"体渲染"积分器中长度为n的路径上返回的光的计算:
- Q127:PBRT-V3,理解“体渲染”积分器的关键竟然是这张图
"体渲染"积分器是在"路径追踪"积分器的基础上考虑了场景中的介质,相当于是对"路径追踪"积分器的拓展. 所以,在学习"体渲染&qu ...
- Q126:PBRT-V3,VolPathIntegrator(体渲染)流程概述
最近在学体渲染,老实说,挺费劲的! Medium的原理.采样.对应的光传播函数.VolPathIntegrator的实现等等,都已经过了一遍. 现总结一下初浅的理解吧. VolPathIntegrat ...
最新文章
- Windows异常学习笔记(四)—— 编译器扩展SEH
- Python自动化运维——系统性能信息模块
- Matlab将一矩阵中等于某个值的元素全部替换成另一个值
- 全局路径规划:图搜索算法介绍2(A star)
- VIM使用小技巧-重新载入文件
- oracle控制文件加载数据,关于SQLLOAD控制文件参数的问题
- 下一代Jquery模板-----JsRender
- 419.甲板上的战舰
- 静态路由协议的默认管理距离是_动态路由选择原理(距离矢量路由协议RIP)
- 精细加工领域中超快激光的应用
- DB2 执行SQL报错: DB2 SQL Error: SQLCODE=-1585, SQLSTATE=54048
- 7-2 Rank a Linked List (25 分)
- Tibco Designer -- 构建EAR文件
- python+selenium实战搭建PO模型
- Kyndryl从IBM完全剥离在纽交所独立上市;新思科技收购AI驱动性能优化软件企业Concertio | 全球TMT...
- photoshop制作白发教程:可爱女孩黑发变白发
- sqlserver审计 —— 服务器与数据库审核规范
- 项目 --- 《水晶报表》
- C专家编程 第6章 运动的诗章:运行时数据结构 6.1 a.out及其传说
- 5位评委对参赛选手进行打分,将所有的打分结果存储到对应类型的数组中, 将所有的评分结果去除一个最低分,去除一个最高分,然后获取的平均分数为 选手的最终得分.设计程序,用键盘输入5位评委的评分,并打印输
热门文章
- 合肥工业大学本科毕业论文答辩和论文选题PPT模板
- 浙江大华软件测试面试经历
- 大学计算机基础及应用教程答案,《大学计算机基础教程》课后习题六答案(新)...
- 阿里巴巴Java开发手册(详尽版)-个人未注意到的知识点
- FreeBSD12.1软件包管理工具ports常见用法
- 2013电大c语言程序设计,2013年一月电大考试C语言程序设计(A)
- 在线cad版本转换_弱电机房工程设计图纸(CAD版本)
- W3school离线手册2019资源下载
- Android手机应用开发实战(一) | 展示王者荣耀英雄信息的APP
- 遥感计算机分类的基本原理,遥感数字图像计算机解译