个人
登录后您可以获得
加入子社区
评论/点赞/收藏
互动提问讨论
参与专属活动
立即登录

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);
AI 代码解读

二、三维坐标系

三维空间用来表达立体形状,需要三个方向的坐标,分别为水平方向的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);
AI 代码解读

三、坐标矩阵变换

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

1、设置绘图区域

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

        // 设置输出屏幕大小
        gl.glViewport(0, 0, width, height);
AI 代码解读

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);
AI 代码解读

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);
AI 代码解读

注意到前面调整相机参数和挪动相机位置这两个动作,都事先调用了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);
        }
    }
}
AI 代码解读
aqi00
+关注
目录
打赏
0
1
1
0
71
分享
相关文章
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
85 13
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
79 19
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
70 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
66 7
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
188 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
62 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
133 12
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
escrcpy:【技术党必看】Android开发,Escrcpy 让你无线投屏新体验!图形界面掌控 Android,30-120fps 超流畅!🔥
escrcpy 是一款基于 Scrcpy 的开源项目,使用 Electron 构建,提供图形化界面来显示和控制 Android 设备。它支持 USB 和 Wi-Fi 连接,帧率可达 30-120fps,延迟低至 35-70ms,启动迅速且画质清晰。escrcpy 拥有丰富的功能,包括自动化任务、多设备管理、反向网络共享、批量操作等,无需注册账号或广告干扰。适用于游戏直播、办公协作和教育演示等多种场景,是一款轻量级、高性能的 Android 控制工具。
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
47 1
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
89 19