OpenGL ES绘制3D纹理贴图_glsurfaceview中绘制3d cube-程序员宅基地

技术标签: 3D图形  android  纹理贴图  opengl es  android图形  

最近看了《疯狂android讲义》的图形相关的内容,结合自己的理解,整理了一下。

下图是做出来的3D纹理贴图效果,手指在屏幕滑动时,图片可以随之转动。

要实现一个纹理贴图,很简单,大致需要五步:

1、gl.glEnable(GL10.GL_TEXTURE_2D) 启用2D纹理功能;

2、gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY) 启用纹理坐标数组;

3、gl.glBindTexture(GL10.GL_TEXTURE_2D,texture) 绑定纹理;

4、GLUtils.texImage2D(GL10.GL_TEXTURE_2D0, bitmap, 0) 根据位图生成纹理;

5、gl.glTexCoordPointer(2, GL10.GL_FLOAT,0, bufferUtil(cubeTextures)) 设置纹理坐标;

    下面是一个完整的例子:

public class MainActivity extends AppCompatActivity implements GestureDetector.OnGestureListener{

    private static final String TAG = "MainActivity";
    GestureDetector gestureDetector;
    // 定义旋转角度
    private float anglex = 0f;
    private float angley = 0f;
    static final float ROTATE_FACTOR = 60;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        GLSurfaceView glSurfaceView = new GLSurfaceView(this);
        MyRenderOne myRender = new MyRenderOne(this);
        glSurfaceView.setRenderer(myRender);
        setContentView(glSurfaceView);
        gestureDetector = new GestureDetector(this, this);
    }

    @Override
    public boolean onDown(MotionEvent e) {
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {  }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {  }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        velocityX = velocityX > 4000 ? 4000 : velocityX;
        velocityX = velocityX < -4000 ? -4000 : velocityX;
        velocityY = velocityY > 4000 ? 4000 : velocityY;
        velocityY = velocityY < -4000 ? -4000 : velocityY;
        // 根据横向上的速度计算沿Y轴旋转的角度
        angley += velocityX * ROTATE_FACTOR / 4000;
        // 根据纵向上的速度计算沿X轴旋转的角度
        anglex += velocityY * ROTATE_FACTOR / 4000;
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 将该Activity上的触碰事件交给GestureDetector处理
        return gestureDetector.onTouchEvent(event);
    }

    public class MyRenderOne implements GLSurfaceView.Renderer {

        // 立方体的顶点座标(一共是36个顶点,组成12个三角形)
        private float[] cubeVertices = { -0.4f, -0.4f, -0.4f, -0.4f, 0.4f,
                -0.4f, 0.4f, 0.4f, -0.4f, 0.4f, 0.4f, -0.4f, 0.4f, -0.4f, -0.4f,
                -0.4f, -0.4f, -0.4f, -0.4f, -0.4f, 0.4f, 0.4f, -0.4f, 0.4f, 0.4f,
                0.4f, 0.4f, 0.4f, 0.4f, 0.4f, -0.4f, 0.4f, 0.4f, -0.4f, -0.4f,
                0.4f, -0.4f, -0.4f, -0.4f, 0.4f, -0.4f, -0.4f, 0.4f, -0.4f, 0.4f,
                0.4f, -0.4f, 0.4f, -0.4f, -0.4f, 0.4f, -0.4f, -0.4f, -0.4f, 0.4f,
                -0.4f, -0.4f, 0.4f, 0.4f, -0.4f, 0.4f, 0.4f, 0.4f, 0.4f, 0.4f,
                0.4f, 0.4f, -0.4f, 0.4f, 0.4f, -0.4f, -0.4f, 0.4f, 0.4f, -0.4f,
                -0.4f, 0.4f, -0.4f, -0.4f, 0.4f, 0.4f, -0.4f, 0.4f, 0.4f, 0.4f,
                0.4f, 0.4f, 0.4f, 0.4f, -0.4f, -0.4f, 0.4f, -0.4f, -0.4f, -0.4f,
                -0.4f, -0.4f, -0.4f, 0.4f, -0.4f, -0.4f, 0.4f, -0.4f, 0.4f, 0.4f,
                -0.4f, 0.4f, -0.4f, };
        // 定义立方体所需要的6个面(一共是12个三角形所需的顶点)
        private byte[] cubeFacets = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
                13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
                30, 31, 32, 33, 34, 35, };
        // 定义纹理贴图的72个座标数据
        private float[] cubeTextures = { 1.0000f, 1.0000f, 1.0000f, 0.0000f,
                0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 1.0000f,
                1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f,
                1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f,
                1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f,
                0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f,
                1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f,
                0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f,
                0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f,
                0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f,
                0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f };

        private Context context;
        private int texture;
        public MyRenderOne(Context con)
        {
            this.context = con;
        }

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            gl.glDisable(GL10.GL_DITHER); //关闭抗抖动
            gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); //设置系统对透视进行修正
            gl.glClearColor(0, 0, 0, 0); //黑色背景
            gl.glShadeModel(GL10.GL_SMOOTH); //设置平滑模式
            gl.glEnable(GL10.GL_DEPTH_TEST);
            gl.glDepthFunc(GL10.GL_LEQUAL); //设置深度测试(opengl es会跟踪z轴的深度,以避免后面的图像挡住前面的图像)的类型
            gl.glEnable(GL10.GL_TEXTURE_2D); //开启2D纹理贴图
            loadTexture(gl);
        }

        private void loadTexture(GL10 gl) {
            Bitmap bitmap = null;
            try {
                bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.aa);
                int textures[] = new int[1];
                gl.glGenTextures(1,textures,0); //创建纹理
                texture = textures[0];
                gl.glBindTexture(GL10.GL_TEXTURE_2D, texture); //将纹理绑定到目标
                // 设置纹理被缩小(距离视点很远时被缩小)时候的滤波方式
                gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
                // 设置纹理被放大(距离视点很近时被放大)时候的滤波方式
                gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
                // 设置在横向、纵向上都是平铺纹理
                gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
                gl.glTexParameterf(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_WRAP_T,GL10.GL_REPEAT);
                // 加载位图生成纹理
                GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
            }  finally {
                if (bitmap != null){
                    bitmap.recycle();
                }
            }
        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            Log.i(TAG, "onSurfaceChanged enter...");
            gl.glViewport(0, 0, width, height); //设置视窗的大小及位置
            gl.glMatrixMode(GL10.GL_PROJECTION); //设置为投影矩阵
            gl.glLoadIdentity(); // 初始化单位矩阵
            float ratio = (float) width / height;
            gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); // 设置透视视窗的空间大小。
        }

        @Override
        public void onDrawFrame(GL10 gl) {
            // 清除屏幕缓存和深度缓存
            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
            //开启顶点设置和纹理设置功能
            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
            gl.glMatrixMode(GL10.GL_MODELVIEW); //设置为模型矩阵
            Log.i(TAG, "onDrawFrame enter...");

            gl.glLoadIdentity();
            gl.glTranslatef(0f, 0.0f, -2.0f); // 把绘图中心移入屏幕2个单位

            gl.glRotatef(angley, 0, 1, 0); // 旋转图形
            gl.glRotatef(anglex, 1, 0, 0);
            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, bufferUtil(cubeVertices)); //设置立方体的顶点
            gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, bufferUtil(cubeTextures)); //设置纹理顶点
            //gl.glBindTexture(GL10.GL_TEXTURE_2D, texture); //将纹理绑定到目标(执行纹理贴图)
            //绘制立方体
            gl.glDrawElements(GL10.GL_TRIANGLES, cubeFacets.length, GL10.GL_UNSIGNED_BYTE, bufferUtil2(cubeFacets));

            gl.glFinish(); //绘制完成
            // 禁用顶点和纹理设置
            gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        }

        /*
         * OpenGL 是一个非常底层的画图接口,它所使用的缓冲区存储结构是和我们的 java 程序中不相同的。
         * Java 是大端字节序(BigEdian),而 OpenGL 所需要的数据是小端字节序(LittleEdian)。
         * 所以,我们在将 Java 的缓冲区转化为 OpenGL 可用的缓冲区时需要作一些工作。建立buff的方法如下
         * */
        public Buffer bufferUtil(float []arr){
            FloatBuffer mBuffer ;
            //先初始化buffer,数组的长度*4,因为一个float占4个字节
            ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
            //数组排列用nativeOrder
            qbb.order(ByteOrder.nativeOrder());
            mBuffer = qbb.asFloatBuffer();
            mBuffer.put(arr);
            mBuffer.position(0);
            return mBuffer;
        }

        public Buffer bufferUtil1(int []arr){
            IntBuffer mBuffer ;
            //先初始化buffer,数组的长度*4,因为一个int占4个字节
            ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
            //数组排列用nativeOrder
            qbb.order(ByteOrder.nativeOrder());
            mBuffer = qbb.asIntBuffer();
            mBuffer.put(arr);
            mBuffer.position(0);
            return mBuffer;
        }

        public Buffer bufferUtil2(byte []arr){
            //先初始化buffer
            ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length);
            //数组排列用nativeOrder
            qbb.order(ByteOrder.nativeOrder());
            qbb.put(arr);
            qbb.position(0);
            return qbb;
        }

    }

}
    其中有几点需要说明下:

1、启动2D纹理功能用的是gl.glEnable(),而不是gl.glEnableClientState();
2、gl.glDrawElements()的第一个参数指定绘制的模块,可以是:GL10.GL_TRIANGLES(绘制三角形)和GL10.GL_TRIANGLE_STRIP(用多个三角形绘制多边形),第三个参数是坐标值的类型,如果是float则为GL10.GL_FLOAT,如果是int则为GL10.GL_FIXED,如果是byte则为GL10.GL_UNSIGNED_BYTE;

3、gl.glDrawElements()最后一个参数是坐标数组,如果用不好就会报错:java.lang.IllegalArgumentException: Must use a native order direct Buffer这个错误在android1.6以上会出现,原因是Java使用的是大端字节序,而opengl使用的是小端字节序,所以需要转换一下才能使用。


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wdong_love_cl/article/details/51378484

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法