Android开发之OpenGL的画笔工具GL10

本文涉及的产品
视觉智能开放平台,视频资源包5000点
视觉智能开放平台,图像资源包5000点
视觉智能开放平台,分割抠图1万点
简介: 这篇文章简述了OpenGL通过GL10进行三维图形绘制,强调颜色取值范围为0.0到1.0,背景和画笔颜色设置方法;介绍了三维坐标系及与之相关的旋转、平移和缩放操作;最后探讨了坐标矩阵变换,包括设置绘图区域、调整镜头参数和改变观测方位。示例代码展示了如何使用这些方法创建简单的三维立方体。

上一篇文章介绍了OpenGL绘制三维图形的流程,其实没有传说中的那么玄乎,只要放平常心把它当作一个普通控件就好了,接下来继续介绍OpenGL具体的绘图操作,这项工作得靠三维图形的画笔GL10来完成了。

GL10作为三维空间的画笔,它所描绘的三维物体却要显示在二维平面上,显而易见这不是一个简单的伙计。为了理顺物体从三维空间到二维平面的变换关系,有必要搞清楚OpenGL关于三维空间的几个基本概念。下面就概括介绍一下GL10编码的三类常见方法:

一、颜色的取值范围

Android中的三原色,不管是红色还是绿色还是蓝色,取值范围都是0到255,对应的十六进制数值则为00到FF,颜色数值越小表示亮度越弱,数值越大表示亮度越强。但在OpenGL之中,颜色的取值范围却是0.0到1.0,其中0.0对应Android标准的0,1.0对应Android标准的255,同理,OpenGL值为0.5的颜色对应Android标准的128。

GL10与颜色有关的方法主要有两个,说明如下:

glClearColor : 设置背景颜色。以下代码表示给三维空间设置白色背景:

        // 设置白色背景。四个参数依次为透明度alpha、红色red、绿色green、蓝色blue
        gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glColor4f : 设置画笔颜色。以下代码表示把画笔颜色设置为橙色:

        // 设置画笔颜色为橙色
        gl.glColor4f(0.0f, 1.0f, 1.0f, 0.0f);

二、三维坐标系

三维空间用来表达立体形状,需要三个方向的坐标,分别为水平方向的x轴和y轴,以及垂直方向的z轴。如下图的三维坐标系所示,三维空间有个M点,该点在x轴上的投影为P点,在y轴上的投影为Q点,在z轴上的投影为R点,因此M点的坐标位置就是(P, Q, R)。

sanwei.png

既然三维空间中的每个点都存在x、y、z三个方向的坐标值,那么与物体位置有关的方法均需提供x、y、z三方向的数值。比如物体的旋转方法glRotatef、平移方法glTranslatef、缩放方法glScalef,要分别指定物体在三个坐标轴上的旋转方向、平移距离、缩放倍率。具体的方法调用例子如下所示:

        // 沿着y轴的负方向旋转90度
        gl.glRotatef(90, 0, -1, 0);
        // 沿x轴方向移动1个单位
        gl.glTranslatef(1, 0, 0);
        // x,y,z三方向各缩放0.1倍
        gl.glScalef(0.1f, 0.1f, 0.1f);

三、坐标矩阵变换

有了三维坐标系,还要把三维物体投影到二维平面上,才能在手机屏幕中绘制三维图形。这个投影操作主要有三个步骤,下面分别展开叙述:

1、设置绘图区域

前面说过OpenGL使用GLSurfaceView这个控件作为绘图场所,于是允许绘制的区域范围自然落在GLSurfaceView内部。设置绘图区域的方法是glViewport,它指定了该区域左上角的平面坐标,以及区域的宽度和高度。当然一般OpenGL的绘图范围与GLSurfaceView的大小重合,所以倘若GLSurfaceView控件的宽度为width,高度为height,则设置绘图区域的方法调用示例如下:

        // 设置输出屏幕大小
        gl.glViewport(0, 0, width, height);

2、调整镜头参数

框住了绘图区域,还要把三维物体在二维平面上的投影一点一点描绘进去才行,这中间的坐标变换计算由OpenGL内部自行完成,开发者无需关注具体的运算逻辑。好比日常生活中的拍照,用户只管拿起手机咔嚓一下,根本不用关心摄像头怎么生成照片。用户所关心的照片效果,不外乎景物是大还是小,是远还是近;用专业一点的术语来讲,景物的大小由镜头的焦距决定,景物的远近由镜头的视距决定。

对于镜头的焦距而言,拍摄同样尺寸的照片,广角镜头看到的景物比标准镜头看到的景物更多,这意味着单个景物在广角镜头中会比较小,从而照片面积不增大、容纳的景物却变多了。

对于镜头的视距而言,它表示镜头的视力好坏,即最近能看到多近的景物,最远能看到多远的景物。在日常生活当中,每个人的睫毛离自己的眼睛太近了,这么近的东西能看得清楚吗?所以必须规定一下,最近只能看清楚比如离眼睛十厘米的物体。很遥远的景物自然也是看不清楚的,所以也要规定一下,比如最远只能看到一公里之内的人影。这个能看清景物的最近距离和最远距离,就构成了镜头的视距。

所以,镜头的焦距是横向的,它反映了画面的广度;而镜头的视距是纵向的,它反映了画面的深度。在OpenGL中,这些镜头参数的调节依赖于GL10的gluPerspective方法,具体的参数调整代码举例如下:

        // 设置投影矩阵,对应gluPerspective(调整相机参数)、glFrustumf(调整透视投影)、glOrthof(调整正投影)
        gl.glMatrixMode(GL10.GL_PROJECTION);
        // 重置投影矩阵,即去掉所有的参数调整操作
        gl.glLoadIdentity();
        // 设置透视图视窗大小。第二个参数是焦距的角度,第四个参数是能看清的最近距离,第五个参数是能看清的最远距离
        GLU.gluPerspective(gl, 40, (float) width / height, 0.1f, 20.0f);

3、挪动观测方位

调整好了镜头的拍照参数,要不要再摆个POSE,来个花式摄影?比如用户跃上好几级台阶,居高临下拍摄;也可俯下身子,从下向上拍摄;还能把手机横过来拍或者倒过来拍。要是怕摄影家累坏了,不妨叫摆拍的模特自己挪动身影,或者走进或者走远,往左靠一点或者往右靠一点,还可以躺下来甚至倒立过来。

因此,不管是挪动相机的位置,还是挪动物体的位置,都会让照片里的景物发生变化。挪动相机的位置,依靠的是GL10的gluLookAt方法;至于挪动物体的位置,依靠的则是旋转方法glRotatef、平移方法glTranslatef,以及缩放方法glScalef了。下面是OpenGL挪动相机位置的方法调用代码:

        // 选择模型观察矩阵,对应gluLookAt(人动)、glTranslatef/glScalef/glRotatef(物动)
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        // 重置模型矩阵,即去掉所有的位置挪动操作
        gl.glLoadIdentity();

        // 设置镜头的方位。第二到第四个参数为相机的位置坐标,第五到第七个参数为相机画面中心点的坐标,第八到第十个参数为朝上的坐标方向,比如第八个参数为1表示x轴朝上,第九个参数为1表示y轴朝上,第十个参数为1表示z轴朝上
        GLU.gluLookAt(gl, 10.0f, 8.0f, 6.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);

注意到前面调整相机参数和挪动相机位置这两个动作,都事先调用了glMatrixMode与glLoadIdentity方法,这是什么缘故呢?其实这两个方法结合起来只不过是状态重置操作,好比把手机恢复出厂设置,接下来重新进行状态设置。glMatrixMode方法的参数指定了重置操作的类型,像GL10.GL_PROJECTION类型涵盖了所有的镜头参数调整方法,包括gluPerspective(调整相机参数)、glFrustumf(调整透视投影)、glOrthof(调整正投影)三种方法,每次重置GL10.GL_PROJECTION类型,意味着之前的这三种参数设置统统失效。而GL10.GL_MODELVIEW类型涵盖的是位置变换的相关方法,包括挪动相机的gluLookAt方法,以及挪动物体的glTranslatef/glScalef/glRotatef方法,每次重置GL10.GL_MODELVIEW类型,意味着之前的位置挪动统统失效。

现在了解了以上的三维绘图的常见方法,接下来再看OpenGL的应用代码就会比较轻松了。先来看看一个最简单的三维立方体是如何实现的,下面是OpenGL绘制立方体的代码例子片段:

public class GlCubeActivity extends Activity {
   
   
    private final static String TAG = "GlCubeActivity";
    private GLSurfaceView glsv_content;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
   
   
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gl_cube);
        initVertexs(); // 初始化立方体的顶点集合,具体说明详见下一篇文章
        glsv_content = (GLSurfaceView) findViewById(R.id.glsv_content);
        // 注册渲染器
        glsv_content.setRenderer(new GLRender());
    }
    private class GLRender implements GLSurfaceView.Renderer {
   
   
        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
   
   
            // 背景:白色
            gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
            // 启动阴影平滑
            gl.glShadeModel(GL10.GL_SMOOTH);
        }
        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
   
   
            // 设置输出屏幕大小
            gl.glViewport(0, 0, width, height);
            // 设置投影矩阵,对应gluPerspective(调整相机)、glFrustumf(调整透视投影)、glOrthof(调整正投影)
            gl.glMatrixMode(GL10.GL_PROJECTION);
            // 重置投影矩阵,即去掉所有的平移、缩放、旋转操作
            gl.glLoadIdentity();
            // 设置透视图视窗大小
            GLU.gluPerspective(gl, 40, (float) width / height, 0.1f, 20.0f);
            // 选择模型观察矩阵,对应gluLookAt(人动)、glTranslatef/glScalef/glRotatef(物动)
            gl.glMatrixMode(GL10.GL_MODELVIEW);
            // 重置模型矩阵
            gl.glLoadIdentity();
        }
        @Override
        public void onDrawFrame(GL10 gl) {
   
   
            // 清除屏幕和深度缓存
            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
            // 重置当前的模型观察矩阵
            gl.glLoadIdentity();
            // 设置画笔颜色
            gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
            // 设置镜头的方位
            GLU.gluLookAt(gl, 10.0f, 8.0f, 6.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
            // 旋转图形
            //gl.glRotatef(angle, 0, 0, -1);
            //gl.glRotatef(angle, 0, -1, 0);
            // 沿x轴方向移动1个单位
            //gl.glTranslatef(1, 0, 0);
            // x,y,z方向缩放0.1倍
            //gl.glScalef(0.1f, 0.1f, 0.1f);
            // 绘制一个立方体,具体说明详见下一篇文章
            drawCube(gl);
        }
    }
}
目录
相关文章
|
2天前
|
编解码 Java Android开发
通义灵码:在安卓开发中提升工作效率的真实应用案例
本文介绍了通义灵码在安卓开发中的应用。作为一名97年的聋人开发者,我在2024年Google Gemma竞赛中获得了冠军,拿下了很多项目竞赛奖励,通义灵码成为我的得力助手。文章详细展示了如何安装通义灵码插件,并通过多个实例说明其在适配国际语言、多种分辨率、业务逻辑开发和编程语言转换等方面的应用,显著提高了开发效率和准确性。
|
1天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
11 5
|
3天前
|
存储 IDE 开发工具
探索Android开发之旅:从新手到专家
【10月更文挑战第26天】在这篇文章中,我们将一起踏上一段激动人心的旅程,探索如何在Android平台上从零开始,最终成为一名熟练的开发者。通过简单易懂的语言和实际代码示例,本文将引导你了解Android开发的基础知识、关键概念以及如何实现一个基本的应用程序。无论你是编程新手还是希望扩展你的技术栈,这篇文章都将为你提供价值和启发。让我们开始吧!
|
9天前
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
34 5
|
7天前
|
设计模式 IDE Java
探索安卓开发:从新手到专家的旅程
【10月更文挑战第22天】 在数字时代的浪潮中,移动应用开发如同一座金矿,吸引着无数探险者。本文将作为你的指南针,指引你进入安卓开发的广阔天地。我们将一起揭开安卓平台的神秘面纱,从搭建开发环境到掌握核心概念,再到深入理解安卓架构。无论你是初涉编程的新手,还是渴望进阶的开发者,这段旅程都将为你带来宝贵的知识和经验的财富。让我们开始吧!
|
16天前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
63 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
7天前
|
搜索推荐 Android开发 UED
安卓开发中的自定义视图:打造个性化用户界面
【10月更文挑战第22天】在安卓应用的海洋中,如何让你的应用脱颖而出?一个独特且直观的用户界面(UI)至关重要。本文将引导你通过自定义视图来打造个性化的用户体验,从基础的视图绘制到触摸事件的处理,我们将一步步深入探讨。准备好了吗?让我们开始吧!
|
8天前
|
Android开发
我是一位Android工程师,用通义灵码的AS插件做开发工作助手,对比之前没有灵码,现在提效了60%
我是一位Android工程师,用通义灵码的AS插件做开发工作助手,对比之前没有灵码,现在提效了60%
20 0
|
21天前
|
消息中间件 存储 前端开发
资深Android开发的5个经典面试题
本文首发于公众号“AntDream”,欢迎关注。文章详细解答了五个常见的Android面试题,涵盖内存泄漏与溢出、Binder机制、MVC/MVP/MVVM架构、Handler机制及Context对象等内容,帮助读者深入了解Android开发的核心概念。
29 0
|
21天前
|
存储 安全 Android开发
探索Android开发之旅:从新手到专家的蜕变之路
【10月更文挑战第8天】在这篇文章中,我们将共同踏上一段激动人心的旅程,深入探索Android开发的奥秘。无论你是初涉编程世界的新手,还是渴望提升技能的开发者,这里都有你需要的知识与启示。通过简洁明了的语言和实际案例,我们将一起解锁Android开发的核心概念、掌握关键技能,并最终实现从新手到专家的华丽转变。

推荐镜像

更多