一、首先看一张效果图
立方体贴图
二、纹理坐标划分
如上图是一张2D纹理,我们需要将这个2D纹理贴到立方体上,立方体有6个面,所以上面的2D图分成了6个面,共有14个纹理坐标
三、立方体
上边的立方体一共8个顶点坐标,范围是[-1, 1];
我们要做的是将纹理图贴到这6个面上面
四、顶点坐标纹理坐标关联
我们绘制的时候使用了VBO、VAO、EBO、
vertices里面是纹理坐标和顶点坐标的对应关系,纹理贴到哪个顶点上面;纹理坐标一共十四个,贴到8个顶点上。
indices里面是绘制的12个三角形
float DP = 0.5f;
/*顶点 纹理*/
float vertices[] = {-DP, -DP, DP, 0.25, 0.333, //0
DP, -DP, DP, 0.50, 0.333, //1
DP, DP, DP, 0.50, 0.666, //2
-DP, DP, DP, 0.25, 0.666, //3
-DP, -DP, -DP, 1.00, 0.333, //4
DP, -DP, -DP, 0.75, 0.333, //5
DP, DP, -DP, 0.75, 0.666, //6
-DP, DP, -DP, 1.00, 0.666, //7
-DP, -DP, -DP, 0.25, 0, //4 8
DP, -DP, -DP, 0.5, 0, //5 9
DP, DP, -DP, 0.5, 1, //6 10
-DP, DP, -DP, 0.25, 1, //7 11
-DP, -DP, -DP, 0, 0.333, //4 12
-DP, DP, -DP, 0, 0.666, //7 13
};
unsigned int indices[] = {
0, 1, 2, 0, 2, 3, // front
1, 2, 5, 2, 5, 6, // right
4, 5, 6, 4, 6, 7, // back
0, 3, 12, 3, 12, 13, // left
0, 1, 8, 1, 8, 9, // bottom
2, 3, 10, 3, 10, 11, // top
};
五、完整代码
有部分代码是测试用的,不用细究奇怪的逻辑
//
// Created by fengcheng.cai on 2023/12/15.
//
#define EGL_EGLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#include "com_sprd_opengl_test_MyNdk4.h"
#include <ggl.h>
#include <string.h>
#include <unistd.h>
#include <android/bitmap.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#define LOG_TAG "MyNdk4"
struct GL_Context4 {
GLint program;
EGLDisplay display;
EGLSurface winSurface;
EGLContext context;
ANativeWindow *nw;
AImageReader *reader;
};
static GL_Context4 gl_cxt;
static char *vertexSimpleShape = "#version 300 es\n"
"layout (location = 0) in vec4 aPosition;\n"
"layout (location = 1) in vec2 aTexCoord;\n"
"uniform mat4 u_MVPMatrix;\n"
"out vec2 TexCoord;\n"
"void main() {\n"
" gl_Position = u_MVPMatrix * aPosition;\n"
" TexCoord = aTexCoord;\n"
"}\n";
static char *fragSimpleShape = "#version 300 es\n"
" precision mediump float;\n"
" in vec2 TexCoord;\n"
" out vec4 FragColor;\n"
" uniform sampler2D ourTexture;\n"
" void main() {\n"
" FragColor = texture(ourTexture, TexCoord);\n"
" }\n";
static GLint initShader(const char *source, GLint type) {
//创建着色器对象,type表示着色器类型,比如顶点着色器为GL_VERTEX_SHADER,片段着色器为GL_FRAGMENT_SHADER。返回值为一个类似引用的数字。
GLint sh = glCreateShader(type);
if (sh == 0) {
//返回值sh为0则表示创建着色器失败
LOGD("glCreateShader %d failed", type);
return 0;
}
//着色器对象加载着色器对象代码source
glShaderSource(sh,
1,//shader数量
&source,
0);//代码长度,传0则读到字符串结尾
//编译着色器对象
glCompileShader(sh);
//以下为打印出编译异常信息
GLint status;
glGetShaderiv(sh, GL_COMPILE_STATUS, &status);
if (status == 0) {
LOGD("glCompileShader %d failed", type);
LOGD("source %s", source);
auto *infoLog = new GLchar[512];
GLsizei length;
glGetShaderInfoLog(sh, 512, &length, infoLog);
LOGD("ERROR::SHADER::VERTEX::COMPILATION_FAILED %s", infoLog);
return 0;
}
LOGD("glCompileShader %d success", type);
return sh;
}
JNIEXPORT void JNICALL Java_com_sprd_opengl_test_MyNdk4_init
(JNIEnv *env, jobject obj, jobject surface) {
// egl ------------------------------------------------------------------- start
LOGD("init");
ANativeWindow *nwin = ANativeWindow_fromSurface(env, surface);
gl_cxt.nw = nwin;
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) {
LOGD("egl display failed");
return;
}
if (EGL_TRUE != eglInitialize(display, 0, 0)) {
LOGD("eglInitialize failed");
return;
}
EGLConfig eglConfig;
EGLint configNum;
EGLint configSpec[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RECORDABLE_ANDROID, EGL_TRUE,
EGL_NONE
};
if (EGL_TRUE != eglChooseConfig(display, configSpec, &eglConfig, 1, &configNum)) {
LOGD("eglChooseConfig failed");
return;
}
EGLSurface winSurface = eglCreateWindowSurface(display, eglConfig, nwin, 0);
if (winSurface == EGL_NO_SURFACE) {
LOGD("eglCreateWindowSurface failed");
return;
}
const EGLint ctxAttr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLContext context = eglCreateContext(display, eglConfig, EGL_NO_CONTEXT, ctxAttr);
if (context == EGL_NO_CONTEXT) {
LOGD("eglCreateContext failed");
return;
}
if (EGL_TRUE != eglMakeCurrent(display, winSurface, winSurface, context)) {
LOGD("eglMakeCurrent failed");
return;
}
gl_cxt.display = display;
gl_cxt.winSurface = winSurface;
gl_cxt.context = context;
// egl ------------------------------------------------------------------- end
// shader ------------------------------------------------------------------- start
GLint vsh = initShader(vertexSimpleShape, GL_VERTEX_SHADER);
GLint fsh = initShader(fragSimpleShape, GL_FRAGMENT_SHADER);
GLint program = glCreateProgram();
if (program == 0) {
LOGD("glCreateProgram failed");
return;
}
glAttachShader(program, vsh);
glAttachShader(program, fsh);
glLinkProgram(program);
GLint status2 = 0;
glGetProgramiv(program, GL_LINK_STATUS, &status2);
if (status2 == 0) {
LOGD("glLinkProgram failed");
return;
}
gl_cxt.program = program;
LOGD("glLinkProgram success");
// shader ------------------------------------------------------------------- end
}
static void printMat4(glm::mat4 matrix) {
LOGD("\nll\n%f, %f, %f, %f\n%f, %f, %f, %f\n%f, %f, %f, %f\n%f, %f, %f, %f\n",
matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0],
matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1],
matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2],
matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]);
}
static void printVec4(glm::vec4 vec) {
LOGD("\nll\n%f, %f, %f, %f\n",
vec[0], vec[1], vec[2], vec[3]);
}
JNIEXPORT void JNICALL Java_com_sprd_opengl_test_MyNdk4_process
(JNIEnv *env, jobject obj, jobject bitmap, jint surfaceW, jint surfaceH) {
glUseProgram(gl_cxt.program);
AndroidBitmapInfo bitmapInfo;
if (AndroidBitmap_getInfo(env, bitmap, &bitmapInfo) < 0) {
LOGE("AndroidBitmap_getInfo() failed ! ");
return;
}
void *bmpPixels;
int width = bitmapInfo.width;
int height = bitmapInfo.height;
LOGD("process format: %d, stride: %d", bitmapInfo.format, bitmapInfo.stride);
AndroidBitmap_lockPixels(env, bitmap, &bmpPixels);
unsigned int textureId;
glGenTextures(1, &textureId);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
LOGD("process2 4");
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bmpPixels);
unsigned char *pOri = (unsigned char *)bmpPixels;
LOGD("process2 5 %d, %d, %d, %d, %d, %d, %d, %d", *(pOri), *(pOri+1), *(pOri+2), *(pOri+3),
*(pOri+114), *(pOri+115), *(pOri+116), *(pOri+117));
glBindTexture(GL_TEXTURE_2D, 0);
AndroidBitmap_unlockPixels(env, bitmap);
float DP = 0.5f;
/*顶点 纹理*/
float vertices[] = {-DP, -DP, DP, 0.25, 0.333, //0
DP, -DP, DP, 0.50, 0.333, //1
DP, DP, DP, 0.50, 0.666, //2
-DP, DP, DP, 0.25, 0.666, //3
-DP, -DP, -DP, 1.00, 0.333, //4
DP, -DP, -DP, 0.75, 0.333, //5
DP, DP, -DP, 0.75, 0.666, //6
-DP, DP, -DP, 1.00, 0.666, //7
-DP, -DP, -DP, 0.25, 0, //4 8
DP, -DP, -DP, 0.5, 0, //5 9
DP, DP, -DP, 0.5, 1, //6 10
-DP, DP, -DP, 0.25, 1, //7 11
-DP, -DP, -DP, 0, 0.333, //4 12
-DP, DP, -DP, 0, 0.666, //7 13
};
unsigned int indices[] = {
0, 1, 2, 0, 2, 3, // front
1, 2, 5, 2, 5, 6, // right
4, 5, 6, 4, 6, 7, // back
0, 3, 12, 3, 12, 13, // left
0, 1, 8, 1, 8, 9, // bottom
2, 3, 10, 3, 10, 11, // top
};
bool looper = true;
int count = 0;
float angleX = 0.0f;
float angleY = 0.0f;
float angleZ = 0.0f;
#define MAX_LEN 512
float near[MAX_LEN] = {0.0f};
for (int i = 0; i < MAX_LEN / 2; i++) {
near[i] = 1.0f + 1.0f * i / (MAX_LEN / 2);
}
for (int i = 0; i < MAX_LEN / 2; i++) {
near[i + MAX_LEN / 2] = 2.0f - 1.0f * i / (MAX_LEN / 2);
}
int sizeNear = sizeof(near) / sizeof(float);
while(looper) {
angleX += 0.5f;
angleY += 0.6f;
angleZ += 0.8f;
glm::mat4 modelM = glm::mat4(1.0f);
modelM = glm::scale(modelM, glm::vec3(1.0f, 1.0f, 1.0f));
modelM = glm::rotate(modelM, glm::radians(angleX), glm::vec3(1.0f, 0.0f, 0.0f));
modelM = glm::rotate(modelM, glm::radians(angleY), glm::vec3(0.0f, 1.0f, 0.0f));
modelM = glm::rotate(modelM, glm::radians(angleZ), glm::vec3(0.0f, 0.0f, 1.0f));
modelM = glm::translate(modelM, glm::vec3(0.0f, 0.0f, 0.0f));
LOGD("modelM:");
printMat4(modelM);
glm::mat4 viewM = glm::lookAt(
glm::vec3(0, 0, 2.88), // Camera is at (0,0,1), in World Space 相机位置
glm::vec3(0, 0, 0), // and looks at the origin 观察点坐标
glm::vec3(0, 1, 0)); // Head is up (set to 0,-1,0 to look upside-down) 相机 up 方向,即相机头部朝向
LOGD("viewM:");
printMat4(viewM);
glm::mat4 mv = viewM*modelM;
printVec4(mv*glm::vec4(-1.0, -1.0, 0, 1));
printVec4(mv*glm::vec4(1.0, 1.0, 0, 1));
printVec4(mv*glm::vec4(-1.0, 1.0, 0, 1));
printVec4(mv*glm::vec4(1.0, -1.0, 0, 1));
float ratio = 1.0f * width / height;
LOGD("ratio: %f, width: %d, height: %d, surfaceW: %d, surfaceH: %d", ratio, width, height, surfaceW, surfaceH);
glm::mat4 prjM;
if (1.0f * height / width > 1.0f * surfaceH / surfaceW) {
prjM = glm::ortho(-1.0f * width / height, 1.0f * width / height, -1.0f, 1.0f, 0.0f, 100.0f); //ratio 一般表示视口的宽高比,width/height
} else {
prjM = glm::ortho(-1.0f, 1.0f,
-1.0f * surfaceH / (1.0f*surfaceW*height/width), 1.0f * surfaceH / (1.0f*surfaceW*height/width),
3.0f, 100.0f);
}
prjM = glm::ortho(-1.0f, 1.0f,
-1.0f, 1.0f,
1.5f, 100.0f); // 这两个值其实是负的方向更好理解
printMat4(prjM);
// prjM = glm::perspective(glm::radians(45.0f), 1.0f * surfaceW / surfaceH, 2.6f, 100.f); //ratio 一般表示视口的宽高比,width/height,
// LOGD("prjM:");
// printMat4(prjM);
prjM = glm::frustum(-1.0f, 1.0f,
-1.0f, 1.0f,
near[count%sizeNear], 100.f);
LOGD("prjM:");
printMat4(prjM);
glm::mat4 mvp = prjM*viewM*modelM;
printVec4(mvp*glm::vec4(-DP, -DP, DP, 1));
printVec4(mvp*glm::vec4(-DP, -DP, -DP, 1));
//mvp = glm::mat4(1.0f);
GLint mvpLoc = glGetUniformLocation(gl_cxt.program, "u_MVPMatrix");
glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, (GLfloat *)&mvp[0][0]);
// optimal
unsigned int VBO, EBO, VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)((0 + 3)*sizeof(float)));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glBindVertexArray(0);
glBindVertexArray(VAO);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_TEXTURE_2D);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
// draw to screen
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureId);
glDrawElements(GL_TRIANGLES, sizeof(indices) / sizeof(int), GL_UNSIGNED_INT, (void*)0);
glBindTexture(GL_TEXTURE_2D, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindVertexArray(0);
eglSwapBuffers(gl_cxt.display, gl_cxt.winSurface);
count++;
usleep(15 * 1000);
if (count == 99999) {
looper = false;
}
}
LOGD("process2 X");
}
JNIEXPORT void JNICALL Java_com_sprd_opengl_test_MyNdk4_uninit
(JNIEnv *env, jobject obj) {
LOGD("uninit");
eglDestroySurface(gl_cxt.display, gl_cxt.winSurface);
eglDestroyContext(gl_cxt.display, gl_cxt.context);
eglMakeCurrent(gl_cxt.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(gl_cxt.display);
gl_cxt.winSurface = EGL_NO_SURFACE;
gl_cxt.display = EGL_NO_DISPLAY;
gl_cxt.context = EGL_NO_CONTEXT;
}
六、注意点
EGLint configSpec[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RECORDABLE_ANDROID, EGL_TRUE,
EGL_NONE
};
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
一定要配置 EGL_DEPTH_SIZE,我调试的时候没有配置EGL_DEPTH_SIZE(即使
glEnable(GL_DEPTH_TEST)调用了),导致绘制的立方体一直有问题,没有立体效果