【Android App】给三维的地球仪贴上动物贴纸实战(附源码和演示 超详细必看)

news2025/1/20 12:05:28

需要源码和图片集请点赞关注收藏后评论区留言~~~

一、纹理贴图

给三维物体穿衣服的动作,通常叫做给三维图形贴图,更专业地说叫纹理渲染。 渲染纹理的过程主要由三大项操作组成,分别说明如下:

(1)启用纹理的一系列开关设置

(2)计算材质的纹理坐标

(3)在三维图形上根据纹理点坐标逐个贴上对应的材质

三维物体的股价是通过三维坐标系表示的,每个点都有x,y,z三个方向上的坐标值,三维物体的纹理也需要通过纹理坐标来表达,但是纹理坐标并非三维形式而是二维形式,把三维的曲面剪开,然后摊平就可以得到平面式的坐标,因此纹理坐标的目的就是标记被摊平一副的二维坐标,从而将同属二维坐标系的一块块贴上去。

在OpenGL体系中,纹理坐标又称为UV坐标,通过两个浮点数组合来设置一个点的纹理坐标,其中U表示横轴,V表示纵轴,纹理坐标不关心物体的三维位置

实战效果如下 在三维地球仪的各个地方贴上了每个半球标志性的动物

南半球标志性动物企鹅~~

西半球标志性动物如下 

 

东半球标志性动物如下 

 

北半球标志性动物北极熊~~~ 

 

最后可以实现地球仪的转到功能,上面的动物贴纸也随着地球仪一起转动~~~ 

 

 代码如下

Java类

package com.example.threed;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;

import androidx.appcompat.app.AppCompatActivity;

import com.example.threed.util.GlVertexUtil;

import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class GlGlobeActivity extends AppCompatActivity {
    private GLSurfaceView glsv_content; // 声明一个图形库表面视图对象
    private Bitmap mBitmap; // 声明一个位图对象
    private int mType; // 地球仪的类型
    private int mDivide = 40; // 将经纬度等分的面数
    private int mRadius = 4; // 球半径
    private int mAngle = 0; // 旋转角度
    private List<FloatBuffer> mVertexList = new ArrayList<>(); // 顶点列表
    private List<FloatBuffer> mTextureCoords = new ArrayList<>(); // 纹理列表

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gl_globe);
        initTypeSpinner(); // 初始化类型下拉框
        // 从资源文件中获取平面世界地图的位图对象
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.mokatuo);
        // 计算球面顶点坐标
        mVertexList = GlVertexUtil.getBallVertexs(mDivide, mRadius);
        // 计算球面材质坐标
        mTextureCoords = GlVertexUtil.getTextureCoords(mDivide);
        glsv_content = findViewById(R.id.glsv_content);
        // 给OpenGL的表面视图注册三维图形的渲染器
        glsv_content.setRenderer(new GlobeRender());
        // 设置渲染模式。默认的RENDERMODE_CONTINUOUSLY表示持续刷新,RENDERMODE_WHEN_DIRTY表示只有首次创建和调用requestRender方法时才会刷新
        glsv_content.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
        //glsv_content.requestRender(); // 主动请求渲染操作
    }

    // 初始化类型下拉框
    private void initTypeSpinner() {
        ArrayAdapter<String> typeAdapter = new ArrayAdapter<>(this,
                R.layout.item_select, typeArray);
        Spinner sp_type = findViewById(R.id.sp_type);
        sp_type.setPrompt("请选择球体贴图类型");
        sp_type.setAdapter(typeAdapter);
        sp_type.setOnItemSelectedListener(new TypeSelectedListener());
        sp_type.setSelection(0);
    }

    private String[] typeArray = {"东半球", "西半球", "北半球", "南半球", "转动地球仪"};
    class TypeSelectedListener implements AdapterView.OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            mType = arg2;
            if (mType == 4) {
                glsv_content.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); // 设置渲染模式
            } else {
                mAngle = 0;
                glsv_content.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); // 设置渲染模式
                glsv_content.requestRender(); // 主动请求渲染操作
            }
        }

        public void onNothingSelected(AdapterView<?> arg0) {}
    }

    // 定义一个三维图形的渲染器
    private class GlobeRender 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); // 启用阴影平滑
            // 启用某功能,对应的glDisable是禁用某功能
            // GL_DEPTH_TEST用来开启更新深度缓冲区的功能,也就是,如果通过比较后深度值发生变化了,
            // 会进行更新深度缓冲区的操作。一旦启用它,OpenGL就可以跟踪在Z轴上的像素,
            // 这样,它只会在那个像素前方没有东西时,才会绘制这个像素。
            // 在绘制三维图形时,这个功能最好启动,视觉效果比较真实。
            gl.glEnable(GL10.GL_DEPTH_TEST);
            // 除了深度测试,还可以开启以下功能
            //gl.glEnable(GL10.GL_LIGHTING); // 开启灯照效果
            //gl.glEnable(GL10.GL_LIGHT0); // 启用光源0
            //gl.glEnable(GL10.GL_COLOR_MATERIAL); // 启用颜色追踪
            gl.glEnable(GL10.GL_TEXTURE_2D); // 启用纹理。启用之后才能贴图
            // 使用OpenGL库创建一个材质(Texture),首先要获取一个材质编号(保存在textures中)
            int[] textures = new int[1];
            gl.glGenTextures(1, textures, 0); // 生成材质编号
            gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]); // 通知OpenGL绑定这个材质编号
            // 材质纹理的尺寸可能大于或小于渲染区域,所以要设置纹理在放大或缩小时的模式
            // GL_TEXTURE_MAG_FILTER表示放大的情况,GL_TEXTURE_MIN_FILTER表示缩小的情况
            // 常用的两种模式为GL10.GL_LINEAR和GL10.GL_NEAREST。
            // 使用GL_NEAREST会得到较清晰的图像,使用GL_LINEAR会得到较模糊的图像
            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);
            // 当定义的材质坐标点超过UV坐标的区域范围(UV坐标为0,0到1,1),就要告诉OpenGL如何渲染这些不存在的纹理
            // GL_TEXTURE_WRAP_S表示水平方向,GL_TEXTURE_WRAP_T表示垂直方向
            // 有两种设置:GL_REPEAT表示重复Texture,GL_CLAMP_TO_EDGE表示只靠边线绘制一次
            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
            // 将位图Bitmap和纹理Texture绑定起来,即指定一个具体的材质资源
            GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, mBitmap, 0);
        }

        // 在表面变更时触发
        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            gl.glViewport(0, 0, width, height); // 设置输出屏幕大小
            gl.glMatrixMode(GL10.GL_PROJECTION); // 设置投影矩阵
            gl.glLoadIdentity(); // 重置投影矩阵
            // 设置透视图视窗大小。第二个参数是视角,越大则视野越广;第三个参数是宽高比
            // 第四个参数表示眼睛距离物体最近处的距离;第五个参数表示眼睛距离物体最远处的距离
            // gluPerspective和gluLookAt需要配合使用,才能调节观察到的物体大小
            GLU.gluPerspective(gl, 8, (float) width / (float) height, 0.1f, 100.0f);
            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(); // 重置当前的模型观察矩阵
            // 下面根据类型设置不同的观测点
            if (mType == 0 || mType == 4) { // 东半球
                GLU.gluLookAt(gl, 0.0f, 0.0f, 70.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
            } else if (mType == 1) { // 西半球
                GLU.gluLookAt(gl, 0.0f, 0.0f, -70.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
            } else if (mType == 2) { // 北半球
                GLU.gluLookAt(gl, 0.0f, 70.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
            } else if (mType == 3) { // 南半球
                GLU.gluLookAt(gl, 0.0f, -70.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
            }
            gl.glRotatef(mAngle, 0, 1, 0); // 设置旋转角度,转动地球仪
            mAngle++;
            drawGlobe(gl); // 绘制地球仪
        }
    }

    // 绘制地球仪
    private void drawGlobe(GL10 gl) {
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); // 启用材质开关
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); // 启用顶点开关
        for (int i = 0; i <= mDivide; i++) {
            // 将顶点坐标传给 OpenGL 管道
            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexList.get(i));
            // 声明纹理点坐标
            gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureCoords.get(i));
            // GL_LINE_STRIP只绘制线条,GL_TRIANGLE_STRIP才是画三角形的面
            gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, mDivide * 2 + 2);
        }
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); // 禁用顶点开关
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); // 禁用材质开关
    }

}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="right"
            android:text="球体贴图类型:"
            android:textColor="@color/black"
            android:textSize="17sp" />

        <Spinner
            android:id="@+id/sp_type"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:spinnerMode="dialog" />
    </LinearLayout>

    <!-- 注意这里要使用控件的全路径android.opengl.GLSurfaceView -->

    <android.opengl.GLSurfaceView
        android:id="@+id/glsv_content"
        android:layout_width="match_parent"
        android:layout_height="400dp"/>

</LinearLayout>

创作不易 觉得有帮助请点赞关注收藏~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/44022.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STL的常用遍历算法(20221128)

STL的常用算法 概述&#xff1a; 算法主要是由头文件<algorithm> <functional> <numeric> 组成。 <algorithm>是所有STL头文件中最大的一个&#xff0c;涉及比较、交换、查找、遍历等等&#xff1b; <functional>定义了一些模板类&#xff0…

2022VR高级研修班总结

本人有幸参加2022VR高级研修班&#xff0c;此次高级研修班由赵沁平院士和丁文华院士领衔&#xff0c;全国知名专家及长期在相关领域从事产业、管理、科研工作的专家参与&#xff0c;带来了18个专题讲座&#xff0c;内容涵盖虚拟现实技术与系统现状与发展、产学研合作与产业协同…

都什么年代了,你居然还连不上GitHub?

前言 众所周知&#xff0c;GitHub是我们程序员在上班或者学习的时候经常会逛的一个地方[手动狗头]&#xff0c;而且如果我们想参与开源项目的话&#xff0c;GitHub也是一个很好的平台。 可问题是&#xff0c;GitHub网页总是进不去&#xff0c;提交代码到GitHub也总是超时&…

在Navicat上如何停止正在运行的MYSQL语句

目录 &#xff08;一&#xff09;前言 &#xff08;二&#xff09;正文 1. 图形化停止SQL 2. 用SQL方式停止运行的SQL &#xff08;1&#xff09;找到运行的SQL的ID &#xff08;2&#xff09;运行kill命令杀掉SQL &#xff08;一&#xff09;前言 众所周知&#xff0c;…

BI数据分析软件有哪些?为什么说奥威BI很特别?

经过十几年的发展&#xff0c;以及近几年国家的大力鼓励发展大数据智能产业等原因&#xff0c;BI数据分析软件开始越来越被大家所熟知&#xff0c;那么BI数据分析软件都有哪些&#xff0c;为什么说奥威BI数据分析软件很特别&#xff1f;它对企业的大数据智能数据可视化分析起到…

什么样的人最适合做软件测试---喜欢找人帮忙办事的人

今天和大家说下什么样类型的人适合做软件测试。 经验干货&#xff0c;可仔细品 很多测试工程师面试中也可能会遇到问题&#xff0c;说怎么做一名优秀合格的测试工程师&#xff0c;需要有哪些品质&#xff0c;很多人会回答说要仔细&#xff0c;要承受压力&#xff0c;要有责任感…

2013-2020年全国31省数字经济数据集

1、时间&#xff1a;2013-2020年 2、来源&#xff1a;整理自国家统计J和统计NJ 3、指标包括&#xff1a; 信息化基础&#xff1a;"光缆线路长度(公里)、移动电话基站&#xff08;万个&#xff09;、信息传输、软件和信息技术服务业城镇单位就业人员(万人)、年末常住人口…

Android-P夜间模式

0 前言 Android-P可通过“Developer options > Night mode”打开夜间模式&#xff0c;本文研究其配置和效果。 验证环境&#xff1a;Pixel 3a、模拟器 1 设置 图1.1 Night mode上图对应XML配置如下&#xff1a; <ListPreferenceandroid:key"dark_ui_mode"and…

【Python】六、程序流程控制综合训练

文章目录实验目的一、列表定义二、元组定义三、列表的常用操作append() -- 向列表的尾部添加元素insert(index&#xff0c;object) -- 向指定的下标处添加元素sort&#xff08;&#xff09;-- 进行排序&#xff08;从下到大 int类型&#xff09;可以对字母进行排序&#xff08;…

多态原理、虚表指针与虚基表的内存布局。

文章目录前言多态虚函数静态类型与动态类型重载、覆盖和隐藏的区别final和override虚函数的默认实参虚析构函数在构造函数和析构函数中调用虚函数多态原理各种形式继承的虚函数内存布局单一继承无覆盖单一继承有覆盖单一虚拟继承有覆盖多继承无覆盖多继承有覆盖菱形继承有覆盖菱…

STC/MLLT--学习笔记

gmm建模方差使用对角矩阵的前提是假设特征之间相互独立&#xff0c;使用full或者block-diagonal矩阵可以对相关性的特征建模&#xff0c;但是参数增多。为了解决使用这个问题&#xff0c;有两种方法&#xff1a; feature-space 使用DCT或者LDA去相关model-space 不同的模型可以…

连续时间系统的时域分析

一.微分方程的求解 1.求微分方程的齐次解 &#xff08;1&#xff09;写出特征方程并求解 2.写出齐次解 2.求微分方程的特解 已知 &#xff08;1&#xff09;根据表2-2&#xff0c;写出特解函数 ​​​​​​​ &#xff08;2&#xff09;带入并求解 3.完全解 二.微分方…

基于GRU与注意力机制实现法语-葡萄牙语的翻译详细教程 数据+代码

本教程通过机器翻译的例子来介绍和实现一个简单的机器翻译方法,机器翻译是指将一段文本从源语言(如语言A)自动翻译到目标语言(如语言B)。本教程通过加载和预处理数据、构造编码器和解码器、训练模型、结果评价得到一个可以应用的机器翻译工具。 1.2 任务描述 神经机器翻译方…

uni-app 介绍及使用

一、什么是uni-app uni-app由dcloud公司开发的多端融合框架&#xff0c;是一个使用 Vue.js 开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到iOS、Android、Web&#xff08;响应式&#xff09;、以及各种小程序&#xff08;微信/支付宝/百度/头条/飞…

单文件组件环境配置步骤---vue-cli版

因为浏览器只认识&#xff1a;html、css、js文件&#xff0c;其他一概不认识&#xff1b; 所以要把单文件组件的vue文件转化为上面浏览器能认识的文件&#xff1b; 有两种环境配置途径&#xff1a; 第一种就是&#xff1a;配置webpack环境&#xff0c;要下载很多东西&#x…

灰色预测GM(1.1)模型及matlab程序负荷预测

灰色GM(1.1)预测模型 GM&#xff08;1.1&#xff09;模型由包含单一变量的一阶微分方程构成的模型&#xff0c;是灰色模型中最常用的模型。 设有负荷变量为的原始数据列: (3-1) 生成一阶累加数据列&#xff1a; (3-2) 其中 (3-3) 一阶微分方程的解呈指数增长形式&#xff0c…

URLDNS利用链分析

目录 前言&#xff1a; (一&#xff09;原理 &#xff08;二&#xff09;利用链 再来分析 URLDNS.java 这个文件&#xff0c;并且在入口处设置断点进行调试&#xff1a; &#xff08;三&#xff09; POC 参考资料 前言&#xff1a; URLDNS是Java反序列化中比较简单的一个链…

引擎入门 | Unity UI简介–第2部分(1)

欢迎回来! 在这个三部分教程系列的第二部分中&#xff0c;你将学习如何在用户界面中加入动画。 在上一个部分中你学习并创建了一个带有两个按钮的场景&#xff0c;也学会了如何使用图像、按钮和文本UI控件&#xff0c;并学习了RectTransform、Anchors和Pivots等核心概念&#…

元宇宙的核心技术之我见

14天学习训练营导师课程&#xff1a; 张子良《 元宇宙体系结构、关键技术和实践探索》 前言 提起元宇宙&#xff0c;相比读者都有所耳闻&#xff0c;而且元宇宙最近两年时间里异常的火&#xff0c;堪比之前的人工智能的火爆场景&#xff0c;甚至要超越人工智能的火爆度了。但是…

kubernetes namespace pod label deployment介绍与命令

kubernetes namespace pod label deployment 介绍与命令 1&#xff1a; namespace Namespace是kubernetes系统中的一种非常重要资源&#xff0c;它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。 默认情况下&#xff0c;kubernetes集群中的所有的Pod都是可以…