【openGLES3.0编程指南笔记-8】加载立方图纹理
概述
立方图纹理
立方图就是一个由6个单独2D纹理面组成的纹理,通常,生成环境贴图所用的立方图通过在场景中央放置一个摄像机,从6个轴的方向(+X,-X,+Y,-Y,+Z,-Z)捕捉场景图像并将结果保存在立方体的每个面来生成。
立方图纹素的读取通过使用一个3D向量(s,t,r)作为纹理坐标,在立方图中查找。纹理坐标(s,t,r)代表着3D向量的(x,y,z)分量。这个3D向量首先用于选择立方图中需要读取的一个面,然后该坐标投影到2D坐标(s,t),从该面上读取。
立方图纹理源码解析
(1)加载像素数据
GLuint CreateSimpleTextureCubemap()
{
GLuint textureId;
GLubyte cubePixels[6][3] = {
255, 0, 0,
0, 255, 0,
0, 0, 255,
255, 255, 0,
0, 0, 0,
255, 0, 255,
};
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureId);
// 加载+X面的像素数据
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGB, 1, 1, 0,
GL_RGB, GL_UNSIGNED_BYTE, &cubePixels[0]);
// 加载-X面的像素数据
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGB, 1, 1, 0,
GL_RGB, GL_UNSIGNED_BYTE, &cubePixels[1]);
// 加载+Y面的像素数据
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGB, 1, 1, 0,
GL_RGB, GL_UNSIGNED_BYTE, &cubePixels[2]);
// 加载-Y面的像素数据
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGB, 1, 1, 0,
GL_RGB, GL_UNSIGNED_BYTE, &cubePixels[3]);
// 加载+Z面的像素数据
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGB, 1, 1, 0,
GL_RGB, GL_UNSIGNED_BYTE, &cubePixels[4]);
// 加载-Z面的像素数据
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGB, 1, 1, 0,
GL_RGB, GL_UNSIGNED_BYTE, &cubePixels[5]);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
return textureId;
}
(2)查找纹理
// 使用了法向量来查找纹理
outColor = texture(s_texture, v_normal);
如何画球体
需要得到球的顶点坐标,顶点法向量,三角形索引信息
球的顶点坐标
将程序中的球顶点坐标打印出来,前40个顶点坐标为:
x = 0.000000, y = 1.000000, z = 0.000000, i = 0, j = 0
x = 0.000000, y = 1.000000, z = 0.000000, i = 0, j = 1
x = 0.000000, y = 1.000000, z = 0.000000, i = 0, j = 2
x = 0.000000, y = 1.000000, z = 0.000000, i = 0, j = 3
x = 0.000000, y = 1.000000, z = 0.000000, i = 0, j = 4
x = 0.000000, y = 1.000000, z = -0.000000, i = 0, j = 5
x = 0.000000, y = 1.000000, z = -0.000000, i = 0, j = 6
x = 0.000000, y = 1.000000, z = -0.000000, i = 0, j = 7
x = 0.000000, y = 1.000000, z = -0.000000, i = 0, j = 8
x = 0.000000, y = 1.000000, z = -0.000000, i = 0, j = 9
x = -0.000000, y = 1.000000, z = -0.000000, i = 0, j = 10
x = -0.000000, y = 1.000000, z = -0.000000, i = 0, j = 11
x = -0.000000, y = 1.000000, z = -0.000000, i = 0, j = 12
x = -0.000000, y = 1.000000, z = -0.000000, i = 0, j = 13
x = -0.000000, y = 1.000000, z = -0.000000, i = 0, j = 14
x = -0.000000, y = 1.000000, z = 0.000000, i = 0, j = 15
x = -0.000000, y = 1.000000, z = 0.000000, i = 0, j = 16
x = -0.000000, y = 1.000000, z = 0.000000, i = 0, j = 17
x = -0.000000, y = 1.000000, z = 0.000000, i = 0, j = 18
x = -0.000000, y = 1.000000, z = 0.000000, i = 0, j = 19
x = 0.000000, y = 1.000000, z = 0.000000, i = 0, j = 20
x = 0.000000, y = 0.951057, z = 0.309017, i = 1, j = 0
x = 0.095492, y = 0.951057, z = 0.293893, i = 1, j = 1
x = 0.181636, y = 0.951057, z = 0.250000, i = 1, j = 2
x = 0.250000, y = 0.951057, z = 0.181636, i = 1, j = 3
x = 0.293893, y = 0.951057, z = 0.095491, i = 1, j = 4
x = 0.309017, y = 0.951057, z = -0.000000, i = 1, j = 5
x = 0.293893, y = 0.951057, z = -0.095492, i = 1, j = 6
x = 0.250000, y = 0.951057, z = -0.181636, i = 1, j = 7
x = 0.181636, y = 0.951057, z = -0.250000, i = 1, j = 8
x = 0.095491, y = 0.951057, z = -0.293893, i = 1, j = 9
x = -0.000000, y = 0.951057, z = -0.309017, i = 1, j = 10
x = -0.095491, y = 0.951057, z = -0.293893, i = 1, j = 11
x = -0.181636, y = 0.951057, z = -0.250000, i = 1, j = 12
x = -0.250000, y = 0.951057, z = -0.181636, i = 1, j = 13
x = -0.293893, y = 0.951057, z = -0.095492, i = 1, j = 14
x = -0.309017, y = 0.951057, z = 0.000000, i = 1, j = 15
x = -0.293893, y = 0.951057, z = 0.095492, i = 1, j = 16
x = -0.250000, y = 0.951057, z = 0.181636, i = 1, j = 17
x = -0.181636, y = 0.951057, z = 0.250000, i = 1, j = 18
x = -0.095491, y = 0.951057, z = 0.293893, i = 1, j = 19
x = 0.000000, y = 0.951057, z = 0.309017, i = 1, j = 20
球体绘制的数学模型
绘制球体的源码
// 生成球体
// myesGenSphere(20, 0.75f, &userData->vertices, &userData->normals, NULL, &userData->indices);
int myesGenSphere(int numSlices, float radius, GLfloat **vertices, GLfloat **normals,
GLfloat **texCoords, GLuint **indices)
{
int i, j;
int numParallels = numSlices / 2;
int numVertices = (numParallels + 1)*(numSlices + 1);
int numIndices = numParallels * numSlices * 6;
float angleStep = (2.0f * ES_PI) / ((float) numSlices);
if (vertices != NULL) {
*vertices = (GLfloat *)malloc(sizeof(GLfloat)*3*numVertices);
}
if (normals != NULL) {
*normals = (GLfloat *)malloc(sizeof(GLfloat)*3*numVertices);
}
if (texCoords != NULL) {
*texCoords = (GLfloat *)malloc(sizeof(GLfloat)*2*numVertices);
}
if (indices != NULL) {
*indices = (GLuint *)malloc(sizeof(GLuint) * numIndices);
}
for (i = 0; i < numParallels + 1; i++) {
for (j = 0; j < numSlices + 1; j++) {
int vertex = (i*(numSlices + 1) + j) * 3;
if (vertices) {
// 获得球体的顶点坐标
(*vertices)[vertex + 0] = radius * sinf(angleStep*(float)i) *
sinf(angleStep*(float)j);
(*vertices)[vertex + 1] = radius * cosf(angleStep * (float)i);
(*vertices)[vertex + 2] = radius * sinf(angleStep * (float) i) *
cosf(angleStep * (float) j);
}
if (normals) {
// 获得球体的法向量
(*normals)[vertex + 0] = (*vertices)[vertex + 0] / radius;
(*normals)[vertex + 1] = (*vertices)[vertex + 1] / radius;
(*normals)[vertex + 2] = (*vertices)[vertex + 2] / radius;
}
if (texCoords) {
int texIndex = (i * (numSlices + 1) + j)*2;
(*texCoords)[texIndex + 0] = (float)j / (float)numSlices;
(*texCoords)[texIndex + 1] = (1.0f - (float)i) / (float)(numParallels - 1);
}
}
}
if (indices != NULL) {
GLuint *indexBuf = (*indices);
for (i = 0; i < numParallels; i++) {
for (j = 0; j < numSlices; j++) {
// 获得index
*indexBuf++ = i * (numSlices + 1) + j;
*indexBuf++ = (i + 1) * (numSlices + 1) + j;
*indexBuf++ = (i + 1) * (numSlices + 1) + (j + 1);
*indexBuf++ = i * (numSlices + 1) + j;
*indexBuf++ = (i + 1) * (numSlices + 1) + (j + 1);
*indexBuf++ = i * (numSlices + 1) + (j + 1);
}
}
}
return numIndices;
}
源码解析
#include <stdlib.h>
#include "esUtil.h"
typedef struct
{
GLuint programObject;
GLint samplerLoc;
GLuint textureId;
int numIndices;
GLfloat *vertices;
GLfloat *normals;
GLuint *indices;
} myUserData;
GLuint CreateSimpleTextureCubemap()
{
GLuint textureId;
GLubyte cubePixels[6][3] = {
255, 0, 0,
0, 255, 0,
0, 0, 255,
255, 255, 0,
0, 0, 0,
255, 0, 255,
};
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureId);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGB, 1, 1, 0,
GL_RGB, GL_UNSIGNED_BYTE, &cubePixels[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGB, 1, 1, 0,
GL_RGB, GL_UNSIGNED_BYTE, &cubePixels[1]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGB, 1, 1, 0,
GL_RGB, GL_UNSIGNED_BYTE, &cubePixels[2]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGB, 1, 1, 0,
GL_RGB, GL_UNSIGNED_BYTE, &cubePixels[3]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGB, 1, 1, 0,
GL_RGB, GL_UNSIGNED_BYTE, &cubePixels[4]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGB, 1, 1, 0,
GL_RGB, GL_UNSIGNED_BYTE, &cubePixels[5]);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
return textureId;
}
int Init(MYESContext *myesContext)
{
myUserData *userData = (myUserData *)myesContext->userData;
char vShaderStr[] =
"#version 300 es \n"
"layout(location = 0) in vec4 a_position; \n"
"layout(location = 1) in vec3 a_normal; \n"
"out vec3 v_normal; \n"
"void main() \n"
"{ \n"
" gl_Position = a_position; \n"
" v_normal = a_normal; \n"
"} \n";
char fShaderStr[] =
"#version 300 es \n"
"precision mediump float; \n"
"in vec3 v_normal; \n"
"layout(location = 0) out vec4 outColor; \n"
"uniform samplerCube s_texture; \n"
"void main() \n"
"{ \n"
" outColor = texture(s_texture, v_normal); \n"
"} \n";
userData->programObject = myesLoadProgram(vShaderStr, fShaderStr);
userData->samplerLoc = glGetUniformLocation(userData->programObject, "s_texture");
userData->textureId = CreateSimpleTextureCubemap();
// 生成球体
userData->numIndices = myesGenSphere(20, 0.75f, &userData->vertices, &userData->normals,
NULL, &userData->indices);
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
return GL_TRUE;
}
void Draw(MYESContext *myesContext)
{
myUserData *userData = (myUserData *)myesContext->userData;
glViewport(0, 0, myesContext->width, myesContext->height);
glClear(GL_COLOR_BUFFER_BIT);
// 面剔除,剔除后面的面,因为看不到,可以提高性能
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glUseProgram(userData->programObject);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, userData->vertices);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, userData->normals);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, userData->textureId);
glUniform1i(userData->samplerLoc, 0);
glDrawElements(GL_TRIANGLES, userData->numIndices,
GL_UNSIGNED_INT, userData->indices);
}
void ShutDown(MYESContext *myesContext)
{
myUserData *userData = (myUserData *)myesContext->userData;
glDeleteTextures(1, &userData->textureId);
glDeleteProgram(userData->programObject);
free(userData->vertices);
free(userData->normals);
}
int myesMain(MYESContext *myesContext)
{
myesContext->userData = malloc(sizeof(myUserData));
myesCreateWindow(myesContext, "9_3_texturewrap", 320, 240, MY_ES_WINDOW_RGB);
if (!Init(myesContext))
{
return GL_FALSE;
}
esRegisterDrawFunc(myesContext, Draw);
esRegisterShutdownFunc(myesContext, ShutDown);
return GL_TRUE;
}
// 生成球体
// myesGenSphere(20, 0.75f, &userData->vertices, &userData->normals, NULL, &userData->indices);
int myesGenSphere(int numSlices, float radius, GLfloat **vertices, GLfloat **normals,
GLfloat **texCoords, GLuint **indices)
{
int i, j;
int numParallels = numSlices / 2;
int numVertices = (numParallels + 1)*(numSlices + 1);
int numIndices = numParallels * numSlices * 6;
float angleStep = (2.0f * ES_PI) / ((float) numSlices);
if (vertices != NULL) {
*vertices = (GLfloat *)malloc(sizeof(GLfloat)*3*numVertices);
}
if (normals != NULL) {
*normals = (GLfloat *)malloc(sizeof(GLfloat)*3*numVertices);
}
if (texCoords != NULL) {
*texCoords = (GLfloat *)malloc(sizeof(GLfloat)*2*numVertices);
}
if (indices != NULL) {
*indices = (GLuint *)malloc(sizeof(GLuint) * numIndices);
}
for (i = 0; i < numParallels + 1; i++) {
for (j = 0; j < numSlices + 1; j++) {
int vertex = (i*(numSlices + 1) + j) * 3;
if (vertices) {
(*vertices)[vertex + 0] = radius * sinf(angleStep*(float)i) *
sinf(angleStep*(float)j);
(*vertices)[vertex + 1] = radius * cosf(angleStep * (float)i);
(*vertices)[vertex + 2] = radius * sinf(angleStep * (float) i) *
cosf(angleStep * (float) j);
}
if (normals) {
(*normals)[vertex + 0] = (*vertices)[vertex + 0] / radius;
(*normals)[vertex + 1] = (*vertices)[vertex + 1] / radius;
(*normals)[vertex + 2] = (*vertices)[vertex + 2] / radius;
}
if (texCoords) {
int texIndex = (i * (numSlices + 1) + j)*2;
(*texCoords)[texIndex + 0] = (float)j / (float)numSlices;
(*texCoords)[texIndex + 1] = (1.0f - (float)i) / (float)(numParallels - 1);
}
}
}
if (indices != NULL) {
GLuint *indexBuf = (*indices);
for (i = 0; i < numParallels; i++) {
for (j = 0; j < numSlices; j++) {
*indexBuf++ = i * (numSlices + 1) + j;
*indexBuf++ = (i + 1) * (numSlices + 1) + j;
*indexBuf++ = (i + 1) * (numSlices + 1) + (j + 1);
*indexBuf++ = i * (numSlices + 1) + j;
*indexBuf++ = (i + 1) * (numSlices + 1) + (j + 1);
*indexBuf++ = i * (numSlices + 1) + (j + 1);
}
}
}
return numIndices;
}
效果图
参考
1. GL02-02:OpenGL球体绘制
https://www.jianshu.com/p/48a4b3f3d51e
2. 面剔除
https://learnopengl-cn.readthedocs.io/zh/latest/04%20Advanced%20OpenGL/04%20Face%20culling/