1 前言

​ 本文通过一个立方体贴图的例子,讲解三维纹理贴图的应用,案例中使用 6 张不同的图片给立方体贴图,图片如下:

​ 本文涉及到的知识点主要包含:三维绘图、MVP 矩阵变换、纹理贴图,读者如果对 OpenGL ES 不太熟悉,请回顾以下内容:

​ 本文完整代码资源见→ 【OpenGL ES】立方体贴图(6张图)

​ 项目目录如下:

2 案例

​ MainActivity.java

package com.zhyan8.texture3d;

import android.opengl.GLSurfaceView;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity {
private GLSurfaceView mGlSurfaceView; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGlSurfaceView = new MyGLSurfaceView(this);
setContentView(mGlSurfaceView);
mGlSurfaceView.setRenderer(new MyRender(this));
} @Override
protected void onResume() {
super.onResume();
mGlSurfaceView.onResume();
} @Override
protected void onPause() {
super.onPause();
mGlSurfaceView.onPause();
}
}

​ MyGLSurfaceView.java

package com.zhyan8.texture3d;

import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet; public class MyGLSurfaceView extends GLSurfaceView {
public MyGLSurfaceView(Context context) {
super(context);
setEGLContextClientVersion(3);
} public MyGLSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
setEGLContextClientVersion(3);
}
}

​ MyRender.java

package com.zhyan8.texture3d;

import android.content.Context;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10; public class MyRender implements GLSurfaceView.Renderer {
private FloatBuffer vertexBuffer;
private FloatBuffer textureBuffer;
private MyGLUtils mGLUtils;
private int mProgramId;
private float mRatio;
private int[] mTextureIds; public MyRender(Context context) {
mGLUtils = new MyGLUtils(context);
} @Override
public void onSurfaceCreated(GL10 gl, EGLConfig eglConfig) {
//设置背景颜色
GLES30.glClearColor(0.1f, 0.2f, 0.3f, 0.4f);
//启动深度测试
gl.glEnable(GLES30.GL_DEPTH_TEST);
//编译着色器
final int vertexShaderId = mGLUtils.compileShader(GLES30.GL_VERTEX_SHADER, R.raw.vertex_shader);
final int fragmentShaderId = mGLUtils.compileShader(GLES30.GL_FRAGMENT_SHADER, R.raw.fragment_shader);
//链接程序片段
mProgramId = mGLUtils.linkProgram(vertexShaderId, fragmentShaderId);
GLES30.glUseProgram(mProgramId);
mTextureIds = mGLUtils.loadTexture(Model.resIds);
} @Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//设置视图窗口
GLES30.glViewport(0, 0, width, height);
getFloatBuffer();
mRatio = 1.0f * width / height;
} @Override
public void onDrawFrame(GL10 gl) {
//将颜色缓冲区设置为预设的颜色
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);
mGLUtils.transform(mProgramId, mRatio); //计算MVP变换矩阵
//启用顶点的数组句柄
GLES30.glEnableVertexAttribArray(0);
GLES30.glEnableVertexAttribArray(1);
//准备顶点坐标和纹理坐标
GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer);
GLES30.glVertexAttribPointer(1, 2, GLES30.GL_FLOAT, false, 0, textureBuffer);
//激活纹理
GLES30.glActiveTexture(GLES30.GL_TEXTURE);
//绘制纹理
drawTextures();
//禁止顶点数组句柄
GLES30.glDisableVertexAttribArray(0);
GLES30.glDisableVertexAttribArray(1);
} private void getFloatBuffer() {
vertexBuffer = mGLUtils.getFloatBuffer(Model.vertex);
textureBuffer = mGLUtils.getFloatBuffer(Model.texture);
} private void drawTextures() {
int count = 4;
for (int i =0; i < mTextureIds.length; i++) {
int first = i * count;
//绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureIds[i]);
//绘制正方体的表面(6个面,每个面2个三角形)
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, first, count);
}
}
}

​ GLUtils.java

package com.zhyan8.texture3d;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES30;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer; public class MyGLUtils {
private Context mContext;
private int mRotateAgree = 0; public MyGLUtils(Context context) {
mContext = context;
} public FloatBuffer getFloatBuffer(float[] floatArr) {
FloatBuffer fb = ByteBuffer.allocateDirect(floatArr.length * Float.BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
fb.put(floatArr);
fb.position(0);
return fb;
} //通过代码片段编译着色器
public int compileShader(int type, String shaderCode){
int shader = GLES30.glCreateShader(type);
GLES30.glShaderSource(shader, shaderCode);
GLES30.glCompileShader(shader);
return shader;
} //通过外部资源编译着色器
public int compileShader(int type, int shaderId){
String shaderCode = readShaderFromResource(shaderId);
return compileShader(type, shaderCode);
} //链接到着色器
public int linkProgram(int vertexShaderId, int fragmentShaderId) {
final int programId = GLES30.glCreateProgram();
//将顶点着色器加入到程序
GLES30.glAttachShader(programId, vertexShaderId);
//将片元着色器加入到程序
GLES30.glAttachShader(programId, fragmentShaderId);
//链接着色器程序
GLES30.glLinkProgram(programId);
return programId;
} //从shader文件读出字符串
private String readShaderFromResource(int shaderId) {
InputStream is = mContext.getResources().openRawResource(shaderId);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
StringBuilder sb = new StringBuilder();
try {
while ((line = br.readLine()) != null) {
sb.append(line);
sb.append("\n");
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
} //加载纹理贴图
public int[] loadTexture(int[] resIds) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap[] bitmaps = new Bitmap[resIds.length];
// 生成纹理id
final int[] textureIds = new int[resIds.length];
GLES30.glGenTextures(resIds.length, textureIds, 0);
for (int i = 0; i < resIds.length; i++) {
bitmaps[i] = BitmapFactory.decodeResource(mContext.getResources(), resIds[i], options);
// 绑定纹理到OpenGL
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[i]);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR_MIPMAP_LINEAR);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
// 加载bitmap到纹理中
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmaps[i], 0);
// 生成MIP贴图
GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D);
// 取消绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
bitmaps[i].recycle();
}
return textureIds;
} //计算MVP变换矩阵
public void transform(int programId, float ratio) {
//初始化modelMatrix, viewMatrix, projectionMatrix
float[] modelMatrix = getIdentityMatrix(16, 0); //模型变换矩阵
float[] viewMatrix = getIdentityMatrix(16, 0); //观测变换矩阵
float[] projectionMatrix = getIdentityMatrix(16, 0); //投影变换矩阵
//获取modelMatrix, viewMatrix, projectionMatrix
mRotateAgree = (mRotateAgree + 2) % 360;
Matrix.rotateM(modelMatrix, 0, mRotateAgree, -1, -1, 1); //获取模型旋转变换矩阵
Matrix.setLookAtM(viewMatrix, 0, 0, 5, 10, 0, 0, 0, 0, 1, 0); //获取观测变换矩阵
Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1, 1, 3, 20); //获取投影变换矩阵
//计算MVP变换矩阵: mvpMatrix = projectionMatrix * viewMatrix * modelMatrix
float[] tempMatrix = new float[16];
float[] mvpMatrix = new float[16];
Matrix.multiplyMM(tempMatrix, 0, viewMatrix, 0, modelMatrix, 0);
Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, tempMatrix, 0);
//设置MVP变换矩阵
int mvpMatrixHandle = GLES30.glGetUniformLocation(programId, "mvpMatrix");
GLES30.glUniformMatrix4fv(mvpMatrixHandle, 1, false, mvpMatrix, 0);
} private float[] getIdentityMatrix(int size, int offset) {
float[] matrix = new float[size];
Matrix.setIdentityM(matrix, offset);
return matrix;
}
}

​ Model.java

package com.zhyan8.texture3d;

public class Model {
private static float r = 1.0f; public static int[] resIds = new int[] {
R.raw.x1, R.raw.x2, R.raw.x3,
R.raw.x4, R.raw.x5, R.raw.x6
}; public static float[] vertex = new float[] {
//前面
r, r, r,
-r, r, r,
-r, -r, r,
r, -r, r,
//后面
r, r, -r,
-r, r, -r,
-r, -r, -r,
r, -r, -r,
//上面
r, r, r,
r, r, -r,
-r, r, -r,
-r, r, r,
//下面
r, -r, r,
r, -r, -r,
-r, -r, -r,
-r, -r, r,
//右面
r, r, r,
r, r, -r,
r, -r, -r,
r, -r, r,
//左面
-r, r, r,
-r, r, -r,
-r, -r, -r,
-r, -r, r
}; public static float[] texture = new float[] {
//前面
0f, 0f,
1f, 0f,
1f, 1f,
0f, 1f,
//后面
0f, 0f,
1f, 0f,
1f, 1f,
0f, 1f,
//上面
0f, 0f,
1f, 0f,
1f, 1f,
0f, 1f,
//下面
0f, 0f,
1f, 0f,
1f, 1f,
0f, 1f,
//右面
0f, 0f,
1f, 0f,
1f, 1f,
0f, 1f,
//左面
0f, 0f,
1f, 0f,
1f, 1f,
0f, 1f
};
}

​ vertex_shader.glsl

#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec2 aTextureCoord;
uniform mat4 mvpMatrix;
out vec2 vTexCoord;
void main() {
gl_Position = mvpMatrix * vPosition;
vTexCoord = aTextureCoord;
}

​ 顶点着色器的作用:进行矩阵变换位置、根据光照公式计算顶点颜⾊⽣成 / 变换纹理坐标,并且把位置和纹理坐标发送到片元着色器。

​ 顶点着色器中,如果没有指定默认精度,则 int 和 float 的默认精度都为 highp。

​ fragment_shader.glsl

#version 300 es
precision mediump float;
uniform sampler2D uTextureUnit;
in vec2 vTexCoord;
out vec4 fragColor;
void main() {
fragColor = texture(uTextureUnit,vTexCoord);
}

​ 片元着色器的作用:处理经光栅化阶段生成的每个片元,计算每个像素的颜色和透明度。

​ 在片元着色器中,浮点值没有默认的精度值,每个着色器必须声明一个默认的 float 精度。

运行效果:

​ 声明:本文转自【OpenGL ES】立方体贴图(6张图)

【OpenGL ES】立方体贴图(6张图)的更多相关文章

  1. OpenGL ES课程VI之纹理贴图(原文对照)

    http://www.educity.cn/wenda/92368.html OpenGL ES教程VI之纹理贴图(原文对照) OpenGL ES Tutorial for Android – Par ...

  2. (转)OpenGL学习——立方体贴图

    转自:https://learnopengl-cn.readthedocs.io/zh/latest/04%20Advanced%20OpenGL/06%20Cubemaps/ 我们之前一直使用的是2 ...

  3. 基于Cocos2d-x学习OpenGL ES 2.0系列——纹理贴图(6)

    在上一篇文章中,我们介绍了如何绘制一个立方体,里面涉及的知识点有VBO(Vertex Buffer Object).IBO(Index Buffer Object)和MVP(Modile-View-P ...

  4. SQL知识点脑图(一张图总结SQL)

    sql语言的分类DDL:create drop alter DML:insert delete update DCL:rollback grant revoke commit 概要,主外键,视图,索引 ...

  5. 在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping)

    在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping) 视差贴图 最近一直在研究如何在我的 iPad 2(只支持 OpenGL ES 2.0, 不支持 3.0) 上实现 视 ...

  6. OpenGL ES 2.0 渲染管线 学习笔记

    图中展示整个OpenGL ES 2.0可编程管线 图中Vertex Shader和Fragment Shader 是可编程管线: Vertex Array/Buffer objects 顶点数据来源, ...

  7. Xcode OpenGL ES Frame Capture的使用

    一.使用背景 近期在Xcode中使用OpenGL ES 2.0实现一些效果,刚开始存在一些性能问题(CPU和GPU),幸运的是Xcode中自带了免费的性能工具Instruments,其中包含OpenG ...

  8. OpenGL+OpenCV实现立方体贴图

    我屮艸芔茻,转眼就7月份了. 今天试了一下立方体贴图,比较简单,大概说下和平面贴图的区别. 1. 平面贴图需要的是纹理坐标vec2:立方体贴图需要的是一个方向向量vec3,长度没有关系,重要的是方向, ...

  9. OpenGL 核心技术之立方体贴图

    笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家.特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D ...

  10. 2.x最终照着教程,成功使用OpenGL ES 绘制纹理贴图,添加了灰度图

    在之前成功绘制变色的几何图形之后,今天利用Openg ES的可编程管线绘制出第一张纹理. 学校时候不知道OpenGL的重要性,怕晦涩的语法.没有跟老师学习OpenGL的环境配置,现在仅仅能利用coco ...

随机推荐

  1. Linux系统下安装JDK8和Maven3.8.5

    一.下载JDK8Linux版本 官网下载太慢了,小编这里为大家下载好了: 链接:百度网盘地址 提取码:ov24 二.下载Maven maven3.8.5下载链接 三.使用xftp上传到linux上 四 ...

  2. phpcms - 获取单网页 , 例如关于我们

       {pc:get sql="select * from phpcms_page where catid=2" num="1"}         {loop  ...

  3. 基本操作Linux

    基本操作Linux 关机,重启# 关机 shutdown -h now # 重启 shutdown -r now 查看系统,CPU信息# 查看系统内核信息 uname -a # 查看系统内核版本 ca ...

  4. [转帖]能使 Oracle 索引失效的六大限制条件

    Oracle 索引的目标是避免全表扫描,提高查询效率,但有些时候却适得其反. 例如一张表中有上百万条数据,对某个字段加了索引,但是查询时性能并没有什么提高,这可能是 oracle 索引失效造成的.or ...

  5. [转帖]一个小技巧解决笔记本HDMI接口失灵

      https://baijiahao.baidu.com/s?id=1738289993804283647&wfr=spider&for=pc 现如今笔记本的接口是越来越多,哪怕是标 ...

  6. vim配置的学习与总结

    摘要 有时候在linux里面操作需要使用粘贴以及其他处理. 很多发行版, 遇到 # 会自动退格, 很难受. 想着能够处理一下vim的默认设置可以好很多. 所以这里简单总结一下. 配置文件 vim的配置 ...

  7. OpenPower机器上面搭建RabbitMQ 以及简单进行用户配置的方法

    OpenPower机器上面搭建RabbitMQ 以及简单进行用户配置的方法 公司有一台性能比较好的power机器. 同事要求安装rabbitmq 今天尝试进行了一下处理 公司里面有网络有相应的源 性能 ...

  8. charles如何抓取https请求

    我们都知道charles下载安装后只能抓取http请求,要想抓取https请求需要下载安装证书 下面介绍pc端和移动端的配置方法 一.pc端(win) 1.打开charles,点击help>SS ...

  9. MySQL查询聚合函数与分组查询

    连接数据库 mysql -hlocalhost -uroot -proot 聚合函数 聚合函数:作用于某一列,对数据进行计算. ps: 所有的null值是不参与聚合函数的运算的. 06 常见的聚合函数 ...

  10. 【NSSCTF-Round#16】 Web和Crypto详细完整WP

    每天都要加油哦!    ------2024-01-18  11:16:55 [NSSRound#16 Basic]RCE但是没有完全RCE <?php error_reporting(0); ...