iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >Android OpenGL如何实现APP裸眼3D效果
  • 351
分享到

Android OpenGL如何实现APP裸眼3D效果

2023-06-28 23:06:41 351人浏览 八月长安
摘要

Android OpenGL如何实现APP裸眼3D效果,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。原理简介 & OpenGL 的优势裸眼 3D 效果的本质是

Android OpenGL如何实现APP裸眼3D效果,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

    原理简介 & OpenGL 的优势

    裸眼 3D 效果的本质是——将整个图片结构分为 3 层:上层、中层、以及底层。在手机左右上下旋转时,上层和底层的图片呈相反的方向进行移动,中层则不动,在视觉上给人一种 3D 的感觉:

    Android OpenGL如何实现APP裸眼3D效果

    也就是说效果是由以下三张图构成的:

    Android OpenGL如何实现APP裸眼3D效果

    接下来,如何感应手机的旋转状态,并将三层图片进行对应的移动呢?当然是使用设备自身提供各种各样优秀的传感器了,通过传感器不断回调获取设备的旋转状态,对 UI 进行对应地渲染即可。

    笔者最终选择了 Android 平台上的 OpenGL api 进行渲染,直接的原因是,无需将社区内已有的实现方案重复照搬。

    另一个重要的原因是,GPU 更适合图形、图像的处理,裸眼3D效果中有大量的缩放和位移操作,都可在 java 层通过一个 矩阵 对几何变换进行描述,通过 shader 小程序中交给 GPU 处理 ——因此,理论上 OpenGL 的渲染性能比其它几个方案更好一些。

    本文重点是描述 OpenGL 绘制时的思路描述,因此下文仅展示部分核心代码。

    具体实现

    1. 绘制静态图片

    首先需要将3张图片依次进行静态绘制,这里涉及大量 OpenGL API 的使用,不熟悉的读可略读本小节,以捋清思路为主。

    首先看一下顶点和片元着色器的 shader 代码,其定义了图像纹理是如何在GPU中处理渲染的:

    // 顶点着色器代码// 顶点坐标attribute vec4 av_Position;// 纹理坐标attribute vec2 af_Position;unifORM mat4 u_Matrix;varying vec2 v_texPo;void main() {    v_texPo = af_Position;    gl_Position =  u_Matrix * av_Position;}
    // 顶点着色器代码// 顶点坐标attribute vec4 av_Position;// 纹理坐标attribute vec2 af_Position;uniform mat4 u_Matrix;varying vec2 v_texPo;void main() {    v_texPo = af_Position;    gl_Position =  u_Matrix * av_Position;}

    定义好了 Shader ,接下来在 GLSurfaceView (可以理解为 OpenGL 中的画布) 创建时,初始化Shader小程序,并将图像纹理依次加载到GPU中:

    public class My3DRenderer implements GLSurfaceView.Renderer {    @Override  public void onSurfaceCreated(GL10 gl, EGLConfig config) {      // 1.加载shader小程序      mProgram = loadShaderWithResource(              mContext,              R.raw.projection_vertex_shader,              R.raw.projection_fragment_shader      );            // ...             // 2. 依次将3张切图纹理传入GPU      this.texImageInner(R.drawable.bg_3d_back, mBackTextureId);      this.texImageInner(R.drawable.bg_3d_mid, mMidTextureId);      this.texImageInner(R.drawable.bg_3d_fore, mFrontTextureId);  }}

    接下来是定义视口的大小,因为是2D图像变换,且切图和手机屏幕的宽高比基本一致,因此简单定义一个单位矩阵的正交投影即可:

    public class My3DRenderer implements GLSurfaceView.Renderer {      // 投影矩阵    private float[] mProjectionMatrix = new float[16];    @Override    public void onSurfaceChanged(GL10 gl, int width, int height) {        // 设置视口大小,这里设置全屏        GLES20.glViewport(0, 0, width, height);        // 图像和屏幕宽高比基本一致,简化处理,使用一个单位矩阵        Matrix.setIdentityM(mProjectionMatrix, 0);    }}

    最后就是绘制,读者需要理解,对于前、中、后三层图像的渲染,其逻辑是基本一致的,差异仅仅有2点:图像本身不同 以及 图像的几何变换不同

    public class My3DRenderer implements GLSurfaceView.Renderer {      private float[] mBackMatrix = new float[16];    private float[] mMidMatrix = new float[16];    private float[] mFrontMatrix = new float[16];    @Override    public void onDrawFrame(GL10 gl) {        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);        GLES20.glUseProgram(mProgram);                // 依次绘制背景、中景、前景        this.drawLayerInner(mBackTextureId, mTextureBuffer, mBackMatrix);        this.drawLayerInner(mMidTextureId, mTextureBuffer, mMidMatrix);        this.drawLayerInner(mFrontTextureId, mTextureBuffer, mFrontMatrix);    }        private void drawLayerInner(int textureId, FloatBuffer textureBuffer, float[] matrix) {        // 1.绑定图像纹理        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);        // 2.矩阵变换        GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, matrix, 0);        // ...        // 3.执行绘制        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);    }}

    参考 drawLayerInner 的代码,其用于绘制单层的图像,其中 textureId 参数对应不同图像,matrix 参数对应不同的几何变换。

    现在我们完成了图像静态的绘制,效果如下:

    Android OpenGL如何实现APP裸眼3D效果

    接下来我们需要接入传感器,并定义不同层级图片各自的几何变换,让图片动起来。

    2. 让图片动起来

    首先我们需要对 Android 平台上的传感器进行注册,监听手机的旋转状态,并拿到手机 xy 轴的旋转角度。

    // 2.1 注册传感器mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);MacceleSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);mMagneticSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);mSensorManager.reGISterListener(mSensorEventListener, mAcceleSensor, SensorManager.SENSOR_DELAY_GAME);mSensorManager.registerListener(mSensorEventListener, mMagneticSensor, SensorManager.SENSOR_DELAY_GAME);// 2.2 不断接受旋转状态private final SensorEventListener mSensorEventListener = new SensorEventListener() {    @Override    public void onSensorChanged(SensorEvent event) {        // ... 省略具体代码        float[] values = new float[3];        float[] R = new float[9];        SensorManager.getRotationMatrix(R, null, mAcceleValues, mMageneticValues);        SensorManager.getOrientation(R, values);        // x轴的偏转角度        float degreeX = (float) Math.toDegrees(values[1]);        // y轴的偏转角度        float degreeY = (float) Math.toDegrees(values[2]);        // z轴的偏转角度        float degreeZ = (float) Math.toDegrees(values[0]);                // 拿到 xy 轴的旋转角度,进行矩阵变换        updateMatrix(degreeX, degreeY);    }};

    注意,因为我们只需控制图像的左右和上下移动,因此,我们只需关注设备本身 x 轴和 y 轴的偏转角度:

    Android OpenGL如何实现APP裸眼3D效果

    拿到了 x 轴和 y 轴的偏转角度后,接下来开始定义图像的位移了。

    但如果将图片直接进行位移操作,将会因为位移后图像的另一侧没有纹理数据,导致渲染结果有黑边现象,为了避免这个问题,我们需要将图像默认从中心点进行放大,保证图像移动的过程中,不会超出自身的边界。

    也就是说,我们一开始进入时,看到的肯定只是图片的部分区域。给每一个图层设置 scale,将图片进行放大。显示窗口是固定的,那么一开始只能看到图片的正中位置。(中层可以不用,因为中层本身是不移动的,所以也不必放大)

    Android OpenGL如何实现APP裸眼3D效果

    明白了这一点,我们就能理解,裸眼3D的效果实际上就是对 不同层级的图像 进行缩放和位移的变换,下面是分别获取几何变换的代码:

    public class My3DRenderer implements GLSurfaceView.Renderer {      private float[] mBackMatrix = new float[16];    private float[] mMidMatrix = new float[16];    private float[] mFrontMatrix = new float[16];        private void updateMatrix(@FloatRange(from = -180.0f, to = 180.0f) float degreeX,                              @FloatRange(from = -180.0f, to = 180.0f) float degreeY) {        // ... 其它处理                                                        // 背景变换        // 1.最大位移量        float maxTransXY = MAX_VISIBLE_SIDE_BACKGROUND - 1f;        // 2.本次的位移量        float transX = ((maxTransXY) / MAX_TRANS_DEGREE_Y) * -degreeY;        float transY = ((maxTransXY) / MAX_TRANS_DEGREE_X) * -degreeX;        float[] backMatrix = new float[16];        Matrix.setIdentityM(backMatrix, 0);        Matrix.translateM(backMatrix, 0, transX, transY, 0f);                    // 2.平移        Matrix.scaleM(backMatrix, 0, SCALE_BACK_GROUND, SCALE_BACK_GROUND, 1f);  // 1.缩放        Matrix.multiplyMM(mBackMatrix, 0, mProjectionMatrix, 0, backMatrix, 0);  // 3.正交投影        // 中景变换        Matrix.setIdentityM(mMidMatrix, 0);        // 前景变换        // 1.最大位移量        maxTransXY = MAX_VISIBLE_SIDE_FOREGROUND - 1f;        // 2.本次的位移量        transX = ((maxTransXY) / MAX_TRANS_DEGREE_Y) * -degreeY;        transY = ((maxTransXY) / MAX_TRANS_DEGREE_X) * -degreeX;        float[] frontMatrix = new float[16];        Matrix.setIdentityM(frontMatrix, 0);        Matrix.translateM(frontMatrix, 0, -transX, -transY - 0.10f, 0f);            // 2.平移        Matrix.scaleM(frontMatrix, 0, SCALE_FORE_GROUND, SCALE_FORE_GROUND, 1f);    // 1.缩放        Matrix.multiplyMM(mFrontMatrix, 0, mProjectionMatrix, 0, frontMatrix, 0);  // 3.正交投影    }}

    这段代码中还有几点细节需要处理。

    3. 几个反直觉的细节

    1 旋转方向 ≠ 位移方向

    首先,设备旋转方向和图片的位移方向是相反的,举例来说,当设备沿 X 轴旋转,对于用户而言,对应前后景的图片应该上下移动,反过来,设备沿 Y 轴旋转,图片应该左右移动(没太明白的同学可参考上文中陀螺仪的图片加深理解):

    // 设备旋转方向和图片的位移方向是相反的float transX = ((maxTransXY) / MAX_TRANS_DEGREE_Y) * -degreeY;float transY = ((maxTransXY) / MAX_TRANS_DEGREE_X) * -degreeX;// ...Matrix.translateM(backMatrix, 0, transX, transY, 0f);

    2 默认旋转角度 ≠ 0°

    其次,在定义最大旋转角度的时候,不能主观认为旋转角度 = 0°是默认值。什么意思呢?Y 轴旋转角度为0°,即 degreeY = 0 时,默认设备左右的高度差是 0,这个符合用户的使用习惯,相对易于理解,因此,我们可以定义左右的最大旋转角度,比如 Y ∈ (-45°,45°),超过这两个旋转角度,图片也就移动到边缘了。

    但当 X 轴旋转角度为0°,即 degreeX = 0 时,意味着设备上下的高度差是 0,你可以理解为设备是放在水平的桌面上的,这个绝不符合大多数用户的使用习惯,相比之下,设备屏幕平行于人的面部 才更适用大多数场景(degreeX = -90):

    Android OpenGL如何实现APP裸眼3D效果

    因此,代码上需对 X、Y 轴的最大旋转角度区间进行分开定义:

    private static final float USER_X_AXIS_STANDARD = -45f;private static final float MAX_TRANS_DEGREE_X = 25f;   // X轴最大旋转角度 ∈ (-20°,-70°)private static final float USER_Y_AXIS_STANDARD = 0f;private static final float MAX_TRANS_DEGREE_Y = 45f;   // Y轴最大旋转角度 ∈ (-45°,45°)

    解决了这些 反直觉 的细节问题,我们基本完成了裸眼3D的效果。

    4. 帕金森综合征?

    还差一点就大功告成了,最后还需要处理下3D效果抖动的问题:

    Android OpenGL如何实现APP裸眼3D效果

    如图,由于传感器过于灵敏,即使平稳的握住设备,XYZ 三个方向上微弱的变化都会影响到用户的实际体验,会给用户带来 帕金森综合征 的自我怀疑。

    解决这个问题,传统的 OpenGL 以及 Android API 似乎都无能为力,好在 GitHub 上有人提供了另外一个思路。

    熟悉信号处理的同学比较了解,为了通过剔除短期波动、保留长期发展趋势提供了信号的平滑形式,可以使用 低通滤波器,保证低于截止频率的信号可以通过,高于截止频率的信号不能通过。

    因此有人建立了 这个仓库 , 通过对 Android 传感器追加低通滤波 ,过滤掉小的噪声信号,达到较为平稳的效果:

    private final SensorEventListener mSensorEventListener = new SensorEventListener() {    @Override    public void onSensorChanged(SensorEvent event) {        // 对传感器的数据追加低通滤波        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {            mAcceleValues = lowPass(event.values.clone(), mAcceleValues);        }        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {            mMageneticValues = lowPass(event.values.clone(), mMageneticValues);        }              // ... 省略具体代码        // x轴的偏转角度        float degreeX = (float) Math.toDegrees(values[1]);        // y轴的偏转角度        float degreeY = (float) Math.toDegrees(values[2]);        // z轴的偏转角度        float degreeZ = (float) Math.toDegrees(values[0]);                // 拿到 xy 轴的旋转角度,进行矩阵变换        updateMatrix(degreeX, degreeY);    }};

    大功告成,最终我们实现了预期的效果:

    Android OpenGL如何实现APP裸眼3D效果

    Android是什么

    Android是一种基于linux内核的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发

    看完上述内容,你们掌握Android OpenGL如何实现APP裸眼3D效果的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注编程网精选频道,感谢各位的阅读!

    --结束END--

    本文标题: Android OpenGL如何实现APP裸眼3D效果

    本文链接: https://www.lsjlt.com/news/321177.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档
    猜你喜欢
    • Android OpenGL如何实现APP裸眼3D效果
      Android OpenGL如何实现APP裸眼3D效果,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。原理简介 & OpenGL 的优势裸眼 3D 效果的本质是...
      99+
      2023-06-28
    • AndroidOpenGL仿自如APP裸眼3D效果详解
      目录原理简介 & OpenGL 的优势具体实现1. 绘制静态图片2. 让图片动起来3. 几个反直觉的细节4. 帕金森综合征?源码原理简介 & OpenGL 的优势 裸...
      99+
      2024-04-02
    • 详解Android 裸眼3D效果View控件
      描述:这是一个裸眼3D效果的控件View。 Tips:本项目代码部分逻辑参考于其他文章(自如的3D裸眼实现),众人拾柴火焰高,希望大家能多多补充。 项目代码:https://gite...
      99+
      2024-04-02
    • 在小程序中如何实现3d裸眼轮播效果
      小编今天带大家了解在小程序中如何实现3d裸眼轮播效果,文中知识点介绍的非常详细。觉得有帮助的朋友可以跟着小编一起浏览文章的内容,希望能够帮助更多想解决这个问题的朋友找到问题的答案,下面跟着小编一起深入学习“在小程序中如何实现3d裸眼轮播效果...
      99+
      2023-06-26
    • Android Flutter如何实现3D动画效果
      这篇文章主要讲解了“Android Flutter如何实现3D动画效果”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Android Flutter如何实现3D动画效果”吧...
      99+
      2023-06-29
    • Android实现3D滚动效果
      先上效果图 下载链接http://down.51cto.com/data/1076318...
      99+
      2023-01-31
      效果 Android
    • OpenGL Shader如何实现阴影遮罩效果
      小编给大家分享一下OpenGL Shader如何实现阴影遮罩效果,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!smoothstep另一种用法在之前Ope...
      99+
      2023-06-29
    • Android怎么实现3D界面效果
      要实现3D界面效果,可以通过以下几种方法:1. 使用OpenGL ES:Android支持OpenGL ES库,可以使用OpenGL...
      99+
      2023-08-24
      Android
    • OpenGL Shader如何实现简单转场效果
      这篇文章主要介绍了OpenGL Shader如何实现简单转场效果,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。渐变转场通过mix函数混合两个纹理图像,使用time在...
      99+
      2023-06-29
    • Android集成Unity,实现3D看房效果
      引子 前几天有人找小编问能不能把3D模型放入到Unity中,再把Unity放入到Android APP中,在APP中实现观看房屋家具的功能,这次小编便来分享一下吧,如果还需要了解Android 集成Unity知识的,可以翻我主页其他文章 ...
      99+
      2023-08-31
      android unity 3d
    • css3如何实现3d翻转效果
      今天小编给大家分享的是css3如何实现3d翻转效果,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会有所收获的哦。什么是csscss是一种用来表现HTML或XML等文件样式的计算机语言,主要是用来设计...
      99+
      2023-06-14
    • js如何实现3d悬浮效果
      这篇文章主要介绍了js如何实现3d悬浮效果,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。效果如下:代码如下:<!DOCTYPE&nbs...
      99+
      2024-04-02
    • CSS3如何实现3D翻书效果
      小编给大家分享一下CSS3如何实现3D翻书效果,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!先上效果图:(样式有点丑,可以忽略一下下,效果出来了就好,后期加到其他项目中方便更改0.0)类似...
      99+
      2024-04-02
    • vue+highcharts如何实现3D饼图效果
      这篇文章给大家分享的是有关vue+highcharts如何实现3D饼图效果的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。这是最终效果<template><div class="...
      99+
      2023-06-29
    • vue如何实现3D环形图效果
      这篇文章主要介绍“vue如何实现3D环形图效果”,在日常操作中,相信很多人在vue如何实现3D环形图效果问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”vue如何实现3D环形图效果”的疑惑有所帮助!接下来,请跟...
      99+
      2023-06-29
    • 如何使用OpenGL Shader实现彩虹条纹效果
      小编给大家分享一下如何使用OpenGL Shader实现彩虹条纹效果,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!前言在一款图片处理软件colorow中发现彩虹效果滤镜感觉蛮有意思。在OpenGL Shader系...
      99+
      2023-06-29
    • 纯css如何实现照片墙3D效果
      小编给大家分享一下纯css如何实现照片墙3D效果,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!准备材料:准备材料就是没什么材料,自己麻溜赶快的去:百度找几张美女的照片来测试即可。html代码:<!DOCTYPE&nb...
      99+
      2023-06-08
    • 如何使用css实现过渡+3D效果
      这篇文章主要为大家展示了“如何使用css实现过渡+3D效果”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何使用css实现过渡+3D效果”这篇文章吧。css过渡...
      99+
      2024-04-02
    • CSS3如何实现3D开门动画效果
      这篇文章将为大家详细讲解有关CSS3如何实现3D开门动画效果,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。 这篇文章将为大家详细讲解有关CSS3如何实现3D开门动画效果,...
      99+
      2024-04-02
    • CSS如何实现卡片3D翻转效果
      这篇文章将为大家详细讲解有关CSS如何实现卡片3D翻转效果,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。效果:代码:html:<div class="main">...
      99+
      2023-06-08
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作