在上文中,讲述了PyOpenGL的基本配置,以及网格,球形的生成,以及基本的漫游。现在利用上一篇的内容,来利用高程图实现一个基本的地形,并且,利用上文中的第三人称漫游,以小球为视角,来在地形上前后左右漫游,能实时检测高度。下面先看下效果图:

  二张图,球分别在不同的地方,不同的显示模型,一个是全填充的,一个是线连,可以从中看到一些基本的思路。大致过程分别如下,首先拿到一张高度图,检索其中的高度对应的通道的值,然后用来改变网格的高度。这个过程只需要在初始化时生成就行了,所以我们可以简单的用CPU来完成这个。然后是球体的漫游,这部分在上文中已经讲了第一与第三人称漫游,用的就是其中的第三人称作漫游,在这里,我们主要是要检索球下面的地形的高度,因为需要实时计算,这部分用GPU来完成。

  我们先看下,根据高度图来改变网格高度的相关代码(请对照前文中的网格类Plane来看,下面的setHeight为其中的一个方法):

     def setHeight(this,image):
ix = image.size[]
iy = image.size[]
this.heightImage = image
print ix,iy
#print "xr,yr",this.xr,this.yr
lerp = lambda a,b,d:a * d + b * (1.0 - d)
fade = lambda t : t*t*(3.0-2.0*t) #t*t*t*(t*(t*6.0-15.0)+10.0)
for y in range(this.yr):
for x in range(this.xr):
index = * (this.xr * y + x) +
#print index
fx = float(x) / float(this.xr - ) * float(ix - )
fy = float(y) / float(this.yr - ) * float(iy - )
#print float(x) / float(this.xr - ),fx,float(y) / float(this.yr - ),fy
xl,xr,yu,yd = int(math.floor(fx)),int(math.ceil(fx)),int(math.floor(fy)),int(math.ceil(fy))
dx,dy = fade(fx - xl),fade(fy - yu)
#print "loc:",xl,xr,yu,yd,dx,dy
#left up,right up,left down,right down
lu,ru,ld,rd = image.im[ix * yu + xl],image.im[ix * yu + xr],image.im[ix * yd + xl],image.im[ix * yd + xr]
#print ix * yu + xl,lu,ru,ld,rd
hight = lerp(lerp(lu,ru,dx),lerp(ld,rd,dx),dy)
this.data[index] = hight / 255.0
#print "setHeight:",hight / 255.0

检索高度图

  当初完成这段代码后,我有时后悔在前面学习noise时,没有自己先完成一个根据高度图生成地形,不然理解柏林噪声函数会是一件非常简单的事,这段代码很简单,得到image的信息,然后把原来的地形网格他们二个做一个映射关系,就好像二个大小不同的矩阵,根据其中一个在本矩阵里的位置,找出在另一个矩阵中对应的位置。首先index得到的是当初位置网络数据里的高度索引,第前面文章中,这个值是0,然后根据线性关系,就如上面后说,找到当前位置对应在image的位置,fx,fy.为什么我说很后悔先看了noise,大家可以看下,这里的代码的逻辑和noise里的就是一样,但是这里我可以自己推出来,而看noise里的过程花费太多不必要的时间。在这里,我们可以想象的到,fx,fy是整数的机会不大,那么无论取floor(fx)或ceil(fx),都有较大的偏差,正确的方法应该是根据fx的小数位来做floor(fx)或ceil(fx)权重进行计算。简单来说,就是根据fx,fy来取在它周围的四个像素点,然后根据他们的小数位来对四个小数点进行混合计算。其中小数部分需要的fade可以用lerp,也可以用二阶平滑来至三阶平滑的映射关系,而像素值根据简单线性关系求就可以了。理解这里的以后,再去看柏林噪声实践(一) 海波等就容易理解多了。

  这个是初始化地形的高度值,下面就是重点,如果根据球所在的位置,来得到当前位置里的高度,然后用来设定球的高度(简化问题,只求球心下的高度,二片卡住球的问题就没考虑)。有了前面的基础,在CPU中进行得到高度值也很容易,但是现在是漫游过程中,当前位置每时都在计算,CPU应该用来进行更复杂的逻辑运算,这部分交给GPU了,先给出相关着色器代码:

 update_v = """
//#version 330 compatibility
#version
uniform sampler2D tex0;
uniform float xw;
uniform float yw;
uniform float height;
//the location of center of the sphere
uniform vec2 xz;
uniform float sphereRadius;
uniform mat4 mMatrix;
uniform mat4 vMatrix;
uniform mat4 pMatrix;
out vec4 o_color;
void main() {
vec4 pos = vec4(gl_Vertex);
vec2 uv = vec2(xz/vec2(xw,yw) + vec2(0.5,0.5));
uv.y = 1.0 - uv.y;
vec3 rgb = texture2D(tex0, uv).rgb;
pos.y = pos.y + sphereRadius + rgb.r;//height;//
o_color = vec4(uv.x, uv.y, rgb.r, );
gl_Position = pMatrix * vMatrix * mMatrix * pos; //vec4 v = vec4(gl_Vertex);
//vec2 uv = vec2(xz/vec2(xw,yw) + vec2(0.5,0.5)).xy;
//uv.y = 1.0 - uv.y;
//v.x = v.x + xz.x;
//v.z = v.z + xz.y;
//v.y = v.y + texture2D(tex0, uv).r+ sphereRadius;
//o_color = vec4(uv.x,uv.y, 0, 1 );
//gl_Position = gl_ModelViewProjectionMatrix * v;
}""" update_f = """
//#version 330 compatibility
#version
in vec4 o_color;
void main() {
//vec4 color = texture2D(tex1, gl_TexCoord[0].st);
gl_FragColor = o_color;// vec4( 0, 1, 0, 1 );
}"""

着色器检索高度

  这段代码大致思想和前面一样,不过纹理坐标需要映射到0-1之前,不过,也少了混合周围定点的计算,因为我们在设定纹理时(glTexParameterf),已经告诉着色器,自动线性混合了。在这里,需要说明的是,因为130后,已经废弃了固定管线的相关功能与API,虽然还能用,但是毕竟要向前看,所以主体的部分有二部分,一部分是用的是着色器版本120固定管线提供的gl_ModelViewProjectionMatrix,以及120后的,自己提供MVP,也顺便练习下如何在PyOpenGL里进行矩阵的基本操作。其中height是CPU计算的高度,这个就放附件里,不拿出来说了,至于为什么还拿出一个CPU版,前面不是说了不交给CPU吗,主要是我发现,有些旧的显卡对于这段逻辑处理还是有些问题(有一问题浪费大量时间才发现是旧显卡的问题),代码就不放了,大家有兴趣可以去附件里看。

  嗯,还有必要说下绘制部分,地形网络用到一个颜色纹理,而球需要用到高度纹理,在glsl中,多纹理,多着色器如何设定以及完成。

 class allshader:
def __init__(this):
this.planeProgram = shaders.compileProgram(shaders.compileShader(plane_v, GL_VERTEX_SHADER),
shaders.compileShader(plane_f, GL_FRAGMENT_SHADER))
#the parameter tex0 must be use in shaders,otherwise the
#glGetUniformLocation get -1
this.planeProgram.tex0 = glGetUniformLocation(this.planeProgram,"tex0")
this.planeProgram.tex1 = glGetUniformLocation(this.planeProgram,"tex1")
print this.planeProgram.tex0,this.planeProgram.tex1 this.updateProgram = shaders.compileProgram(shaders.compileShader(update_v, GL_VERTEX_SHADER),
shaders.compileShader(update_f, GL_FRAGMENT_SHADER))
this.updateProgram.xl = glGetUniformLocation(this.updateProgram,"xw")
this.updateProgram.yl = glGetUniformLocation(this.updateProgram,"yw")
this.updateProgram.height = glGetUniformLocation(this.updateProgram,"height")
this.updateProgram.sphereRadius = glGetUniformLocation(this.updateProgram,"sphereRadius")
this.updateProgram.tex0 = glGetUniformLocation(this.updateProgram,"tex0")
this.updateProgram.xz = glGetUniformLocation(this.updateProgram,"xz")
this.updateProgram.hight = glGetUniformLocation(this.updateProgram,"hight")
this.updateProgram.mMatrix = glGetUniformLocation(this.updateProgram,"mMatrix")
this.updateProgram.vMatrix = glGetUniformLocation(this.updateProgram,"vMatrix")
this.updateProgram.pMatrix = glGetUniformLocation(this.updateProgram,"pMatrix") def DrawGLScene():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_MODELVIEW)
camera.setLookat()
#texture set
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, colorMap)
glActiveTexture(GL_TEXTURE1)
glBindTexture(GL_TEXTURE_2D, hightMap)
#plane
glUseProgram(shaderall.planeProgram)
glUniform1i(shaderall.planeProgram.tex0, 0)
plane.draw()
glUseProgram(0)
#sphare
eyeLoc = camera.origin
uv = eyeLoc[0] / plane.xl + 0.5,eyeLoc[2] / plane.yl + 0.5
glUseProgram(shaderall.updateProgram)
glUniform1f(shaderall.updateProgram.xl, plane.xl)
glUniform1f(shaderall.updateProgram.yl, plane.yl)
#CPU compute height
#glUniform1f(shaderall.updateProgram.height, plane.getHeight(eyeLoc[0],eyeLoc[2]))
glUniform1f(shaderall.updateProgram.sphereRadius, sph.radius)
glUniform1i(shaderall.updateProgram.tex0, 1)
#print uv
glUniform2f(shaderall.updateProgram.xz,eyeLoc[0],eyeLoc[2])
#print "eye:",eyeLoc,eyeLoc[0],eyeLoc[2]
getMVP(eyeLoc)
sph.draw()
glUseProgram(0)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, 0)
glDisable(GL_TEXTURE_2D)
glActiveTexture(GL_TEXTURE1)
glBindTexture(GL_TEXTURE_2D, 0)
glDisable(GL_TEXTURE_2D) glBegin(GL_LINES)
glColor(1.0,0.0,0.0)
glVertex3f(-plane.xl / 2.0, 1.0, -plane.yl / 2.0)
glVertex3f(100.0, 1.0, -plane.yl / 2.0)
glColor(0.0,1.0,0.0)
glVertex3f(-plane.xl / 2.0, 1.0, -plane.yl / 2.0)
glVertex3f(-plane.xl / 2.0, 1.0, 100.0) glColor(1.0,0.0,0.0)
glVertex3f(0.0, 0.0,0.0)
glVertex3f(100.0, 0.0, 0.0)
glColor(0.0,1.0,0.0)
glVertex3f(0.0, 0.0, 0.0)
glVertex3f(0.0, 1.0, 100.0)
glEnd() glutSwapBuffers()

glsl多纹理,多着色器

  嗯,python好像对中文注解支持不友好,故采用我的鬼哭神嚎的英语,大家就不要笑了。说一下,后面给出二个坐标系,用来确定当家位置的,一个是左下角,一个是中心,分别向X,Z正轴发射出去。

  下面这段是设定球MVP的代码:

 def getMVP(eye):
v = ny.array(glGetFloatv(GL_MODELVIEW_MATRIX), ny.float32)
p = ny.array(glGetFloatv(GL_PROJECTION_MATRIX), ny.float32)
m = ny.array([[1, 0, 0, 0],[0, 1, 0, 0], [0, 0, 1, 0],[eye[0],0,eye[2],1]],ny.float32)
#print m
glUniformMatrix4fv(shaderall.updateProgram.pMatrix,1,GL_FALSE,p)
glUniformMatrix4fv(shaderall.updateProgram.vMatrix,1,GL_FALSE,v)
glUniformMatrix4fv(shaderall.updateProgram.mMatrix,1,GL_FALSE,m)
#glgeffloat

球MVP

  附件:Python地形.zip 和上方的漫游模式一样,其中EDSF前后左右移动,WR分别向上与向下,鼠标右键加移动鼠标控制方向,V切换第一人称与第三人称。UP与DOWN切换前面操作的移动幅度。在第三人称下,因为球中着色器限定了Y轴,故那时模式看起来如2.5D的那种游戏视角,能左右转动视角,不能看到天,不知2.5D游戏里的那种是不是也是这样被限制住了。

初试PyOpenGL二 (Python+OpenGL)基本地形生成与高度检测的更多相关文章

  1. 初试PyOpenGL四 (Python+OpenGL)GPU粒子系统与基本碰撞

    这篇相当于是对前三篇的总结,基本效果如下: 在初试PyOpenGL一 (Python+OpenGL)讲解Pyopengl环境搭建,网格,球体,第一与第三人称摄像机的实现.在初试PyOpenGL二 (P ...

  2. 初试PyOpenGL一 (Python+OpenGL)

    很早就一直想学Python,看到一些书都有介绍,不管是做为游戏的脚本语言,还是做为开发项目的主要语言都有提及(最主要的CUDA都开始支持Python,CUDA后面一定要学),做为先熟悉一下Python ...

  3. 初试PyOpenGL三 (Python+OpenGL)GPGPU基本运算与乒乓技术

    这篇GPGPU 概念1: 数组= 纹理 - 文档文章提出的数组与纹理相等让人打开新的眼界与思维,本文在这文基础上,尝试把这部分思想拿来用在VBO粒子系统上. 在前面的文章中,我们把CPU的数据传到GP ...

  4. (Python OpenGL)【5】平移 PyOpenGL

    (Python OpenGL) 原文:http://ogldev.atspace.co.uk/www/tutorial06/tutorial06.html  (英文) 下面是我翻译过来的: 背景 在本 ...

  5. 使用Python的库qrcode生成二维码

    现在有很多二维码的生成工具,在线的,或者安装的软件,都可以进行生成二维码.今天我用Python的qrcode库生成二维码.需要预先安装  Image 库 安装 用pip安装 # pip install ...

  6. python小工具myqr生成动态二维码

    python小工具myqr生成动态二维码 (一)安装 (二)使用 (一)安装 命令: pip install myqr 安装完成后,就可以在命令行中输入 myqr 查看下使用帮助: myqr --he ...

  7. 【Python OpenGL】【2】第一个三角形(Pyopengl)

    根据顶点缓存来生成图元(Python OpenGL) 原文(英文链接)http://ogldev.atspace.co.uk/www/tutorial03/tutorial03.html __auth ...

  8. (Python OpenGL)【4】Uniform变量 PyOpenGL

    (Python OpenGL) 原文:http://ogldev.atspace.co.uk/www/tutorial05/tutorial05.html(英文) __author__ = " ...

  9. (Python OpenGL)【3】着色器 PyOpenGL

    (Python OpenGL)现在开始我们使用着色器来进行渲染.着色器是目前做3D图形最流行的方式. OpenGL的渲染管线流程: 数据传输到OpenGL—>顶点处理器—>细分着色—> ...

随机推荐

  1. pc客户端网页录音和压缩

    web录音的功能,也就是怎么使用 getUserMedia 音频上传 栗子中最后返回的是Blob数据 return new Blob([dataview], { type: type }) 因为对ht ...

  2. 压力测试工具ab及centos下单独安装方法 nginx和tomcat静态资源的性能测试

    Apache安装包中自带的压力测试工具Apache Benchmark(简称ab)简单易用,这里采用ab作为压国测试工具. 独立安装: ab运行需要信赖apr-util包: # yum install ...

  3. 未能为数据库 '*'中得对象'*'分配空间,因文件组'PRIMARY'已满

    服务器使用mssqlserver2005,最近经常出现无法新增信息错误,查看日志,发现严重错误提示,内容大致为: 无法为数据库 'weixin_main' 中的对象 'dbo.wx_logs'.'PK ...

  4. 每日英语:Nanjing's New Sifang Art Museum Illustrates China's Cultural Boom

    In a forest on the outskirts of this former Chinese capital, 58-year-old real-estate developer Lu Ju ...

  5. cc(self)是什么意思?

    分别找到这2句代码:   复制代码 cc(self):addComponent("components.behavior.EventProtocol"):exportMethods ...

  6. 【驱动】USB驱动实例·串口驱动·键盘驱动

    Preface   USB体系支持多种类型的设备. 在 Linux内核,所有的USB设备都使用 usb_driver结构描述.    对于不同类型的 USB设备,内核使用传统的设备驱动模型建立设备驱动 ...

  7. LeetCode: Largest Rectangle in Histogram 解题报告

    Largest Rectangle in Histogram Given n non-negative integers representing the histogram's bar height ...

  8. JavaScript高级 面向对象(12)--引用类型值类型作为参数传递的特性

    说明(2017-4-2 18:27:11): 1. 作为函数的参数,就是将函数的数据拷贝一份,传递给函数的定义中的参数. 函数foo()在调用的时候,做了两件事: (1)函数在调用的时候,首先需要将参 ...

  9. 轻量级Modal模态框插件cta.js

    今天给大家分享一款轻量级Modal模态框插件cta.js.这是一款无需使用jQuery插件,纯js编写的模态框弹出特效.效果图如下: 在线预览   源码下载 实现的代码. html代码: <se ...

  10. NLP实现文本分词+在线词云实现工具

    实现文本分词+在线词云实现工具 词云是NLP中比较简单而且效果较好的一种表达方式,说到可视化,R语言当仍不让,可见R语言︱文本挖掘——词云wordcloud2包 当然用代码写词云还是比较费劲的,网上也 ...