文档章节

OpenGL ES绘制3D纹理贴图

abcijkxyz
 abcijkxyz
发布于 2016/07/30 17:24
字数 1564
阅读 11
收藏 0
点赞 0
评论 0

最近看了《疯狂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使用的是小端字节序,所以需要转换一下才能使用。


© 著作权归作者所有

共有 人打赏支持
abcijkxyz
粉丝 61
博文 6195
码字总数 1876
作品 0
深圳
项目经理
OpenGL实现物体动画和视频特效

OpenGL实现视频的水印、滤镜?OpenGL实现视频的剪裁、旋转? 2D/3D物体的 旋转,平移,缩放? OpenGL图片滤镜与视频滤镜? 矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合,最早来自于方...

shareus ⋅ 04/24 ⋅ 0

iOS-OpenGL ES入门教程(四)光照

前言 前面的基础文章列表 iOS-零基础学习OpenGL ES入门教程(一) iOS-OpenGL ES入门教程(二)最简单的纹理Demo iOS-OpenGL ES入门教程(三)纹理取样,混合,多重纹理 下面来讲一下光照 光...

安东_Ace ⋅ 06/14 ⋅ 0

Android 自定义相机开发(三) —— 了解下EGL

胡说八道 如果要使用OpenGl来自定义相机,这个还是要了解下的。可能大多数开发者使用过OpengGL但是不知道EGL是什么?EGL的作用是什么?这其实一点都不奇怪,因为Android中的GlSurfaceView已经...

aserbao ⋅ 05/10 ⋅ 0

图片和图形之构建一个OpenGL ES环境(10)

原文 概要 构建一个OpenGL ES环境 为了在Android应用程序中使用OpenGL ES绘制图形,您必须为它们创建一个视图容器。其中一种更直接的方法是实现a GLSurfaceView和a GLSurfaceView.Renderer。...

lichong951 ⋅ 05/26 ⋅ 0

iOS-OpenGL ES入门教程(三)纹理取样,混合,多重纹理

前言 上两篇文章里我们分别绘制了最简单的三角形和纹理图片 iOS-零基础学习OpenGL ES入门教程(一) iOS-OpenGL ES入门教程(二)最简单的纹理Demo 下面来讲一下纹理取样,混合,和多重纹理 ...

安东_Ace ⋅ 05/15 ⋅ 0

Android HWUI硬件加速模块浅析

原址 什么是硬件加速(What) 传统软件的UI绘制是依靠CPU来完成的,硬件加速就是将绘制任务交由GPU来执行。Android系统负责硬件加速的模块主要是HWUI,如下图所示: 为什么要硬件加速(Why)...

u010164190 ⋅ 04/27 ⋅ 0

OpenGL ES3(第一篇)

OpenGL ES 3.0主要新功能有: 1、渲染管线多重增强,实现先进视觉效果的加速,包括遮挡查询(Occlusion Query)、变缓反馈(Transform Feedback)、实例渲染(Instanced Rendering)、四个或更多渲...

星星编程 ⋅ 前天 ⋅ 0

OpenGL ES3(第二篇)

你这一生中的迷,必须要用其他的迷才能解开,就像有的梦,必须穿过其他的梦才能醒来。你必须一个一个地走过,才能走出这场连环梦。 ——《如梦之梦》 OpenGL ES提供了一个纹理贴图对几何图形...

星星编程 ⋅ 昨天 ⋅ 0

图片和图形之用OpenGL ES显示图形(9)

原文 概要 Android框架提供了大量用于创建有吸引力的功能性图形用户界面的标准工具。但是,如果您想要更多地控制应用程序在屏幕上绘制的内容,或者冒险进入三维图形,则需要使用其他工具。A...

lichong951 ⋅ 05/26 ⋅ 0

通过FFmpeg解码和OpenGL的YUV转RGB实现Android视频播放

前言 在我的博文 https://blog.csdn.net/ericbar/article/details/80506390 中,我们在Android平台上,实现了通过FFmpeg在native(C/C++)层进行视频解码,并通过OpenGL实现了硬件渲染工作,...

ericbar ⋅ 05/30 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Boost库编译应用

版本:Boost 1.66.0 Windows库编译 官网指南:直接执行bootstrap.bat处理文件即可,可以我却遇到一堆的问题。 环境:Windows 10 + Visual Studio 2017 Boost编译出来库命名 boost库生成文件命...

水海云 ⋅ 28分钟前 ⋅ 0

解决Eclipse发布到Tomcat丢失依赖jar包的问题

如果jar文件是以外部依赖的形式导入的。Eclipse将web项目发布到Tomcat时,是不会自动发布这些依赖的。 可以通过Eclipse在项目上右击 - Propertics - Deployment Assembly,添加“Java Build ...

ArlenXu ⋅ 28分钟前 ⋅ 0

iview tree组件层级过多时可左右滚动

使用vue+iview的tree组件,iview官网iview的tree树形控件 问题描述:tree层级过多时左右不可滚动 问题解决:修改overflow属性值 .el-tree-node>.el-tree-node_children { overflow: vi...

YXMBetter ⋅ 30分钟前 ⋅ 0

分布式锁

1.通过数据库实现 http://www.weizijun.cn/2016/03/17/%E8%81%8A%E4%B8%80%E8%81%8A%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E7%9A%84%E8%AE%BE%E8%AE%A1/ 2.ZK实现:curator-recipes分布式锁的......

素雷 ⋅ 38分钟前 ⋅ 0

Sublime Text3 快捷键

选择类 Ctrl+D 选中光标所占的文本,继续操作则会选中下一个相同的文本。 Alt+F3 选中文本按下快捷键,即可一次性选择全部的相同文本进行同时编辑。举个栗子:快速选中并更改所有相同的变量名...

AndyZhouX ⋅ 45分钟前 ⋅ 0

XamarinAndroid组件教程RecylerView自定义适配器动画

XamarinAndroid组件教程RecylerView自定义适配器动画 如果RecyclerViewAnimators.Adapters命名空间中没有所需要的适配器动画,开发者可以自定义动画。此时,需要让自定义的动画继承Animation...

大学霸 ⋅ 45分钟前 ⋅ 0

eureka 基础(二)

使用Eureka服务器进行身份验证 如果其中一个eureka.client.serviceUrl.defaultZone网址中包含一个凭据(如http://user:password@localhost:8761/eureka)),HTTP基本身份验证将自动添加到您...

明理萝 ⋅ 48分钟前 ⋅ 1

Kubernetes(五) - Service

Kubernetes解决的另外一个痛点就是服务发现,服务发现机制和容器开放访问都是通过Service来实现的,把Deployment和Service关联起来只需要Label标签相同就可以关联起来形成负载均衡,基于kuberne...

喵了_个咪 ⋅ 48分钟前 ⋅ 0

更新队友POM文件后报错

打开报错的地方的pom及其引用方法所在文件的pom,观察其版本号是否一致,不一致进行更改

森火 ⋅ 今天 ⋅ 0

IDEA使用sonarLint

一、IDEA如何安装SonarLint插件 1.打开 Idea 2.点击【File】 3.点击【Settings】 4.点击【Plugins】 5.在搜索栏中输入“sonarlint”关键字 6.点击【Install】进行安装 7.重启Idea 二、IDEA如...

开源中国成都区源花 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部