【Android App】给三维魔方贴图以及旋转动画讲解和实战(附源码和演示视频 超详细必看)

简介: 【Android App】给三维魔方贴图以及旋转动画讲解和实战(附源码和演示视频 超详细必看)

需要源码请点赞关注收藏后评论区留言私信~~~

一、给三维物体贴图

除了位置坐标、颜色向量和矩阵向量,着色器还支持声明材质坐标和纹理采样器。 其中材质坐标表示物体表面摊平后的二维空间,纹理采样器表示采用第几层的纹理单元。引入纹理指标之后,需要修改顶点着色器的GLSL文件,补充声明纹理坐标的输入参数以及输出参数

接着还要修改片段着色器的GLSL文件,补充声明纹理坐标的输入参数以及纹理采样器的全局变量,并调用texture方法生成片元颜色

然后根据小程序编号在指定的材质坐标上应用纹理,注意末尾调用glGetUniformLocation方法获取采样器的位置序号之后还要调用glUniformli方法传入采样器纹理单元的编号

回到活动页面,在绘制图形之前调用以上的bindImageTexture方法绑定图像纹理

运行测试效果如下

演示视频已上传至个人主页 有需要可自行前往观看

Android魔方贴图旋转动画

代码如下

Java类

package com.example.threed;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import com.example.threed.util.GlUtil;
import com.example.threed.util.EsVertexUtil;
import java.nio.ByteBuffer;
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 EsTextureActivity extends AppCompatActivity {
    private final static String TAG = "EsTextureActivity";
    private GLSurfaceView glsv_content; // 声明一个图形库表面视图对象
    private List<FloatBuffer> mVertexList = new ArrayList<>(); // 顶点列表
    private ByteBuffer mIndicateBuffer; // 示意连线的字节缓存
    private int mAngle = 60; // 旋转角度
    private int mProgramId; // 声明glsl小程序的编号
    private Bitmap mMagicBitmap; // 魔方的位图
    private float[] mProjectionMatrix = new float[16]; // 投影矩阵
    private float[] mModelMatrix = new float[16]; // 模型矩阵
    private float[] mMVPMatrix = new float[16]; // 结果矩阵
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_es_texture);
        initShapeSpinner(); // 初始化形状下拉框
        mVertexList = EsVertexUtil.getCubeVertexs(); // // 获得立方体的顶点列表
        mIndicateBuffer = GlUtil.getByteBuffer(EsVertexUtil.cubeIndicates);
        glsv_content = findViewById(R.id.glsv_content);
        // 声明使用OpenGL ES的版本号为3.0。使用ES30方法之前务必指定版本号
        glsv_content.setEGLContextClientVersion(3);
        // 给OpenGL的表面视图注册三维图形的渲染器
        glsv_content.setRenderer(new TextureRenderer());
        // 设置渲染模式。默认的RENDERMODE_CONTINUOUSLY表示持续刷新,RENDERMODE_WHEN_DIRTY表示只有首次创建和调用requestRender方法时才会刷新
        glsv_content.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }
    // 初始化形状下拉框
    private void initShapeSpinner() {
        ArrayAdapter<String> shapeAdapter = new ArrayAdapter<>(this,
                R.layout.item_select, shapeArray);
        Spinner sp_shape = findViewById(R.id.sp_shape);
        sp_shape.setPrompt("请选择三维物体形状");
        sp_shape.setAdapter(shapeAdapter);
        sp_shape.setOnItemSelectedListener(new EsTextureActivity.ShapeSelectedListener());
        sp_shape.setSelection(0);
    }
    private String[] shapeArray = { "静止魔方", "旋转魔方" };
    class ShapeSelectedListener implements AdapterView.OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            mMagicBitmap = null;
            if (arg2 == 1) {
                glsv_content.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); // 设置渲染模式
            } else {
                glsv_content.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); // 设置渲染模式
                glsv_content.requestRender(); // 主动请求渲染操作
            }
        }
        public void onNothingSelected(AdapterView<?> arg0) {}
    }
    public class TextureRenderer implements GLSurfaceView.Renderer {
        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            GLES30.glClearColor(1f, 1f, 1f, 1f); //设置背景颜色
            // 初始化着色器
            mProgramId = GlUtil.initShaderProgram(EsTextureActivity.this, "texture_vertex.glsl", "texture_fragment.glsl");
        }
        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            GLES30.glViewport(0, 0, width, height); // 设置输出屏幕大小
            float aspectRatio = width>height ? 1.0f*width/height : 1.0f*height/width;
            Matrix.setIdentityM(mProjectionMatrix, 0); // 初始化投影矩阵
            // 计算矩阵的正交投影
            Matrix.orthoM(mProjectionMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f);
        }
        @Override
        public void onDrawFrame(GL10 gl) {
            // 清除屏幕和深度缓存
            GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);
            // 开启深度测试,这样只绘制靠前的表面,不绘制靠后的表面
            GLES30.glEnable(GLES30.GL_DEPTH_TEST);
            Matrix.setIdentityM(mModelMatrix, 0); // 初始化模型矩阵
            Matrix.rotateM(mModelMatrix, 0, mAngle,1f, 1f, 0.5f); // 旋转模型矩阵
            // 把投影矩阵和模型矩阵相乘,得到最终的变换矩阵
            Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mModelMatrix, 0);
            // 获取顶点着色器的unMatrix位置
            int matrixLoc = GLES30.glGetUniformLocation(mProgramId, "unMatrix");
            Log.d(TAG, "matrixLoc="+matrixLoc);
            // 输入变换矩阵信息
            GLES30.glUniformMatrix4fv(matrixLoc, 1, false, mMVPMatrix, 0);
            mAngle++;
            drawMagic(); // 绘制魔方
        }
    }
    // 绘制魔方
    private void drawMagic() {
        if (mMagicBitmap == null) {
            mMagicBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.magic);
            FloatBuffer textureBuffer = GlUtil.getFloatBuffer(EsVertexUtil.cubeTextCoord);
            // 绑定魔方的图像纹理
            int textureId = GlUtil.bindImageTexture(mProgramId, textureBuffer, mMagicBitmap);
            Log.d(TAG, "textureId="+textureId);
        }
        // 获取顶点着色器的vPosition位置
        int positionLoc = GLES30.glGetAttribLocation(mProgramId, "vPosition");
        Log.d(TAG, "positionLoc="+positionLoc);
        GLES30.glEnableVertexAttribArray(positionLoc); // 启用顶点属性数组
        for (FloatBuffer buffer : mVertexList) {
            // 指定顶点属性数组的信息
            GLES30.glVertexAttribPointer(positionLoc, 3, GLES30.GL_FLOAT, false, 0, buffer);
            // 假设绘制一个立方体,绘制类型为GL_TRIANGLES,那么六个面各由两个三角形组成,
            // 就得向渲染管线传入36个顶点依次绘制,可实际上一个矩形也只有4个顶点,
            // 为了优化绘制的效率,减少数据的传递,于是有了glDrawElements绘制方法。
            GLES30.glDrawElements(GLES30.GL_TRIANGLES, EsVertexUtil.cubeIndicates.length,
                    GLES30.GL_UNSIGNED_BYTE, mIndicateBuffer);
        }
        GLES30.glDisableVertexAttribArray(positionLoc); // 禁用顶点属性数组
    }
    @Override
    protected void onPause() {
        super.onPause();
        glsv_content.onPause(); // 暂停绘制三维图形
        mMagicBitmap = null;
        mAngle = 60;
    }
    @Override
    protected void onResume() {
        super.onResume();
        glsv_content.onResume(); // 恢复绘制三维图形
    }
}

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_shape"
            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>

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

相关文章
|
6天前
|
存储 监控 API
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
|
6月前
|
XML 自然语言处理 Android开发
🌐Android国际化与本地化全攻略!让你的App走遍全球无障碍!🌍
【7月更文挑战第28天】在全球化背景下,实现Android应用的国际化与本地化至关重要 for 用户基础扩展。本文通过旅游指南App案例,介绍全攻略。步骤包括资源文件拆分与命名、适配布局与方向、处理日期时间及货币格式、考虑文化习俗及进行详尽测试。采用Android Studio支持,创建如`res/values-en/strings.xml`等多语言资源文件夹,使用灵活布局解决文本长度差异问题,并通过用户反馈迭代优化。最终,打造一款能无缝融入全球各地文化的App。
227 3
|
4月前
|
开发工具
uniapp, 短剧视频类App实现参考,支持滑动播放,仿抖音 仿陌陌 短视频 无限滑动播放 视频流
阿里云点播服务web播放器sdk,短剧视频类App实现参考。仿抖音 仿陌陌 短视频 无限滑动播放 视频流。无uniapp video 原生组件的层级、遮挡、覆盖问题,适合与不同功能视图组合使用,实现丰富的应用功能。
uniapp, 短剧视频类App实现参考,支持滑动播放,仿抖音 仿陌陌 短视频 无限滑动播放 视频流
|
6月前
|
消息中间件 Android开发 开发者
🔍深度剖析Android内存泄漏,让你的App远离崩溃边缘,稳如老狗!🐶
【7月更文挑战第28天】在 Android 开发中,内存管理至关重要。内存泄漏可悄无声息地累积,最终导致应用崩溃或性能下滑。它通常由不正确地持有 Activity 或 Fragment 的引用引起。常见原因包括静态变量持有组件引用、非静态内部类误用、Handler 使用不当、资源未关闭及集合对象未清理。使用 Android Studio Profiler 和 LeakCanary 可检测泄漏,修复方法涉及使用弱引用、改用静态内部类、妥善管理 Handler 和及时释放资源。良好的内存管理是保证应用稳定性的基石。
123 4
|
6月前
|
XML 缓存 Android开发
🎯解锁Android性能优化秘籍!让你的App流畅如飞,用户爱不释手!🚀
【7月更文挑战第28天】在移动应用竞争中,性能是关键。掌握Android性能优化技巧对开发者至关重要。
48 2
|
5月前
|
编解码 监控 API
惊艳登场!揭秘如何在Android平台上轻松玩转GB28181标准,实现多视频通道接入的超实用指南!
【8月更文挑战第14天】GB28181是公共安全视频监控联网的技术标准。本文介绍如何在Android平台上实现该标准下的多视频通道接入。首先准备开发环境,接着引入GB28181 SDK依赖并初始化SDK。实现设备注册与登录后,通过指定不同通道号请求多路视频流。最后,处理接收到的数据并显示给用户。此过程涉及视频解码,需确保应用稳定及良好的用户体验。
113 0
|
6月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的成人教育APP附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的成人教育APP附带文章源码部署视频讲解等
62 2
|
6月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的房屋租赁App附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的房屋租赁App附带文章源码部署视频讲解等
60 1
|
6月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的多功能智能手机阅读APP附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的多功能智能手机阅读APP附带文章源码部署视频讲解等
89 1
|
6月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的宠物饲养管理APP附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的宠物饲养管理APP附带文章源码部署视频讲解等
39 1