使用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