使用opengles绘制灰度地形图
来源:互联网 发布:playclub 捏脸数据 编辑:程序博客网 时间:2024/05/02 14:04
效果图:
基本原理:
灰度地形图生成技术的基本原理是利用MxN的网格表示地形,同时提供一副对应尺寸的灰度图,根据灰度图中每个像素的灰度来确定网格中顶点的海拔,黑色像素(RGB各个色彩通道的值为0)代表海拔最低的位置,白色像素(RGB中各个色彩通道的值为255)代表海拔最高的位置,实际开发中用如下公式来计算某像素顶点的海拔高度:
实际海拔=最低海拔+最大高差x像素值/255.0
代码如下:
package test.com.opengles11_2;import android.content.res.Resources;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Color;/** * Created by hbin on 2016/9/27. */public class Constant { public static float[][] yArray; public static final float LAND_HIGH_ADJUST=-2f;//陆地的高度调整值 public static final float LAND_HIGHEST=20f;//陆地最大高差 //从灰度图片中加载陆地上每个顶点的高度 public static float[][] loadLandforms(Resources resources,int index) { Bitmap bt=BitmapFactory.decodeResource(resources, index);//导入灰度图 int colsPlusOne=bt.getWidth(); int rowsPlusOne=bt.getHeight(); float[][] result=new float[rowsPlusOne][colsPlusOne]; //遍历灰度图像 for(int i=0;i<rowsPlusOne;i++) { for(int j=0;j<colsPlusOne;j++) { int color=bt.getPixel(j,i);//获得指定行列处像素的颜色值 int r=Color.red(color); int g=Color.green(color); int b=Color.blue(color); int h=(r+g+b)/3; //像素顶点的海拔高度=最大高差X像素值/255.0+最低海拔 result[i][j]=h*LAND_HIGHEST/255+LAND_HIGH_ADJUST; } } return result; }}地形图构造类
package test.com.opengles11_2;import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.nio.FloatBuffer;import android.opengl.GLES20;/** * Created by hbin on 2016/9/27. * 绘制山 */public class Mountion { //单位长度 float UNIT_SIZE=1.0f; //自定义渲染管线的id int mProgram; //总变化矩阵引用的id int muMVPMatrixHandle; //顶点位置属性引用id int maPositionHandle; //顶点纹理坐标属性引用id int maTexCoorHandle; //草地的id int sTextureGrassHandle; //顶点数据缓冲和纹理坐标数据缓冲 FloatBuffer mVertexBuffer; FloatBuffer mTexCoorBuffer; //顶点数量 int vCount=0; public Mountion(MySurfaceView mv,float[][] yArray,int rows,int cols) { initVertexData(yArray,rows,cols); initShader(mv); } //初始化顶点坐标与着色数据的方法 public void initVertexData(float[][] yArray,int rows,int cols) { vCount=cols*rows*2*3;//每个格子两个三角形,每个三角形3个顶点 float vertices[]=new float[vCount*3];//每个顶点XYZ三个坐标 int count=0; for (int j=0;j<rows;j++){ for (int i=0;i<cols;i++){ //计算当前格子左上侧点坐标,坐标中心点在图形中心点 //故顶点坐标 x,z的计算需要x(-1*1/2) float zsx=-UNIT_SIZE*cols/2+i*UNIT_SIZE; float zsz=-UNIT_SIZE*rows/2+j*UNIT_SIZE; //第一个三角形--start vertices[count++]=zsx; vertices[count++]=yArray[j][i]; vertices[count++]=zsz; vertices[count++]=zsx; vertices[count++]=yArray[j+1][i]; vertices[count++]=zsz+UNIT_SIZE; vertices[count++]=zsx+UNIT_SIZE; vertices[count++]=yArray[j][i+1]; vertices[count++]=zsz; //第二个三角形start vertices[count++]=zsx+UNIT_SIZE; vertices[count++]=yArray[j][i+1]; vertices[count++]=zsz; vertices[count++]=zsx; vertices[count++]=yArray[j+1][i]; vertices[count++]=zsz+UNIT_SIZE; vertices[count++]=zsx+UNIT_SIZE; vertices[count++]=yArray[j+1][i+1]; vertices[count++]=zsz+UNIT_SIZE; } } //创建顶点坐标数据缓冲 ByteBuffer vbb=ByteBuffer.allocateDirect(vertices.length*4); vbb.order(ByteOrder.nativeOrder());//设置字节顺序 mVertexBuffer=vbb.asFloatBuffer(); mVertexBuffer.put(vertices); mVertexBuffer.position(0); //顶点纹理坐标数据的初始化 float[] texCoor=generateTexCoor(cols,rows); //创建顶点纹理坐标数据缓冲 ByteBuffer cbb = ByteBuffer.allocateDirect(texCoor.length*4); cbb.order(ByteOrder.nativeOrder());//设置字节顺序 mTexCoorBuffer = cbb.asFloatBuffer();//转换为Float型缓冲 mTexCoorBuffer.put(texCoor);//向缓冲区中放入顶点着色数据 mTexCoorBuffer.position(0);//设置缓冲区起始位置 } //初始化Shader的方法 public void initShader(MySurfaceView mv) { String mVertexShader=ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources()); String mFragmentShader=ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources()); //基于顶点着色器与片元着色器创建程序 mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader); //获取程序中顶点位置属性引用id maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); //获取程序中顶点纹理坐标属性引用id maTexCoorHandle= GLES20.glGetAttribLocation(mProgram, "aTexCoor"); //获取程序中总变换矩阵引用id muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); //草地纹理 sTextureGrassHandle=GLES20.glGetUniformLocation(mProgram, "vTextureCoord"); } //自动切分纹理产生纹理数组的方法 public float[] generateTexCoor(int bw,int bh){ float[] result=new float[bw*bh*6*2]; float sizew=16.0f/bw;//列数 float sizeh=16.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; } //自定义的绘制方法drawSelf public void drawSelf(int texId) { //指定使用某套shader程序 GLES20.glUseProgram(mProgram); //将最终变换矩阵传入shader程序 GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinalMatrix(), 0); //传送顶点位置数据 GLES20.glVertexAttribPointer ( maPositionHandle, 3, GLES20.GL_FLOAT, false, 3*4, mVertexBuffer ); //传送顶点纹理坐标数据 GLES20.glVertexAttribPointer ( maTexCoorHandle, 2, GLES20.GL_FLOAT, false, 2*4, mTexCoorBuffer ); //允许顶点位置数据数组 GLES20.glEnableVertexAttribArray(maPositionHandle); GLES20.glEnableVertexAttribArray(maTexCoorHandle); //绑定纹理 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId); GLES20.glUniform1i(sTextureGrassHandle, 0);//使用0号纹理 //绘制纹理矩形 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount); }}
sufaceview绘制类
package test.com.opengles11_2;import android.content.Context;import android.opengl.GLSurfaceView;import java.io.IOException;import java.io.InputStream;import javax.microedition.khronos.egl.EGLConfig;import javax.microedition.khronos.opengles.GL10;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.opengl.GLES20;import android.opengl.GLSurfaceView;import android.opengl.GLUtils;import android.view.MotionEvent;/** * Created by hbin on 2016/9/27. */public class MySurfaceView extends GLSurfaceView { static float direction=0;//视线方向 static float cx=0;//摄像机x坐标 static float cz=12;//摄像机z坐标 static float tx=0;//观察目标点x坐标 static float tz=0;//观察目标点z坐标 static final float DEGREE_SPAN=(float)(3.0/180.0f*Math.PI);//摄像机每次转动的角度 //线程循环的标志位 boolean flag=true; float x; float y; float Offset=12; SceneRenderer mRender; float preX; float preY; public MySurfaceView(Context context) { super(context); this.setEGLContextClientVersion(2); //设置使用OPENGL ES2.0 mRender = new SceneRenderer();//创建场景渲染器 setRenderer(mRender);//设置渲染器 setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//设置渲染模式为主动渲染 } @Override public boolean onTouchEvent(MotionEvent event) { x=event.getX(); y=event.getY(); switch(event.getAction()) { case MotionEvent.ACTION_DOWN: flag=true; new Thread() { @Override public void run() { while(flag) { if(x>0&&x<MainActivity.WIDTH/2&&y>0&&y<MainActivity.HEIGHT/2) {//向前 cx=cx-(float)Math.sin(direction)*1.0f; cz=cz-(float)Math.cos(direction)*1.0f; } else if(x>MainActivity.WIDTH/2&&x<MainActivity.WIDTH&&y>0&&y<MainActivity.HEIGHT/2) {//向后 cx=cx+(float)Math.sin(direction)*1.0f; cz=cz+(float)Math.cos(direction)*1.0f; } else if(x>0&&x<MainActivity.WIDTH/2&&y>MainActivity.HEIGHT/2&&y<MainActivity.HEIGHT) { direction=direction+DEGREE_SPAN; } else if(x>MainActivity.WIDTH/2&&x<MainActivity.WIDTH&&y>MainActivity.HEIGHT/2&&y<MainActivity.HEIGHT) { direction=direction-DEGREE_SPAN; } try { Thread.sleep(100); } catch(Exception e) { e.printStackTrace(); } } } }.start(); break; case MotionEvent.ACTION_UP: flag=false; break; } //设置新的观察目标点XZ坐标 tx=(float)(cx-Math.sin(direction)*Offset);//观察目标点x坐标 tz=(float)(cz-Math.cos(direction)*Offset);//观察目标点z坐标 //设置新的摄像机位置 MatrixState.setCamera(cx,3,cz,tx,1,tz,0,1,0); return true; } private class SceneRenderer implements GLSurfaceView.Renderer { Mountion mountion; //山的纹理id int mountionId; @Override public void onDrawFrame(GL10 gl) { //清除深度缓冲与颜色缓冲 GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); MatrixState.pushMatrix(); mountion.drawSelf(mountionId); MatrixState.popMatrix(); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { //设置视窗大小及位置 GLES20.glViewport(0, 0, width, height); //计算GLSurfaceView的宽高比 float ratio = (float) width / height; //调用此方法计算产生透视投影矩阵 MatrixState.setProjectFrustum(-ratio, ratio, -1, 1, 1, 100); //调用此方法产生摄像机9参数位置矩阵 MatrixState.setCamera(cx,3,cz,tx,1,tz,0,1,0); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { //设置屏幕背景色RGBA GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //打开深度检测 GLES20.glEnable(GLES20.GL_DEPTH_TEST); MatrixState.setInitStack(); Constant.yArray=Constant.loadLandforms(MySurfaceView.this.getResources(), R.mipmap.land); mountion=new Mountion(MySurfaceView.this,Constant.yArray, Constant.yArray.length-1,Constant.yArray[0].length-1); //初始化纹理 mountionId=initTexture(R.mipmap.grass); } } //生成纹理Id的方法 public int initTexture(int drawableId) { //生成纹理ID int[] textures = new int[1]; GLES20.glGenTextures ( 1, //产生的纹理id的数量 textures, //纹理id的数组 0 //偏移量 ); int textureId=textures[0]; 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); //ST方向纹理拉伸方式 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_REPEAT); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_REPEAT); //通过输入流加载图片 InputStream is = this.getResources().openRawResource(drawableId); Bitmap bitmapTmp; try { bitmapTmp = BitmapFactory.decodeStream(is); } finally { try { is.close(); } catch(IOException e) { e.printStackTrace(); } } //实际加载纹理 GLUtils.texImage2D ( GLES20.GL_TEXTURE_2D, //纹理类型,在OpenGL ES中必须为GL10.GL_TEXTURE_2D 0, //纹理的层次,0表示基本图像层,可以理解为直接贴图 bitmapTmp, //纹理图像 0 //纹理边框尺寸 ); //自动生成Mipmap纹理 GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D); //释放纹理图 bitmapTmp.recycle(); //返回纹理ID return textureId; }}
项目下载地址:
http://download.csdn.net/detail/hb707934728/9648418
0 0
- 使用opengles绘制灰度地形图
- Ogitor 绘制地形图(1)
- opengles-绘制旋转三角形
- opengles绘制立方体
- opengles绘制天空盒
- opengles绘制天空穹
- opengles 绘制球
- opengles绘制点精灵
- 用MATLAB解决绘制等高线地形图
- 利用MATLAB绘制GTOPO30的DEM地形图
- opengles绘制纹理(一)
- opengles绘制圆柱体(光照+纹理)
- opengles绘制圆锥体(光照+纹理)
- opengles 2.0如何绘制纹理
- opengles绘制圆锥体(光照+纹理)
- opengles绘制圆柱体(光照+纹理)
- opengles文字绘制相关资料
- 绘制灰度图像
- 华为推送-华为PUSH SDK Android版接入方法
- 我的第一篇博客
- 监管互联网彩票
- android 网络框架知多少
- html area map标签的使用(局部区域标记可以点击)
- 使用opengles绘制灰度地形图
- Jmeter测试框架学习总结之代码分析
- 热补丁Nuwa配置说明
- SVN命令使用详解
- WebSocket学习笔记——无痛入门
- 广场舞简史
- VS2005/2008/2010 增加代码边界线 缩进对齐线
- 解决myeclipse启动项目提示org.apache.jasper.JasperException: Unable to compile class for JSP
- Struts2文件上传和下载