[OpenGL] 利用Shader实现复杂地形的渲染
来源:互联网 发布:消息队列java 编辑:程序博客网 时间:2024/06/01 22:52
已经好久没写关于OpenGL的博客了。不过昨天晚上,和我一个可爱的小学弟做了一个娱乐程序,也正好用来写一篇博客。
我们在游戏中经常能见到一座高耸的山,雪线以上是白皑皑的积雪,雪线以下是郁郁葱葱的植被;抑或在某座地狱城探险时,碰见一座被熔岩侵蚀的山谷,在山谷缝隙中是被岩浆烤红的岩石……
虚拟世界中如此多复杂的地表,我们如何使用OpenGL进行渲染呢。使用一张纹理? 一张与地形契合同时又复杂的纹理图案显然难以搜寻。那么PS得到一张纹理?显然又太过麻烦。
那么,这篇博客使用OpenGL可编程管线的灵活性,来解决这个问题,去轻松渲染一个复杂的地形图。还是惯例先贴一下我的结果。
我的程序截图:
一、地形数据的处理
地形数据的来源有很多种方式:Height map、分形、噪声等等。由于这篇博客的重点是纹理的生成,所以这里采用最简单的高度图。上图中地形的三维数据来自如如下的高度图(Height map):
Height map本质是一章灰度图,我们把图片中像素的灰度值作为地形的高度(y),而像素在图片中的位置作为地形的水平坐标(x,y)。然后生成三角网格数据,按照VAO或者VBO进行组织,即可将一章高度图转变为三维地貌。
将地形数据转换三角网格的代码如下:
// VAO:顶点数组和纹理索引数组Vector<vec3> m_vertexs;Vector<vec2> m_textures; void Terrain::loadTerrain(const String &filename, const String &texName){ Image image(filename); m_vertexs.clear(); for(int i=0; i<image.height()-1; i++) // 0 1 2 |0| 3 4 5 |1| { for(int k=0; k<image.width()-1; k++) { // 生成顶点数组, 坐标按照三角网格处理 GL_TRIGANLES vec3 v1(k+0,QColor(image.pixel(k+0,i+0)),i+0); vec3 v2(k+0,QColor(image.pixel(k+0,i+1)),i+1); vec3 v3(k+1,QColor(image.pixel(k+1,i+1)),i+1); vec3 v4(k+1,QColor(image.pixel(k+1,i+0)),i+0); m_vertexs.push_back(v1); m_vertexs.push_back(v2); m_vertexs.push_back(v3); m_vertexs.push_back(v1); m_vertexs.push_back(v3); m_vertexs.push_back(v4); } } // 生成纹理坐标 for(int i=0; i<m_vertexs.size(); i++) { vec2 tmp(m_vertexs[i].x()/image.width(),m_vertexs[i].z()/image.height()); m_textures.push_back(tmp); }}
二、利用Shader实现纹理混合
在熔岩地貌的模拟中(截图2),我们使用了下面的两张纹理再着色器中进行渲染:
为了让熔岩出现在山谷部分,而岩石裸露在高处,同时营造出从上而下的灼烧过渡的效果。我们利用简单的插值就可以达到目标。已知高度图的高度数值范围为[0,255],根据某点的高度y坐标来对两张纹理进行融合:
如果y>140,完全采用岩石纹理;
如果0<y<140,则将两张纹理进行线性插值,越高岩石所占比例越大,越低岩浆所占比例越大。
为了实现这一过程,片段shader的代码如下:
uniform sampler2D texture1;uniform sampler2D texture2;varying vec3 v_position;varying vec2 v_texcoord;void main(){ if(v_position.y >140) { gl_FragColor = texture2D(texture1,v_texcoord); } else { float alpha = v_position.y/(140); // 比例系数 gl_FragColor = texture2D(texture1,v_texcoord)*alpha + texture2D(texture2,v_texcoord)*(1-alpha); }}
借助上述代码,炼狱峡谷渲染的三角网格如下:
三、使用蒙版(mask)实现纹理混合
这里的蒙版,我称之为Mask,就是不知道这样叫标准不标准。对于截图1中山顶的积雪,我们并不想让它和前面的方法一样,只出现在某个高度以上。因为现实中的积雪,虽然整体在雪线以上,但是在雪线以下的周围区域,也有少量沿着山脊的过渡积雪。为了实现这样的效果,我们使用第三张纹理来辅助我们的处理。
我们引入一张黑白纹理(如下图),但是并不将它用来贴图,而是作为一张蒙版,黑色的区域没有积雪,白色的区域将被渲染为积雪区域。
这张纹理,我是直接用PS在前面的高度图上抠出来的,所以积雪区域正好大致分布在山脊处。在我的代码中,我利用蒙版纹理的灰度大小,作为积雪深浅的影响因子,越亮积雪越明显,越浅积雪越薄,这样也能营造出一种自然地过渡效果。
其片段shader的代码如下:
uniform sampler2D texture1;uniform sampler2D texture2;uniform sampler2D mask;varying vec3 v_position;varying vec2 v_texcoord;void main(){ vec4 color; if(v_position.y >140) // 高于140为岩石 { color = texture2D(texture1,v_texcoord); } else // 低于140为草地,但是有过渡 { float alpha = v_position.y/(140); color = vec4(texture2D(texture1,v_texcoord)*(alpha+0.2) + texture2D(texture2,v_texcoord)*(1-alpha)); } // 积雪区域的处理(借助蒙版) float alpha = texture2D(mask,v_texcoord).r; gl_FragColor = color*(1-alpha)+vec4(1.0,1.0,1.0,1.0)*alpha;}
- [OpenGL] 利用Shader实现复杂地形的渲染
- WOW的地形渲染
- 新的地形渲染
- OPenGL/3D渲染/shader
- 地形的构建ogre地形shader 析解
- 地形的构建ogre地形shader 析解
- Unity3D教程:如何利用Shader实现钻石渲染效果
- 地形渲染的动态LOD四叉树算法详细实现
- 地形渲染的动态LOD四叉树算法详细实现
- 地形渲染的动态LOD四叉树算法详细实现
- 地形渲染的动态LOD四叉树算法详细实现
- WOW魔兽的地形渲染
- OpenGL ES渲染之Shader准备
- Cocos2D-X shader(二) OpenGL渲染管线
- HDR渲染器的实现(基于OpenGL)
- HDR渲染器的实现(基于OpenGL)
- 渲染地形
- 地形渲染
- Spring入门篇二(2)
- scanf() 的返回值
- 素数筛选 + 下标当值
- 【python 爬虫】伪造UA字符串
- 5000 的阶乘
- [OpenGL] 利用Shader实现复杂地形的渲染
- javaEE之JSP
- 深度学习常用优化方法
- Codeforces 101206 H & HDU 6006 Engineer Assignment
- LeetCode#513 Find Bottom Left Tree Value题解(C++版)
- Java 构造方法的修饰符
- eclipse与虚拟内存
- http协议
- hdu4578