【OpenGL ES】立方体贴图(6张图)
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张图)的更多相关文章
- OpenGL ES课程VI之纹理贴图(原文对照)
http://www.educity.cn/wenda/92368.html OpenGL ES教程VI之纹理贴图(原文对照) OpenGL ES Tutorial for Android – Par ...
- (转)OpenGL学习——立方体贴图
转自:https://learnopengl-cn.readthedocs.io/zh/latest/04%20Advanced%20OpenGL/06%20Cubemaps/ 我们之前一直使用的是2 ...
- 基于Cocos2d-x学习OpenGL ES 2.0系列——纹理贴图(6)
在上一篇文章中,我们介绍了如何绘制一个立方体,里面涉及的知识点有VBO(Vertex Buffer Object).IBO(Index Buffer Object)和MVP(Modile-View-P ...
- SQL知识点脑图(一张图总结SQL)
sql语言的分类DDL:create drop alter DML:insert delete update DCL:rollback grant revoke commit 概要,主外键,视图,索引 ...
- 在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping)
在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping) 视差贴图 最近一直在研究如何在我的 iPad 2(只支持 OpenGL ES 2.0, 不支持 3.0) 上实现 视 ...
- OpenGL ES 2.0 渲染管线 学习笔记
图中展示整个OpenGL ES 2.0可编程管线 图中Vertex Shader和Fragment Shader 是可编程管线: Vertex Array/Buffer objects 顶点数据来源, ...
- Xcode OpenGL ES Frame Capture的使用
一.使用背景 近期在Xcode中使用OpenGL ES 2.0实现一些效果,刚开始存在一些性能问题(CPU和GPU),幸运的是Xcode中自带了免费的性能工具Instruments,其中包含OpenG ...
- OpenGL+OpenCV实现立方体贴图
我屮艸芔茻,转眼就7月份了. 今天试了一下立方体贴图,比较简单,大概说下和平面贴图的区别. 1. 平面贴图需要的是纹理坐标vec2:立方体贴图需要的是一个方向向量vec3,长度没有关系,重要的是方向, ...
- OpenGL 核心技术之立方体贴图
笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家.特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D ...
- 2.x最终照着教程,成功使用OpenGL ES 绘制纹理贴图,添加了灰度图
在之前成功绘制变色的几何图形之后,今天利用Openg ES的可编程管线绘制出第一张纹理. 学校时候不知道OpenGL的重要性,怕晦涩的语法.没有跟老师学习OpenGL的环境配置,现在仅仅能利用coco ...
随机推荐
- 0xGame 2023【WEEK2】Crypto全解
中间的那个人 题目信息 from secret import flag from Crypto.Util.number import * from Crypto.Cipher import AES f ...
- [转帖]Linux内核线程kthread简介【最好的一篇!】
https://zhuanlan.zhihu.com/p/581587583 Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求).内核需要多个执行流并行,为了 ...
- [转帖]在龙芯3A5000上测试SPEC CPU 2006
https://baijiahao.baidu.com/s?id=1707601012673143593&wfr=spider&for=pc 注:百家号中,一些文本.代码等的排版格式无 ...
- [转帖]使用 Crash 工具分析 Linux dump 文件
前言 Linux 内核(以下简称内核)是一个不与特定进程相关的功能集合,内核的代码很难轻易的在调试器中执行和跟踪.开发者认为,内核如果发生了错误,就不应该继续运行.因此内核发生错误时,它的行为通常被设 ...
- Booking.com如何在毫秒内搜索数百万个地点
译自:How Booking.com Searches Through Millions of Locations in Milliseconds Booking.com是一家与酒店.旅馆.度假租赁等 ...
- CTT 总结
Day 1 T1 肝 4 个半小时,主要是前面各种假,中途各种改.出来听说 T2 才是签子.但是 T2 只写了 15 分暴力.100+15+0=115. Day 2 T1 肝 4 个小时,但是 8 点 ...
- vue3封装搜索表单组件
seacrch 表单完成的功能 1.根据配置json配置项自动生成表单 ok 2.是响应式的排版 ok 3.点击搜索按钮会向上抛出值 ok 4.点击重置按钮会自动清空数据,不需要父组件额外的处理 ok ...
- 人均瑞数系列,瑞数 4 代 JS 逆向分析
声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容.敏感网址.数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 本文章未经许 ...
- [置顶] ES篇
环境 第一篇: ES--介绍.安装 第二篇: ES--安装nodejs 第三篇: ES--插件 第四篇: ES--kibana介绍.安装 第五篇: ES--中文分词介绍.下载 第六篇: docker, ...
- C# Switch优雅写法
1 private static bool CanBeUpdateOrDel(bool 是否提交, bool 是否撤回, string 审核状态) => (是否提交, 是否撤回, 审核状态) s ...