CN104867175A - 一种虚拟效果图实景展示装置及其实现方法 - Google Patents

一种虚拟效果图实景展示装置及其实现方法 Download PDF

Info

Publication number
CN104867175A
CN104867175A CN201510293242.9A CN201510293242A CN104867175A CN 104867175 A CN104867175 A CN 104867175A CN 201510293242 A CN201510293242 A CN 201510293242A CN 104867175 A CN104867175 A CN 104867175A
Authority
CN
China
Prior art keywords
float
gles20
add
texture
math
Prior art date
Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
Pending
Application number
CN201510293242.9A
Other languages
English (en)
Inventor
孟君乐
Current Assignee (The listed assignees may be inaccurate. Google has not performed a legal analysis and makes no representation or warranty as to the accuracy of the list.)
Individual
Original Assignee
Individual
Priority date (The priority date is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the date listed.)
Filing date
Publication date
Application filed by Individual filed Critical Individual
Priority to CN201510293242.9A priority Critical patent/CN104867175A/zh
Publication of CN104867175A publication Critical patent/CN104867175A/zh
Pending legal-status Critical Current

Links

Landscapes

  • Image Generation (AREA)

Abstract

一种虚拟效果图实景展示装置,包括有支撑镜架、目镜透镜、显示屏、陀螺仪、计算单元、加速度传感器和加速度传感器。所说的支撑镜架为主体支撑部件,其一侧设置有目镜透镜,所述目镜透镜对应的另一侧,支撑镜架的内部壳体内设置有显示屏,所述显示屏与计算单元电路连接,所述计算单元电路连接有陀螺仪和加速度传感器,本发明所述的一种虚拟效果图实景展示装置,采用“目镜透镜+显示屏”的成像方式,将小型二维显示器所产生的影像藉由光学系统放大。小型显示器所发射的光线经过凸状透镜使影像因折射产生类似远方效果。利用此效果将近处物体放大至远处观赏而达到虚拟现实的效果。

Description

一种虚拟效果图实景展示装置及其实现方法
技术领域
本发明涉及装修效果图展示装置及其实现方法,尤其涉及一种虚拟效果图实景展示装置及其实现方法。
背景技术
目前,各种装修效果图,管线图,也包括结构图,多以平面图片的形式进行展示,但展示的只是部分位置或者部分角度内容,并不能直观全面的展示效果。为了解决内容展示不全面的问题,出现了全景图效果图,但是它仅仅解决了只能展示部分角度的问题,可以多角度的展示效果。但是全景图使用并不直观,还只是平面的展示。目前的解决办法主要是出多张多个角度的图片,但是也只能提供有限角度的内容。而且看图片效果并不直观,也还是平面展示,不能使人有身临其境的感觉。
发明内容
本发明的目的在于,克服现有技术的不足之处,提供一种展示效果图的方法,使用虚拟现实技术显示效果图,根据人方位视角的变化立即进行复杂的运算,将精确的三维世界视频传回,产生临场感,让用户如同身临其境一般,可以及时、没有限制地观察三维空间内的事物。
本发明所述的一种虚拟效果图实景展示装置及其实现方法,从去年 google 提供了开放其 Cardboard 虚拟眼睛解决方案之后,虚拟现实眼镜已经开始大面积推广,本发明依托于其解决方案来实现真实直观的体验效果图。本发明所述的一种虚拟效果图实景展示装置包括有支撑镜架、目镜透镜、显示屏、陀螺仪、计算单元和加速度传感器。所说的支撑镜架为本发明所述的一种虚拟效果图实景展示装置的主体支撑部件,支撑镜架的整体形状类似于望远镜,一侧设置有目镜透镜,所述目镜透镜对应的另一侧,支撑镜架的内部壳体内设置有显示屏,所述显示屏与计算单元电路连接,所述计算单元可以是CPU、GPU或专用芯片;所述计算单元电路连接有陀螺仪和加速度传感器,所述陀螺仪用来获得设备的角度信息,所述加速度传感器可以获得重力的方向,使得设备可以获得上下方位信息来确定效果图的上下方位,所说的加速度传感器也可以采用重力感应传感器替代。
所述显示屏分为左眼区与右眼区,可以有两个显示屏或单个屏幕分成两个部分,分别显示两眼看到的内容。然后通过目镜透镜镜片将内容放大后显示到人眼里面。
所述计算单元根据多种传感器获得方位视角的变化立即进行复杂的运算,将精确的三维世界视频发送到显示屏。
所述支撑镜架的左右两侧分别设置有绑带,便于将该装置固定到头部观看。
本发明所述的一种虚拟效果图实景展示装置,采用“目镜透镜+显示屏”的成像方式,将小型二维显示器所产生的影像藉由光学系统放大。小型显示器所发射的光线经过凸状透镜使影像因折射产生类似远方效果。利用此效果将近处物体放大至远处观赏而达到虚拟现实的效果。
附图说明
图1为本发明所述的一种虚拟效果图实景展示装置的结构示意图,图2为发明所述的球体三角形的顶点坐标及纹理坐标图;1-支撑镜架  2-显示屏  3-目镜透镜  4-绑带。
具体实施方式
现参照附图1和附图2,结合实施例具体说明如下:本发明所述的一种虚拟效果图实景展示装置包括有支撑镜架1、目镜透镜3、显示屏2、陀螺仪、计算单元和加速度传感器。所说的支撑镜架1为本发明所述的一种虚拟效果图实景展示装置的主体支撑部件,支撑镜架1的整体形状类似于望远镜,一侧设置有目镜透镜3,所述目镜透镜3对应的另一侧,支撑镜架的内部壳体内设置有显示屏2,所述显示屏2与计算单元电路连接,所述计算单元可以是CPU、GPU或专用芯片;所述计算单元电路连接有陀螺仪和加速度传感器,所述陀螺仪用来获得设备的角度信息,所述加速度传感器可以获得重力的方向,使得设备可以获得上下方位信息来确定效果图的上下方位。
所述显示屏分为左眼区与右眼区,可以有两个显示屏或单个屏幕分成两个部分,分别显示两眼看到的内容。然后通过目镜透镜镜片将内容放大后显示到人眼里面。
所述计算单元根据多种传感器获得方位视角的变化立即进行复杂的运算,将精确的三维世界视频发送到显示屏。
所述支撑镜架1的左右两侧分别设置有绑带4,便于将该装置固定到头部观看。
本发明所述的一种虚拟效果图实景展示装置,采用“目镜透镜+显示屏”的成像方式,将小型二维显示器所产生的影像藉由光学系统放大。小型显示器所发射的光线经过凸状透镜使影像因折射产生类似远方效果。利用此效果将近处物体放大至远处观赏而达到虚拟现实的效果。
本发明所述的一种虚拟效果图实景展示装置的实现方法具体分为两个步骤:一、预先处理效果图;二、实际显示效果图。
一、预先处理效果图
一般情况AMD A10 CPU 渲染一张3DMax的效果图需要半小时到2个小时,而虚拟现实需要实时的生成图像,这里就会出现矛盾。计算机视频为了不让人感到卡顿,一般需要60帧/秒,最少也要达到30帧/秒。虽然通过集群可以提高渲染性能,但是延时也无法降低到0.03秒,所以完全实时渲染几年内是很难实现的。
为了做到实时显示,需要尽量减少实时显示时的计算量。图像渲染主要涉及到顶点变换、光照计算及图元装配等内容。光照计算可以预先计算合并到纹理贴图里面来减少实时显示时的计算量。顶点变换通过减少精细度可以减少一部分顶点,极端情况下可以放弃效果图的原始形状来减少顶点。
1、优化光照计算
这里以 3dMax 源文件为例子,说明预先渲染的方法。为了和 3dMax渲染出来的普通效果图光照效果一致,使用 3dMax自带的“渲染到纹理”来做光照计算,具体步骤为:1.使用3dMax打开效果图。2.点击“渲染”、“渲染到纹理”菜单,打开渲染到纹理对话框。3.选择需要处理的对象。4.增加渲染输出,并设置目标贴图位置为漫反射,自动贴图大小,输出到源,保留烘培材质等设置之后点击渲染即可。通过以上方法可以预先计算光照等信息并保存到纹理贴图,可以减少很多实时显示的计算量。(预先进行光照计算并不太适合镜面反射,可以实际显示时专门处理)。
2.优化几何体
可以通过“优化”修改器来可以减少对象中面和顶点的数目,简化几何体和加速渲染的同时仍然保留可接受的图像,简单优化具体操作为:1.选定对象。2.打开修改面板。3.点击修改器列表选择“优化”来增加“优化”修改器即可减少顶点、面的数量。
但是对于顶点、面太多的效果图即使优化后也还是太复杂了,这时为了降低实时显示的计算量,可以使用使用天空盒的办法来显示效果图。即预先选定视点生成球形全景图,实时显示时不在使用效果图原本的顶点、面等信息,而是直接通过画一个球体(近似),视点放到球心,球内面贴经过预渲染出的球形全景图作为纹理贴图。
具体做法是:
(一)、生成全景图:(1).打开原效果图。(2).选定一视图,并设置摄像机位置为需要展示的位置。(3).打开渲染设置对话框,将输出图像大小设置为宽200高100,即2:1的比例。修改v-ray 摄像机类型为球形,覆盖视野为360。如果有剪切的话需要去掉。然后执行渲染即可生成球形全景图。
(二)、建立新效果图:(1).创建“球体”。(2).为球体新建材质,并指定漫反射贴图为(一)生成的全景图。(3).设置摄像机位置为球心。
这种办法对计算单元的性能要求很低,缺陷是视角360度旋转没问题,但是视点被固定在了渲染全景图的摄像机位置,想要多个视点只能在预处理时渲染多个摄像机位置的多张全景图做为不同视点的纹理贴图。
    第(二)步的目的是为了通过3dmax生成组成球体三角形的顶点坐标及纹理坐标,实际上顶点坐标及纹理坐标可以自己计算出来。任意球面上的点,三维坐标 (x0, y0, z0) 计算:(R为球半径)
x0 = R * cos(a) * sin(b);
y0 = R * sin(a);
z0 = R * cos(a) * cos(b);
纹理坐标就很简单了,如图2所示,各个顶点的纹理坐标直接(1/份数)*所在行或列数即可得出。
具体代码为:
    public void initVertexData(float r){
        final float UNIT_SIZE=2.5f;
        ArrayList<Float> alVertix=new ArrayList<Float>();
        final float angleSpan=10f;
        for(float vAngle=90;vAngle>-90;vAngle=vAngle-angleSpan){
            for(float hAngle=360;hAngle>0;hAngle=hAngle-angleSpan){
                double xozLength=r*UNIT_SIZE*Math.cos(Math.toRadians(vAngle));
                float x1=(float)(xozLength*Math.cos(Math.toRadians(hAngle)));
                float z1=(float)(xozLength*Math.sin(Math.toRadians(hAngle)));
                float y1=(float)(r*UNIT_SIZE*Math.sin(Math.toRadians(vAngle)));
xozLength=r*UNIT_SIZE*Math.cos(Math.toRadians(vAngle-angleSpan));
                float x2=(float)(xozLength*Math.cos(Math.toRadians(hAngle)));
                float z2=(float)(xozLength*Math.sin(Math.toRadians(hAngle)));
                float y2=(float)(r*UNIT_SIZE*Math.sin(Math.toRadians(vAngle-angleSpan)));
xozLength=r*UNIT_SIZE*Math.cos(Math.toRadians(vAngle-angleSpan));
                float x3=(float)(xozLength*Math.cos(Math.toRadians(hAngle-angleSpan)));
                float z3=(float)(xozLength*Math.sin(Math.toRadians(hAngle-angleSpan)));
                float y3=(float)(r*UNIT_SIZE*Math.sin(Math.toRadians(vAngle-angleSpan)));
xozLength=r*UNIT_SIZE*Math.cos(Math.toRadians(vAngle));
                float x4=(float)(xozLength*Math.cos(Math.toRadians(hAngle-angleSpan)));
                float z4=(float)(xozLength*Math.sin(Math.toRadians(hAngle-angleSpan)));
                float y4=(float)(r*UNIT_SIZE*Math.sin(Math.toRadians(vAngle)));
                alVertix.add(x1);alVertix.add(y1);alVertix.add(z1);
                alVertix.add(x2);alVertix.add(y2);alVertix.add(z2);
                alVertix.add(x4);alVertix.add(y4);alVertix.add(z4);
                alVertix.add(x4);alVertix.add(y4);alVertix.add(z4);
                alVertix.add(x2);alVertix.add(y2);alVertix.add(z2);
                alVertix.add(x3);alVertix.add(y3);alVertix.add(z3);
            }}
        int vCount=alVertix.size()/3;
        vertexCount = vCount;
        float vertices[]=new float[vCount*3];
        for(int i=0;i<alVertix.size();i++){
            vertices[i]=alVertix.get(i);
        }
        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
        vbb.order(ByteOrder.nativeOrder());
        vertexBuffer = vbb.asFloatBuffer();
        vertexBuffer.put(vertices);
        vertexBuffer.position(0);
        float[] texCoor=generateTexCoor(
                (int)(360/angleSpan),
                (int)(180/angleSpan)
        );
        ByteBuffer llbb = ByteBuffer.allocateDirect(texCoor.length*4);
        llbb.order(ByteOrder.nativeOrder());
        mTexCoorBuffer=llbb.asFloatBuffer();
        mTexCoorBuffer.put(texCoor);
        mTexCoorBuffer.position(0);
    }
    //自动切分纹理产生纹理数组的方法
    public float[] generateTexCoor(int bw,int bh){
        float[] result=new float[bw*bh*6*2];
        float sizew=1.0f/bw;//列数
        float sizeh=1.0f/bh;//行数
        int c=0;
        for(int i=0;i<bh;i++){
            for(int j=0;j<bw;j++){
                //每行列一个矩形,由两个三角形构成,共六个点,12个纹理坐标
                float s=j*sizew;
                float t=i*sizeh;
                result[c++]=s;
                result[c++]=t;
                result[c++]=s;
                result[c++]=t+sizeh;
                result[c++]=s+sizew;
                result[c++]=t;
                result[c++]=s+sizew;
                result[c++]=t;
                result[c++]=s;
                result[c++]=t+sizeh;
                result[c++]=s+sizew;
                result[c++]=t+sizeh;
            }}
        return result;
    }
    3.统一格式
由于效果图有不少格式(3dmax、rhino、solidworks、pro/e等),为了后期方便统一显示,预先将其转换成 obj 格式的方便统一处理。Obj是由Alias|Wavefront公司为3D建模和动画软件"Advanced Visualizer"开发的一种标准,纯文本文件,方便编程处理。3dmax可以通过导出命令直接导出obj格式的文件。需要注意导出时需要设置面为三角形(opengl es 只支持点、线、三角形面,不支持四边形),同时将纹理坐标、材质等一起导出,材质会导出到同名的mtl文件。
二、实际显示效果图
为了将精力集中在主要功能上,这里我们选择使用android平台的供的 Cardboard SDK 来实现虚拟现实效果图功能。Cardboard SDK 实现了大部分虚拟现实相关的功能,包括镜头扭曲修正、模拟暗角、头部跟踪等功能。使用 Cardboard SDK 可以像开发普通游戏一样使用OpenGL ES开发虚拟现实应用。
Cardboard SDK 提供了 CardboardActivity 类及StereoRenderer 接口。来提供虚拟现实的相关功能。我们继承它并从写一些方法即可实现虚拟现实显示。主要步骤为:(一).载入顶点及片元着色器。(二).读取obj文件内保存的顶点及面数据,读取mtl文件内保存的材质数据,并转换为opengl能使用的格式。(三).根据上一步都去的材质信息载入材质的纹理贴图。(四).配置摄像机参数并生成变换矩阵。(五).实际显示效果图。(六).根据外部设备输入来确定是否移动摄像机位置来做到移动的效果。
(一)、载入顶点及片元着色器
 着色器(Shader)其实就是一段执行在GPU上的程序,此程序使用OpenGL ES SL语言来编写。它是一个描述顶点或像素特性的简单程序。
对于发送给GPU的每一个Vertex(顶点),都要执行一次顶点着色器(Vertex Shader)。其功能是把每个顶点在虚拟空间中的三维坐标变换为可以在屏幕上显示的二维坐标,并带有用于z-buffer的深度信息。
片元着色器计算每个像素的颜色和其它属性。它通过应用光照值、凹凸贴图,阴影,镜面高光,半透明等处理来计算像素的颜色并输出。它也可改变像素的深度(z-buffering)或在多个渲染目标被激活的状态下输出多种颜色。
通过下面的代码创建着色器:
//顶点着色器
        private final String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"attribute vec2 aTexCoor;" +  //顶点纹理坐标输入
"varying vec2 vTextureCoord;"+  // 纹理坐标输出
"void main() {" +
"  gl_Position = uMVPMatrix * vPosition;" +
"vTextureCoord=aTexCoor;" +            // 纹理
"}";
        private final String fragmentShaderCode =
"precision mediump float;" +
"varying vec2 vTextureCoord;" +
"uniform sampler2D sTexture;" +
"void main() {" +
"  gl_FragColor = texture2D(sTexture, vTextureCoord);" +//给此片元从纹理中采样出颜色值
"}";
着色器使用下面的代码将其载入opengl:
            vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
            fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
            mProgram = GLES20.glCreateProgram();             // 创建一个空的OpenGL ES Program
            GLES20.glAttachShader(mProgram, vertexShader);   // 将vertex shader添加到program
            GLES20.glAttachShader(mProgram, fragmentShader); // 将fragment shader添加到program
            GLES20.glLinkProgram(mProgram);                  // 创建可执行的 OpenGL ES program
    (二)、读取obj文件内保存的顶点及面数据,读取mtl文件内保存的材质数据,并转换为opengl能使用的格式。
    Obj 文件为纯文本文件,保存了顶点坐标、顶点法向量、顶点纹理及三角形的顶点坐标索引、法向量索引、纹理索引等信息。以v 开头的是顶点坐标,以vn开头的保存的是顶点法向量,以vt开头的保存的是顶点纹理,g表示组的开始,usemtl表示之后的三角形面使用的材质。F保存的是用空格分开的三角形的三个顶点的数据的索引。
Obj文件例子:
v  17.6417 0.0000 -30.7221
v  17.6417 0.0000 -76.1059
v  107.8966 0.0000 -76.1059
v  107.8966 0.0000 -30.7221
v  17.6417 31.8889 -30.7221
v  107.8966 31.8889 -30.7221
v  107.8966 31.8889 -76.1059
v  17.6417 31.8889 -76.1059
# 8 vertices
vn 0.0000 -1.0000 -0.0000
vn 0.0000 1.0000 -0.0000
vn 0.0000 0.0000 1.0000
vn 1.0000 0.0000 -0.0000
vn 0.0000 0.0000 -1.0000
vn -1.0000 0.0000 -0.0000
# 6 vertex normals
vt 0.0219 0.2658 0.0000
vt 0.0219 0.0219 0.0000
vt 0.5070 0.0219 0.0000
vt 0.5070 0.2658 0.0000
vt 0.0219 0.3077 0.0000
vt 0.5070 0.3077 0.0000
vt 0.5070 0.5516 0.0000
vt 0.0219 0.5516 0.0000
vt 0.0219 0.5935 0.0000
vt 0.5070 0.5935 0.0000
vt 0.5070 0.7648 0.0000
vt 0.0219 0.7648 0.0000
vt 0.5488 0.0219 0.0000
vt 0.7928 0.0219 0.0000
vt 0.7928 0.1933 0.0000
vt 0.5488 0.1933 0.0000
vt 0.0219 0.8067 0.0000
vt 0.5070 0.8067 0.0000
vt 0.5070 0.9781 0.0000
vt 0.0219 0.9781 0.0000
vt 0.5488 0.3077 0.0000
vt 0.7928 0.3077 0.0000
vt 0.7928 0.4791 0.0000
vt 0.5488 0.4791 0.0000
# 24 texture coords
g Box001
usemtl wire_135110008
f 1/1/1 2/2/1 3/3/1
f 3/3/1 4/4/1 1/1/1
f 5/5/2 6/6/2 7/7/2
f 7/7/2 8/8/2 5/5/2
f 1/9/3 4/10/3 6/11/3
f 6/11/3 5/12/3 1/9/3
f 4/13/4 3/14/4 7/15/4
f 7/15/4 6/16/4 4/13/4
f 3/17/5 2/18/5 8/19/5
f 8/19/5 7/20/5 3/17/5
f 2/21/6 1/22/6 5/23/6
f 5/23/6 8/24/6 2/21/6
# 12 faces
其中“v  17.6417 0.0000 -30.7221”行以v开头,表示本行保存的是顶点坐标,后面用空格隔开的浮点数即顶点的x、y、z的坐标。
“vn 0.0000 -1.0000 -0.0000”行以vm开头,表示本行保存的是顶点法向量。
“vt 0.0219 0.2658 0.0000”行以vt开头,保存的是顶点纹理坐标。
“g Box001”行表示下面的内容属于组Box001.
“usemtl wire_135110008”表示下面的内容使用的材质是wire_135110008。具体的材质信息保存在mtl文件里面。
“f 5/23/6 8/24/6 2/21/6 ”行以f开头,表示本行是三角形面。其中“5/23/6”表示三角形第一个顶点数据,“8/24/6”是第二个定点的数据,以此类推。“5/23/6”以斜线分隔开的分别为该定点的顶点坐标索引、顶点纹理坐标索引及顶点法向量索引。其中5表示第5个顶点坐标即“v  17.6417 31.8889 -30.7221”。23表示第23个顶点纹理坐标即“vt 0.7928 0.4791 0.0000”。
Mtl文件例子:
newmtl wire_135110008
   Ns 25.0000
   Ni 1.5000
   d 1.0000
   Tr 0.0000
   Tf 1.0000 1.0000 1.0000
   illum 2
   Ka 0.5000 0.5000 0.5000
   Kd 0.5000 0.5000 0.5000
   Ks 0.0450 0.0450 0.0450
   Ke 0.0000 0.0000 0.0000
   map_Ka maps\Box001VRay 完成贴图.png
   map_Kd maps\Box001VRay 完成贴图.png
“newmtl wire_135110008”行表示之后是材质wire_135110008的内容。
“map_Kd maps\Box001VRay 完成贴图.png”行保存的是漫反射贴图路径。
public void onSurfaceCreated(EGLConfig config)方法在Activity建立时调用,可以重写它并在这里载入obj文件、mtl文件来获得顶点、材质等信息。
读取材质信息代码:
    public static Map<String,Material> LoadMtl(String dirPath, String fname)
    {
        Log.i(TAG,"LoadMtl... "+dirPath+"/"+fname);
        // 材质列表
        Map<String,Material> materials =new HashMap<String, Material>() ;
        Material material=null;
        try
        {
            File file = new File(dirPath, fname);
            InputStream in=new FileInputStream(file);
            InputStreamReader isr=new InputStreamReader(in,"gb2312");
            BufferedReader br=new BufferedReader(isr);
            String temps=null;
            while((temps=br.readLine())!=null)
            {
                String[] tempsa=temps.trim().split("[ ]+",2);
                if(tempsa[0].trim().equals("newmtl"))
                {//新材质
                    material = new Material();
                    material.name = tempsa[1].trim();
                    materials.put(material.name,material);
                }
                else if(tempsa[0].trim().equals("map_Kd"))
                { //漫反射贴图
                    material.map_Kd = tempsa[1].trim();
                }
            }
        }
        catch(Exception e)
        {
            Log.d("load error", "load error");
            e.printStackTrace();
        }
        Log.i(TAG,"LoadMtl    ok "+dirPath+"/"+fname);
        return materials;
    }
读取顶点数据代码:
    static ArrayList<Float> ParseCoordinate(String value)
    {
        ArrayList<Float> res=new ArrayList<Float>(3);//原始顶点坐标列表
        String[] tempsa = value.trim().split("[ ]+");
        assert tempsa.length == 3;
        res.add(Float.parseFloat(tempsa[0]));
        res.add(Float.parseFloat(tempsa[1]));
        res.add(Float.parseFloat(tempsa[2]));
        return res;
    }
    public static ArrayList<ObjGroup> LoadObj(String dirPath,String fname)
    {
        Log.i(TAG,"LoadObj... "+dirPath+"/"+fname);
        ArrayList<Float> alv=new ArrayList<Float>();//原始顶点坐标列表
        ArrayList<Float> alvn=new ArrayList<Float>();//原始顶点坐标列表
        ArrayList<Float> alvt=new ArrayList<Float>();//原始顶点坐标列表
        ArrayList<ObjGroup> objGroups=new ArrayList<ObjGroup>(); // 组列表,返回值
        ObjGroup nGroup=null; // 当前组
        ObjGroupMtl nGroupMtl=null;//当前组纹理
        try
        {
            File file = new File(dirPath, fname);
            InputStream in=new FileInputStream(file);
            InputStreamReader isr=new InputStreamReader(in,"gb2312");
            BufferedReader br=new BufferedReader(isr);
            String temps=null;
            while((temps=br.readLine())!=null)
            {
                String[] tempsa=temps.trim().split("[ ]+",2);
                if(tempsa[0].trim().equals("v"))
                {//此行为顶点坐标
                    alv.addAll(ParseCoordinate(tempsa[1]));
                }
                else if (tempsa[0].trim().equals("vn"))
                {//此行为顶点法向量
                    alvn.addAll(ParseCoordinate(tempsa[1]));
                }
                else if (tempsa[0].trim().equals("vt"))
                {//此行为顶点纹理
                    alvt.addAll(ParseCoordinate(tempsa[1]));
                }
                else if (tempsa[0].trim().equals("g"))
                {   //组 其实就是3dmax对象
                    nGroup = new ObjGroup(tempsa[1].trim());
                    objGroups.add(nGroup);
                }
                else if (tempsa[0].trim().equals("usemtl"))
                { //纹理组
                    nGroupMtl = new ObjGroupMtl(tempsa[1].trim());
                    nGroup.almtl.add(nGroupMtl);
                }
                else if(tempsa[0].trim().equals("f"))
                {//三角形面
                    String[] tempsaa = tempsa[1].split("[ ]+");
                    assert  tempsaa.length == 3;//xyz
                    for (String t : tempsaa)
                    {
                        String[] tt = t.split("/");  //拆分为顶点坐标、顶点纹理、顶点法向量
                        assert tt.length == 3;
nGroupMtl._alv.add(alv.get((Integer.parseInt(tt[0]) - 1)*3+0));
nGroupMtl._alv.add(alv.get((Integer.parseInt(tt[0]) - 1)*3+1));
nGroupMtl._alv.add(alv.get((Integer.parseInt(tt[0]) - 1)*3+2));
                        if (tt[1].trim().length()!=0) {
nGroupMtl._alvt.add(alvt.get((Integer.parseInt(tt[1]) - 1) * 3 + 0));
                            nGroupMtl._alvt.add(1 - (alvt.get((Integer.parseInt(tt[1]) - 1) * 3 + 1)));
                        }
                        else
                        {
                 nGroupMtl._alvt.add(Float.valueOf(0));
                nGroupMtl._alvt.add(Float.valueOf(0));
                        }
                        if (tt[2].trim().length()!=0) {
nGroupMtl._alvn.add(alvn.get((Integer.parseInt(tt[2]) - 1) * 3 + 0));
nGroupMtl._alvn.add(alvn.get((Integer.parseInt(tt[2]) - 1) * 3 + 1));
nGroupMtl._alvn.add(alvn.get((Integer.parseInt(tt[2]) - 1) * 3 + 2));
                        }
                        else
                        {
nGroupMtl._alvn.add(Float.valueOf(0));
nGroupMtl._alvn.add(Float.valueOf(0));
nGroupMtl._alvn.add(Float.valueOf(0));
                        }
                    }
                }
            }
        }
        catch(Exception e)
        {
            Log.d("load error", "load error");
            e.printStackTrace();
        }
        for(ObjGroup g :objGroups)
        {
            g.zip();
        }
        Log.i(TAG,"LoadObj    ok   "+dirPath+"/"+fname);
        return objGroups;
    }
读取完毕后还需要通过下面的代码将其转换成为opengl能直接使用的FloatBuffer 格式。
        FloatBuffer toFloatBuffer(ArrayList<Float> l)
        {
            float[] v = new float[l.size()];
            for(int i =0;i<l.size();i++)
            {
                v[i]=l.get(i);
            }
            FloatBuffer buffer;
            //创建顶点坐标数据缓冲
            ByteBuffer vbb = ByteBuffer.allocateDirect(v.length*4);
            vbb.order(ByteOrder.nativeOrder());//设置字节顺序
            buffer = vbb.asFloatBuffer();//转换为int型缓冲
            buffer.put(v);//向缓冲区中放入顶点坐标数据
            buffer.position(0);//设置缓冲区起始位置
            return buffer;
        }
        void zip()
        {
            vertexCount = _alv.size()/3;
            vBuffer = toFloatBuffer(_alv);
            vnBuffer = toFloatBuffer(_alvn);
            vtBuffer = toFloatBuffer(_alvt);
            _alv = null;
            _alvn = null;
            _alvt = null;
        }
    通过以上步骤就把定点数据处理完毕了。
(三).根据上一步都去的材质信息载入材质的纹理贴图。
       public void initTexture(Context context)//textureId
        {
            useMaterials = new HashMap<>();
            // 使用的纹理
            ArrayList<String> alm=new ArrayList<String>();
            // 找出使用了的纹理
            for(ObjGroup g :this.objGroups)
            {
                for (ObjGroupMtl m :g.almtl)
                {
                    if (!alm.contains(m.MtlName))
                    {
                        alm.add(m.MtlName);
                    }
                }
            }
             //生成纹理ID
            int[] textures = new int[alm.size()];
            GLES20.glGenTextures
                    (
                   alm.size(),          //产生的纹理id的数量
                            textures,   //纹理id的数组
                            0           //偏移量
                    );
            for(int i =0;i<alm.size();i++)
            {
                String mName = alm.get(i); // 纹理名称
                int textureId = textures[i]; // 纹理资源ID
                Material m = materials.get(mName);
                useMaterials.put(mName,textureId);
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);
                checkGLError("glTexParameterf");
                Bitmap bitmapTmp=null;
                String imgPath=dirPath+m.map_Kd.replace("\\","/");
                try {
                    bitmapTmp = BitmapFactory.decodeFile(imgPath);
                }
                catch(NullPointerException ex)
                {
                    MUtil.makeText(context, "载入纹理失败.fname:"+imgPath);
                    Log.e(TAG, "载入纹理失败。fname="+imgPath);
                    ex.printStackTrace();
                }
                if(bitmapTmp == null)
                {
                    MUtil.makeText(context, "载入纹理失败.fname:" + imgPath);
                    Log.e(TAG, "载入纹理失败。fname=" + imgPath);
                    InputStream is = context.getResources().openRawResource(R.drawable.wall);
                    bitmapTmp = BitmapFactory.decodeStream(is);
                }
                checkGLError("bitmapTmp");
                //实际加载纹理
                GLUtils.texImage2D
                        (
                                GLES20.GL_TEXTURE_2D, 0, bitmapTmp,0);
                checkGLError("GLUtils.texImage2D");
                bitmapTmp.recycle();
            }
        }
(四).配置摄像机参数并生成变换矩阵。
   public void onNewFrame(HeadTransform headTransform)函数是绘制与单眼睛无关的图像的地方,需要在这里确定相机位置。这里相机位置是用的变量,可以修改它的值来达到移动的目的。
        Matrix.setLookAtM(mCamera, 0,
                camera_x, camera_y, camera_z,  // 相机坐标
                camera_x, camera_y,camera_z-CAMERA_Z,       // 目标坐标
                0.0f, 1000000.0f, 0.0f);      // up 顶点坐标
public void onDrawEye(Eye eye) 函数为渲染单眼相关图像的地方,我们需要在这里生成变换矩阵并生成图像。下面的代码生成最终变换矩阵。
        Matrix.multiplyMM(mView, 0, eye.getEyeView(), 0, mCamera, 0);
        float[] perspective = eye.getPerspective(0.1f,  200.0f);        Matrix.multiplyMM(mModelView, 0, mView, 0, mModelCube, 0);
        Matrix.multiplyMM(mModelViewProjection, 0, perspective, 0, mModelView, 0);
(五).实际显示效果图。
生成矩阵后通过下面的代码就是实际的将效果图绘制出来了。
        public void draw(float[] mvpMatrix)
        {
            final int COORDS_PER_VERTEX = 3;
            final int vertexStride = COORDS_PER_VERTEX * 4; // bytes per vertex
            for(ObjGroup g :objGroups) {
                for (ObjGroupMtl m : g.almtl) {
                    int textureId =  this.useMaterials.get(m.MtlName);
                    GLES20.glUseProgram(mProgram);
                    int mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
GLES20.glEnableVertexAttribArray(mPositionHandle);
                    GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                            GLES20.GL_FLOAT, false,
                            vertexStride, m.vBuffer);
                    int maTexCoorHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoor");
                    GLES20.glVertexAttribPointer(
                            maTexCoorHandle,
                            2,
                            GLES20.GL_FLOAT,
                            false,
                            2 * 4,
                            m.vtBuffer
                    );
                    int mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
                    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0); GLES20.glEnableVertexAttribArray(maPositionHandle);
GLES20.glEnableVertexAttribArray(maTexCoorHandle);
                    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
                    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
                    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, m.vertexCount);
GLES20.glDisableVertexAttribArray(mPositionHandle);
                }
            }
        }
    (六).根据外部设备输入来确定是否移动摄像机位置来做到移动的效果。
    通过上面的代码实现了360°虚拟现实观看,抬头、低头、左转、右转都能看到对应位置的效果图。还可以更进一步通过加速度传感器来实现走动实时显示对应位置的效果图。不过带着虚拟现实设备看不见外界走路容易碰东西绊倒观看者,所以这里使用键盘来移动位置,可以通过OTG接口外接USB键盘或者直接连接蓝牙键盘,然后通过按键来修改相机位置即可实现位置的变化。
    Override
    public boolean onKeyDown (int keyCode, KeyEvent event)
    {
        switch (keyCode)
        {
            case KeyEvent.KEYCODE_DPAD_UP:
                camera_z=camera_z-10;
                break;
            case KeyEvent.KEYCODE_DPAD_DOWN:
                camera_z=camera_z+10;
                break;
            case KeyEvent.KEYCODE_DPAD_LEFT:
                camera_x=camera_x-10;
                break;
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                camera_x=camera_x+10;
                break;
            case KeyEvent.KEYCODE_PAGE_DOWN:
                camera_y=camera_y-10;
                break;
            case KeyEvent.KEYCODE_PAGE_UP:
                camera_y=camera_y+10;
                break;
        }
        return super.onKeyDown(keyCode, event);
    }
    以上代码就已经实现了虚拟现实的显示功能。

Claims (8)

1. 一种虚拟效果图实景展示装置,包括有支撑镜架、目镜透镜、显示屏、陀螺仪、计算单元和加速度传感器,其特征是:所说的支撑镜架为主体支撑部件,所述支撑镜架的一侧设置有目镜透镜,所述目镜透镜对应的另一侧,支撑镜架的内部壳体内设置有显示屏,所述显示屏与计算单元电路连接,所述计算单元电路连接有陀螺仪和加速度传感器。
2.根据权利要求1所述的一种虚拟效果图实景展示装置,其特征在于所述显示屏分为左眼区与右眼区。
3.一种虚拟效果图实景展示装置的实现方法,其特征在于具体分为两个步骤:一、预先处理效果图;二、实际显示效果图。
4.根据权利要求3所述的一种虚拟效果图实景展示装置的实现方法,其特征在于所说的预先处理效果图又分为:(一)优化光照计算;(二)优化几何体;(三)统一格式,其具体步骤为:
    (一)优化光照计算
步骤为:1.使用3dMax打开效果图;2.点击“渲染”、“渲染到纹理”菜单,打开渲染到纹理对话框;3.选择需要处理的对象;4.增加渲染输出,并设置目标贴图位置为漫反射,自动贴图大小,输出到源,保留烘培材质等设置之后点击渲染即可;
   (二)优化几何体
简单优化的具体操作为:1.选定需要优化的对象;2.打开修改面板;3.点击修改器列表选择“优化”来增加“优化”修改器即可减少顶点、面的数量;
   (三)统一格式
3dmax通过导出命令直接导出obj格式的文件。
5.根据权利要求4所述的一种虚拟效果图实景展示装置的实现方法,其特征在于优化几何体的具体做法是:
   (一)生成全景图:(1)打开原效果图;(2)选定一视图,并设置摄像机位置为需要展示的位置;(3)打开渲染设置对话框,将输出图像大小设置为宽2048高1024,即2:1的比例;修改v-ray 摄像机类型为球形,覆盖视野为360;
   (二)建立新效果图:(1)创建“球体”;(2)为球体新建材质,并指定漫反射贴图为(一)生成的全景图;(3)设置摄像机位置为球心。
6.根据权利要求5所述的一种虚拟效果图实景展示装置的实现方法,其特征在于也可以通过以下代码计算出来新创建的几何体的顶点坐标及纹理坐标,具体代码为:
    public void initVertexData(float r){
        final float UNIT_SIZE=2.5f;
        ArrayList<Float> alVertix=new ArrayList<Float>();
        final float angleSpan=10f;
        for(float vAngle=90;vAngle>-90;vAngle=vAngle-angleSpan){
            for(float hAngle=360;hAngle>0;hAngle=hAngle-angleSpan){
                double xozLength=r*UNIT_SIZE*Math.cos(Math.toRadians(vAngle));
                float x1=(float)(xozLength*Math.cos(Math.toRadians(hAngle)));
                float z1=(float)(xozLength*Math.sin(Math.toRadians(hAngle)));
                float y1=(float)(r*UNIT_SIZE*Math.sin(Math.toRadians(vAngle)));
                xozLength=r*UNIT_SIZE*Math.cos(Math.toRadians(vAngle-angleSpan));
                float x2=(float)(xozLength*Math.cos(Math.toRadians(hAngle)));
                float z2=(float)(xozLength*Math.sin(Math.toRadians(hAngle)));
                float y2=(float)(r*UNIT_SIZE*Math.sin(Math.toRadians(vAngle-angleSpan)));
                xozLength=r*UNIT_SIZE*Math.cos(Math.toRadians(vAngle-angleSpan));
                float x3=(float)(xozLength*Math.cos(Math.toRadians(hAngle-angleSpan)));
                float z3=(float)(xozLength*Math.sin(Math.toRadians(hAngle-angleSpan)));
                float y3=(float)(r*UNIT_SIZE*Math.sin(Math.toRadians(vAngle-angleSpan)));
                xozLength=r*UNIT_SIZE*Math.cos(Math.toRadians(vAngle));
                float x4=(float)(xozLength*Math.cos(Math.toRadians(hAngle-angleSpan)));
                float z4=(float)(xozLength*Math.sin(Math.toRadians(hAngle-angleSpan)));
                float y4=(float)(r*UNIT_SIZE*Math.sin(Math.toRadians(vAngle)));
                alVertix.add(x1);alVertix.add(y1);alVertix.add(z1);
                alVertix.add(x2);alVertix.add(y2);alVertix.add(z2);
                alVertix.add(x4);alVertix.add(y4);alVertix.add(z4);
                alVertix.add(x4);alVertix.add(y4);alVertix.add(z4);
                alVertix.add(x2);alVertix.add(y2);alVertix.add(z2);
                alVertix.add(x3);alVertix.add(y3);alVertix.add(z3);
            }}
        int vCount=alVertix.size()/3;
        vertexCount = vCount;
        float vertices[]=new float[vCount*3];
        for(int i=0;i<alVertix.size();i++){
            vertices[i]=alVertix.get(i);
        }
        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
        vbb.order(ByteOrder.nativeOrder());
        vertexBuffer = vbb.asFloatBuffer();
        vertexBuffer.put(vertices);
        vertexBuffer.position(0);
        float[] texCoor=generateTexCoor(
                (int)(360/angleSpan),
                (int)(180/angleSpan)
        );
        ByteBuffer llbb = ByteBuffer.allocateDirect(texCoor.length*4);
        llbb.order(ByteOrder.nativeOrder());
        mTexCoorBuffer=llbb.asFloatBuffer();
        mTexCoorBuffer.put(texCoor);
        mTexCoorBuffer.position(0);
    }
    //自动切分纹理产生纹理数组的方法
    public float[] generateTexCoor(int bw,int bh){
        float[] result=new float[bw*bh*6*2];
        float sizew=1.0f/bw;//列数
        float sizeh=1.0f/bh;//行数
        int c=0;
        for(int i=0;i<bh;i++){
            for(int j=0;j<bw;j++){
                //每行列一个矩形,由两个三角形构成,共六个点,12个纹理坐标
                float s=j*sizew;
                float t=i*sizeh;
                result[c++]=s;
                result[c++]=t;
                result[c++]=s;
                result[c++]=t+sizeh;
                result[c++]=s+sizew;
                result[c++]=t;
                result[c++]=s+sizew;
                result[c++]=t;
                result[c++]=s;
                result[c++]=t+sizeh;
                result[c++]=s+sizew;
                result[c++]=t+sizeh;
            }}
        return result;
    }。
7.根据权利要求4或5所述的一种虚拟效果图实景展示装置的实现方法,其特征在于实际显示效果图的步骤为:(一)载入顶点及片元着色器;(二)读取obj文件内保存的顶点及面数据,读取mtl文件内保存的材质数据,并转换为opengl能使用的格式;(三)根据上一步都去的材质信息载入材质的纹理贴图;(四)配置摄像机参数并生成变换矩阵;(五)实际显示效果图;(六)根据外部设备输入来确定是否移动摄像机位置来做到移动的效果。
8.根据权利要求7所述的一种虚拟效果图实景展示装置的实现方法,其特征在于:(一)载入顶点及片元着色器,通过下面的代码创建着色器:
//顶点着色器
        private final String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"attribute vec2 aTexCoor;" +  //顶点纹理坐标输入
"varying vec2 vTextureCoord;"+  // 纹理坐标输出
"void main() {" +
"  gl_Position = uMVPMatrix * vPosition;" +
"vTextureCoord=aTexCoor;" +            // 纹理
"}";
        private final String fragmentShaderCode =
"precision mediump float;" +
"varying vec2 vTextureCoord;" +
"uniform sampler2D sTexture;" +
"void main() {" +
"  gl_FragColor = texture2D(sTexture, vTextureCoord);" +//给此片元从纹理中采样出颜色值
"}";
着色器使用下面的代码将其载入opengl:
            vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
            fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
            mProgram = GLES20.glCreateProgram();             // 创建一个空的OpenGL ES Program
            GLES20.glAttachShader(mProgram, vertexShader);   // 将vertex shader添加到program
            GLES20.glAttachShader(mProgram, fragmentShader); // 将fragment shader添加到program
            GLES20.glLinkProgram(mProgram);                  // 创建可执行的 OpenGL ES program;
    (二)读取obj文件内保存的顶点及面数据,读取mtl文件内保存的材质数据,并转换为opengl能使用的格式
Obj文件具体代码:
v  17.6417 0.0000 -30.7221
v  17.6417 0.0000 -76.1059
v  107.8966 0.0000 -76.1059
v  107.8966 0.0000 -30.7221
v  17.6417 31.8889 -30.7221
v  107.8966 31.8889 -30.7221
v  107.8966 31.8889 -76.1059
v  17.6417 31.8889 -76.1059
# 8 vertices
vn 0.0000 -1.0000 -0.0000
vn 0.0000 1.0000 -0.0000
vn 0.0000 0.0000 1.0000
vn 1.0000 0.0000 -0.0000
vn 0.0000 0.0000 -1.0000
vn -1.0000 0.0000 -0.0000
# 6 vertex normals
vt 0.0219 0.2658 0.0000
vt 0.0219 0.0219 0.0000
vt 0.5070 0.0219 0.0000
vt 0.5070 0.2658 0.0000
vt 0.0219 0.3077 0.0000
vt 0.5070 0.3077 0.0000
vt 0.5070 0.5516 0.0000
vt 0.0219 0.5516 0.0000
vt 0.0219 0.5935 0.0000
vt 0.5070 0.5935 0.0000
vt 0.5070 0.7648 0.0000
vt 0.0219 0.7648 0.0000
vt 0.5488 0.0219 0.0000
vt 0.7928 0.0219 0.0000
vt 0.7928 0.1933 0.0000
vt 0.5488 0.1933 0.0000
vt 0.0219 0.8067 0.0000
vt 0.5070 0.8067 0.0000
vt 0.5070 0.9781 0.0000
vt 0.0219 0.9781 0.0000
vt 0.5488 0.3077 0.0000
vt 0.7928 0.3077 0.0000
vt 0.7928 0.4791 0.0000
vt 0.5488 0.4791 0.0000
# 24 texture coords
g Box001
usemtl wire_135110008
f 1/1/1 2/2/1 3/3/1
f 3/3/1 4/4/1 1/1/1
f 5/5/2 6/6/2 7/7/2
f 7/7/2 8/8/2 5/5/2
f 1/9/3 4/10/3 6/11/3
f 6/11/3 5/12/3 1/9/3
f 4/13/4 3/14/4 7/15/4
f 7/15/4 6/16/4 4/13/4
f 3/17/5 2/18/5 8/19/5
f 8/19/5 7/20/5 3/17/5
f 2/21/6 1/22/6 5/23/6
f 5/23/6 8/24/6 2/21/6
# 12 faces
Mtl文件具体代码:
newmtl wire_135110008
    Ns 25.0000
    Ni 1.5000
    d 1.0000
    Tr 0.0000
    Tf 1.0000 1.0000 1.0000
    illum 2
    Ka 0.5000 0.5000 0.5000
    Kd 0.5000 0.5000 0.5000
    Ks 0.0450 0.0450 0.0450
    Ke 0.0000 0.0000 0.0000
    map_Ka maps\Box001VRay 完成贴图.png
    map_Kd maps\Box001VRay 完成贴图.png
读取材质信息代码:
    public static Map<String,Material> LoadMtl(String dirPath, String fname)
    {
        Log.i(TAG,"LoadMtl... "+dirPath+"/"+fname);
        // 材质列表
        Map<String,Material> materials =new HashMap<String, Material>() ;
        Material material=null;
        try
        {
            File file = new File(dirPath, fname);
            InputStream in=new FileInputStream(file);
            InputStreamReader isr=new InputStreamReader(in,"gb2312");
            BufferedReader br=new BufferedReader(isr);
            String temps=null;
            while((temps=br.readLine())!=null)
            {
                String[] tempsa=temps.trim().split("[ ]+",2);
                if(tempsa[0].trim().equals("newmtl"))
                {//新材质
                    material = new Material();
                    material.name = tempsa[1].trim();
                    materials.put(material.name,material);
                }
                else if(tempsa[0].trim().equals("map_Kd"))
                { //漫反射贴图
                    material.map_Kd = tempsa[1].trim();
                }
            }
        }
        catch(Exception e)
        {
            Log.d("load error", "load error");
            e.printStackTrace();
        }
        Log.i(TAG,"LoadMtl    ok "+dirPath+"/"+fname);
        return materials;
    }
读取顶点数据代码:
    static ArrayList<Float> ParseCoordinate(String value)
    {
        ArrayList<Float> res=new ArrayList<Float>(3);//原始顶点坐标列表
        String[] tempsa = value.trim().split("[ ]+");
        assert tempsa.length == 3;
        res.add(Float.parseFloat(tempsa[0]));
        res.add(Float.parseFloat(tempsa[1]));
        res.add(Float.parseFloat(tempsa[2]));
        return res;
    }
    public static ArrayList<ObjGroup> LoadObj(String dirPath,String fname)
    {
        Log.i(TAG,"LoadObj... "+dirPath+"/"+fname);
        ArrayList<Float> alv=new ArrayList<Float>();        ArrayList<Float> alvn=new ArrayList<Float>();
        ArrayList<Float> alvt=new ArrayList<Float>();        ArrayList<ObjGroup> objGroups=new ArrayList<ObjGroup>();        ObjGroup nGroup=null;
        ObjGroupMtl nGroupMtl=null;
        try
        {
            File file = new File(dirPath, fname);
            InputStream in=new FileInputStream(file);
            InputStreamReader isr=new InputStreamReader(in,"gb2312");
            BufferedReader br=new BufferedReader(isr);
            String temps=null;
            while((temps=br.readLine())!=null)
            {
                String[] tempsa=temps.trim().split("[ ]+",2);
                if(tempsa[0].trim().equals("v"))
                {//此行为顶点坐标
                    alv.addAll(ParseCoordinate(tempsa[1]));
                }
                else if (tempsa[0].trim().equals("vn"))
                {//此行为顶点法向量
                    alvn.addAll(ParseCoordinate(tempsa[1]));
                }
                else if (tempsa[0].trim().equals("vt"))
                {//此行为顶点纹理
                    alvt.addAll(ParseCoordinate(tempsa[1]));
                }
                else if (tempsa[0].trim().equals("g"))
                {   //组 其实就是3dmax对象
                    nGroup = new ObjGroup(tempsa[1].trim());
                    objGroups.add(nGroup);
                }
                else if (tempsa[0].trim().equals("usemtl"))
                { //纹理组
                    nGroupMtl = new ObjGroupMtl(tempsa[1].trim());
                    nGroup.almtl.add(nGroupMtl);
                }
                else if(tempsa[0].trim().equals("f"))
                {//三角形面
                    String[] tempsaa = tempsa[1].split("[ ]+");
                    assert  tempsaa.length == 3;//xyz
                    for (String t : tempsaa)
                    {
                        String[] tt = t.split("/");  //拆分为顶点坐标、顶点纹理、顶点法向量
                        assert tt.length == 3;
nGroupMtl._alv.add(alv.get((Integer.parseInt(tt[0]) - 1)*3+0));
nGroupMtl._alv.add(alv.get((Integer.parseInt(tt[0]) - 1)*3+1));
nGroupMtl._alv.add(alv.get((Integer.parseInt(tt[0]) - 1)*3+2));
                        if (tt[1].trim().length()!=0) {
nGroupMtl._alvt.add(alvt.get((Integer.parseInt(tt[1]) - 1) * 3 + 0));
                            nGroupMtl._alvt.add(1 - (alvt.get((Integer.parseInt(tt[1]) - 1) * 3 + 1)));
                        }
                        else
                        {
nGroupMtl._alvt.add(Float.valueOf(0));
nGroupMtl._alvt.add(Float.valueOf(0));
                        }
                        if (tt[2].trim().length()!=0) {
nGroupMtl._alvn.add(alvn.get((Integer.parseInt(tt[2]) - 1) * 3 + 0));
nGroupMtl._alvn.add(alvn.get((Integer.parseInt(tt[2]) - 1) * 3 + 1));
nGroupMtl._alvn.add(alvn.get((Integer.parseInt(tt[2]) - 1) * 3 + 2));
                        }
                        else
                        {
nGroupMtl._alvn.add(Float.valueOf(0));
nGroupMtl._alvn.add(Float.valueOf(0));
nGroupMtl._alvn.add(Float.valueOf(0));
                        }
                    }
                }
            }
        }
        catch(Exception e)
        {
            Log.d("load error", "load error");
            e.printStackTrace();
        }
        for(ObjGroup g :objGroups)
        {
            g.zip();
        }
        Log.i(TAG,"LoadObj    ok   "+dirPath+"/"+fname);
        return objGroups;
    }
读取完毕后通过下面的代码将其转换成为opengl能直接使用的FloatBuffer 格式;
        FloatBuffer toFloatBuffer(ArrayList<Float> l)
        {
            float[] v = new float[l.size()];
            for(int i =0;i<l.size();i++)
            {
                v[i]=l.get(i);
            }
            FloatBuffer buffer;
            ByteBuffer vbb = ByteBuffer.allocateDirect(v.length*4);
            vbb.order(ByteOrder.nativeOrder());
            buffer = vbb.asFloatBuffer();
            buffer.put(v);
            buffer.position(0);
            return buffer;
        }
        void zip()
        {
            vertexCount = _alv.size()/3;
            vBuffer = toFloatBuffer(_alv);
            vnBuffer = toFloatBuffer(_alvn);
            vtBuffer = toFloatBuffer(_alvt);
            _alv = null;
            _alvn = null;
            _alvt = null;
        }
(三)根据上一步都去的材质信息载入材质的纹理贴图
       public void initTexture(Context context)//textureId
        {
            useMaterials = new HashMap<>();
            ArrayList<String> alm=new ArrayList<String>();
            for(ObjGroup g :this.objGroups)
            {
                for (ObjGroupMtl m :g.almtl)
                {
                    if (!alm.contains(m.MtlName))
                    {
                        alm.add(m.MtlName);
                    }
                }
            }
            int[] textures = new int[alm.size()];
            GLES20.glGenTextures
                    (alm.size(),textures, 0  );
            for(int i =0;i<alm.size();i++)
            {
                String mName = alm.get(i); // 纹理名称
                int textureId = textures[i]; // 纹理资源ID
                Material m = materials.get(mName);
                useMaterials.put(mName,textureId);
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);
                checkGLError("glTexParameterf");
                Bitmap bitmapTmp=null;
                String imgPath=dirPath+m.map_Kd.replace("\\","/");
                try {
                    bitmapTmp = BitmapFactory.decodeFile(imgPath);
                }
                catch(NullPointerException ex)
                {
                    MUtil.makeText(context, "载入纹理失败 fname:"+imgPath);
                    Log.e(TAG, "载入纹理失败 fname="+imgPath);
                    ex.printStackTrace();
                }
                if(bitmapTmp == null)
                {
                    MUtil.makeText(context, "载入纹理失败 fname:" + imgPath);
                    Log.e(TAG, "载入纹理失败 fname=" + imgPath);
                    InputStream is = context.getResources().openRawResource(R.drawable.wall);
                    bitmapTmp = BitmapFactory.decodeStream(is);
                }
                checkGLError("bitmapTmp");
                GLUtils.texImage2D
                        (  GLES20.GL_TEXTURE_2D, 0, bitmapTmp, 0 );
                checkGLError("GLUtils.texImage2D");
                bitmapTmp.recycle();               }
        }
(四)配置摄像机参数并生成变换矩阵
        Matrix.setLookAtM(mCamera, 0,
                camera_x, camera_y, camera_z,  // 相机坐标
                camera_x, camera_y,camera_z-CAMERA_Z, // 目标坐标
                0.0f, 1000000.0f, 0.0f);      // up 顶点坐标
生成最终变换矩阵的代码:
        Matrix.multiplyMM(mView, 0, eye.getEyeView(), 0, mCamera, 0);
        float[] perspective = eye.getPerspective(0.1f,//近视点
                                   200.0f); //远视点
        Matrix.multiplyMM(mModelView, 0, mView, 0, mModelCube, 0);
        Matrix.multiplyMM(mModelViewProjection, 0, perspective, 0, mModelView, 0);
(五)实际显示效果图
        public void draw(float[] mvpMatrix)
        {
            final int COORDS_PER_VERTEX = 3;
            final int vertexStride = COORDS_PER_VERTEX * 4; // bytes per vertex
            for(ObjGroup g :objGroups) {
                for (ObjGroupMtl m : g.almtl) {
                    int textureId =  this.useMaterials.get(m.MtlName);
                    GLES20.glUseProgram(mProgram);
                    int mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
GLES20.glEnableVertexAttribArray(mPositionHandle);
                    GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                            GLES20.GL_FLOAT, false,
                            vertexStride, m.vBuffer);
                    int maTexCoorHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoor");
                    GLES20.glVertexAttribPointer(
                            maTexCoorHandle,
                            2,
                            GLES20.GL_FLOAT,
                            false,
                            2 * 4,
                            m.vtBuffer
                    );
                    int mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
                    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
GLES20.glEnableVertexAttribArray(maPositionHandle);
GLES20.glEnableVertexAttribArray(maTexCoorHandle);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
                    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, m.vertexCount);
GLES20.glDisableVertexAttribArray(mPositionHandle);
                }
            }
        }
(六)根据外部设备输入来确定是否移动摄像机位置来做到移动的效果
    Override
    public boolean onKeyDown (int keyCode, KeyEvent event)
    {
        switch (keyCode)
        {
            case KeyEvent.KEYCODE_DPAD_UP:
                camera_z=camera_z-10;
                break;
            case KeyEvent.KEYCODE_DPAD_DOWN:
                camera_z=camera_z+10;
                break;
            case KeyEvent.KEYCODE_DPAD_LEFT:
                camera_x=camera_x-10;
                break;
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                camera_x=camera_x+10;
                break;
            case KeyEvent.KEYCODE_PAGE_DOWN:
                camera_y=camera_y-10;
                break;
            case KeyEvent.KEYCODE_PAGE_UP:
                camera_y=camera_y+10;
                break;
        }
        return super.onKeyDown(keyCode, event);
    }
以上代码就已经实现了虚拟现实的显示功能。
CN201510293242.9A 2015-06-02 2015-06-02 一种虚拟效果图实景展示装置及其实现方法 Pending CN104867175A (zh)

Priority Applications (1)

Application Number Priority Date Filing Date Title
CN201510293242.9A CN104867175A (zh) 2015-06-02 2015-06-02 一种虚拟效果图实景展示装置及其实现方法

Applications Claiming Priority (1)

Application Number Priority Date Filing Date Title
CN201510293242.9A CN104867175A (zh) 2015-06-02 2015-06-02 一种虚拟效果图实景展示装置及其实现方法

Publications (1)

Publication Number Publication Date
CN104867175A true CN104867175A (zh) 2015-08-26

Family

ID=53912989

Family Applications (1)

Application Number Title Priority Date Filing Date
CN201510293242.9A Pending CN104867175A (zh) 2015-06-02 2015-06-02 一种虚拟效果图实景展示装置及其实现方法

Country Status (1)

Country Link
CN (1) CN104867175A (zh)

Cited By (9)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN105741341A (zh) * 2016-01-27 2016-07-06 桂林长海发展有限责任公司 一种三维空间环境成像系统及方法
CN105898337A (zh) * 2015-11-18 2016-08-24 乐视网信息技术(北京)股份有限公司 全景视频的显示方法和装置
CN106210859A (zh) * 2016-08-11 2016-12-07 合网络技术(北京)有限公司 全景视频渲染方法和装置
CN106651759A (zh) * 2016-12-21 2017-05-10 飞狐信息技术(天津)有限公司 基于固定位置相机的vr场景优化方法及装置
CN106709979A (zh) * 2016-12-29 2017-05-24 福州智永信息科技有限公司 一种模型网格数据导出Obj文件的方法以及系统
WO2017107444A1 (zh) * 2015-12-21 2017-06-29 乐视控股(北京)有限公司 虚拟显示设备的影像播放方法和装置
WO2017113729A1 (zh) * 2015-12-28 2017-07-06 乐视控股(北京)有限公司 360度图像加载方法、加载模块及移动终端
CN107833265A (zh) * 2017-11-27 2018-03-23 歌尔科技有限公司 一种图像切换展示方法和虚拟现实设备
CN109214979A (zh) * 2017-07-04 2019-01-15 北京京东尚科信息技术有限公司 用于在全景视频中融合对象的方法和装置

Citations (3)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
US5682172A (en) * 1994-12-30 1997-10-28 Forte Technologies, Inc. Headset for presenting video and audio signals to a wearer
CN104536579A (zh) * 2015-01-20 2015-04-22 刘宛平 交互式三维实景与数字图像高速融合处理系统及处理方法
CN104618712A (zh) * 2015-02-13 2015-05-13 北京维阿时代科技有限公司 一种头戴式虚拟现实设备及包括该设备的虚拟现实系统

Patent Citations (3)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
US5682172A (en) * 1994-12-30 1997-10-28 Forte Technologies, Inc. Headset for presenting video and audio signals to a wearer
CN104536579A (zh) * 2015-01-20 2015-04-22 刘宛平 交互式三维实景与数字图像高速融合处理系统及处理方法
CN104618712A (zh) * 2015-02-13 2015-05-13 北京维阿时代科技有限公司 一种头戴式虚拟现实设备及包括该设备的虚拟现实系统

Non-Patent Citations (6)

* Cited by examiner, † Cited by third party
Title
ADMIN: "优化修改器", 《3DMAX吧》 *
ANZHONGLIU: "OpenGL es2.0加载纹理图片和CubMap", 《博客》 *
CUGBAK: "浅学OpenGLES2.0", 《博客》 *
佚名: "Android--OpenEL ES之应用投影和摄像机视图", 《移动开发》 *
狼牙: "Ogre中导入Max烘焙的模型(1)—— 渲染到纹理", 《博客》 *
轻轻草原: "Android--DPAD键的事件处理", 《ITEYE技术网站》 *

Cited By (13)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN105898337A (zh) * 2015-11-18 2016-08-24 乐视网信息技术(北京)股份有限公司 全景视频的显示方法和装置
WO2017107444A1 (zh) * 2015-12-21 2017-06-29 乐视控股(北京)有限公司 虚拟显示设备的影像播放方法和装置
WO2017113729A1 (zh) * 2015-12-28 2017-07-06 乐视控股(北京)有限公司 360度图像加载方法、加载模块及移动终端
CN105741341B (zh) * 2016-01-27 2018-11-06 桂林长海发展有限责任公司 一种三维空间环境成像系统及方法
CN105741341A (zh) * 2016-01-27 2016-07-06 桂林长海发展有限责任公司 一种三维空间环境成像系统及方法
CN106210859A (zh) * 2016-08-11 2016-12-07 合网络技术(北京)有限公司 全景视频渲染方法和装置
CN106210859B (zh) * 2016-08-11 2020-03-27 合一网络技术(北京)有限公司 全景视频渲染方法和装置
CN106651759A (zh) * 2016-12-21 2017-05-10 飞狐信息技术(天津)有限公司 基于固定位置相机的vr场景优化方法及装置
CN106709979A (zh) * 2016-12-29 2017-05-24 福州智永信息科技有限公司 一种模型网格数据导出Obj文件的方法以及系统
CN109214979A (zh) * 2017-07-04 2019-01-15 北京京东尚科信息技术有限公司 用于在全景视频中融合对象的方法和装置
CN109214979B (zh) * 2017-07-04 2020-09-29 北京京东尚科信息技术有限公司 用于在全景视频中融合对象的方法和装置
CN107833265A (zh) * 2017-11-27 2018-03-23 歌尔科技有限公司 一种图像切换展示方法和虚拟现实设备
CN107833265B (zh) * 2017-11-27 2021-07-27 歌尔光学科技有限公司 一种图像切换展示方法和虚拟现实设备

Similar Documents

Publication Publication Date Title
CN104867175A (zh) 一种虚拟效果图实景展示装置及其实现方法
CN109313470B (zh) 利用重新投影的锐利文本绘制
US10628990B2 (en) Real-time system and method for rendering stereoscopic panoramic images
Santana et al. Multimodal location based services—semantic 3D city data as virtual and augmented reality
JP2011512575A (ja) 対話型凹凸ディスプレイへのグラフィックオブジェクトの投影
US20130257856A1 (en) Determining a View of an Object in a Three-Dimensional Image Viewer
CN113781624A (zh) 具有可选的世界空间变换的光线跟踪硬件加速
US10325403B2 (en) Image based rendering techniques for virtual reality
JP2012190428A (ja) 立体映像視覚効果処理方法
Sagristà et al. Gaia sky: Navigating the gaia catalog
JP7189288B2 (ja) リモートデバイス上に大型3dモデルを表示するための方法およびシステム
CN117011492B (zh) 图像渲染方法、装置、电子设备及存储介质
CN113962979A (zh) 一种基于深度图像的布料碰撞仿真增强呈现方法及装置
Trapp et al. Strategies for visualising 3D points-of-interest on mobile devices
KR101428577B1 (ko) 적외선 동작 인식 카메라를 사용하여 화면상에 네추럴 유저 인터페이스 기반 입체 지구본을 제공하는 방법
CN111862338B (zh) 模拟眼镜佩戴图像的显示方法及装置
Sellers et al. Rendering massive virtual worlds
JP5481751B2 (ja) 隠蔽処理プログラム、可視化処理方法及び装置
WO2024027237A1 (zh) 渲染的优化方法、电子设备和计算机可读存储介质
Lu Unreal engine nanite foliage shadow imposter
US12002165B1 (en) Light probe placement for displaying objects in 3D environments on electronic devices
EP3923121A1 (en) Object recognition method and system in augmented reality enviroments
JP2007164729A (ja) 画像生成システム、プログラム及び情報記憶媒体
Kolokouris Interactive presentation of cultural content using hybrid technologies, geographical systems and three-dimensional technologies.
Stemkoski et al. Introduction to 3D Graphics and Games

Legal Events

Date Code Title Description
C06 Publication
PB01 Publication
EXSB Decision made by sipo to initiate substantive examination
SE01 Entry into force of request for substantive examination
WD01 Invention patent application deemed withdrawn after publication
WD01 Invention patent application deemed withdrawn after publication

Application publication date: 20150826