目录
- 立方体绘制基本原理
- 立方体的顶点坐标和绘制顺序
- 立方体颜色和着色器
- 实现效果和参考代码
立方体绘制基本原理
一个立方体是由8
个顶点组成,共6
个面,所以绘制立方体本质上就是绘制这6
个面共12
个三角形
顶点的坐标体系如下图所示,三维坐标的中心原点位于立方体的中心,但是要特别注意的是,前后方向表示的是Z轴,上下方向表示的是Y轴
立方体的顶点坐标和绘制顺序
立方体坐标定义如下:
static GLfloat vertices[] = {
// 位置 // 颜色
-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, // 左下后 红色
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // 右下后 绿色
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // 右上后 蓝色
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, // 左上后 黄色
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // 左下前 青色
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 1.0f, // 右下前 品红
0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, // 右上前 灰色
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f // 左上前 白色
};
// 立方体索引数据
static GLuint indices[] = {
// 后面
0, 1, 2,
2, 3, 0,
// 前面
4, 5, 6,
6, 7, 4,
// 左面
0, 4, 7,
7, 3, 0,
// 右面
1, 5, 6,
6, 2, 1,
// 底面
0, 1, 5,
5, 4, 0,
// 顶面
3, 2, 6,
6, 7, 3
};
在这里vertices
定义不同的顶点,indices
数组中表示不同顶点的绘制顺序,每三个顶点构成一个三角形,最后由 glDrawElements(GL_TRIANGLES,36,GL_UNSIGNED_INT,0);
每三个顶点为一组绘制共12个三角形,不同的面和顶点的绑定关系如下:
立方体颜色和着色器
这里为立方体的每个顶点定义不同的颜色,同时在VertexShader
中将颜色传递给FragmentShader
,这样 FragmentShader
会得到经过插值后的每个片元的颜色,并将这个颜色设置为最终的显示,实现的是一个渐变色的效果,shader
程序如下:
static const char* vertexShaderSource =
"#version 300 es \n"
"precision mediump float;\n"
"uniform mat4 u_mvpMatrix; \n"
"layout(location = 0) in vec3 a_position; \n"
"layout(location = 1) in vec3 a_color; \n"
"out vec3 v_color; \n"
"void main() {\n"
" gl_Position = u_mvpMatrix*vec4(a_position,1.0);\n"
" v_color = a_color; \n"
"}\n";
// Fragment Shader source code
static const char* fragmentShaderSource =
"#version 300 es \n"
"precision mediump float;\n"
"layout(location = 0) out vec4 outColor; \n"
"in vec3 v_color; \n"
"void main() {\n"
" outColor = vec4(v_color, 1.0);\n"
"}\n";
实现效果和参考代码
最终实现效果:
参考代码如下:
Note: 这里还使用了
ModeViewProject Matrix
实现了旋转的效果,同时使用VAO,VBO,EBO
实现了对顶点内容和顶点Index的缓冲
typedef struct {
GLuint programObject;
GLuint vboIds[2];
GLuint vaoId;
uint64_t timeInMiliSeconds;
GLint mvpLoc;
GLfloat angle;
GLint deltaTime;
ESMatrix mvpMatrix;
} UserData;
static GLfloat vertices[] = {
// 位置 // 颜色
-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, // 左下后 红色
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // 右下后 绿色
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // 右上后 蓝色
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, // 左上后 黄色
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // 左下前 青色
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 1.0f, // 右下前 品红
0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, // 右上前 灰色
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f // 左上前 白色
};
// 立方体索引数据
static GLuint indices[] = {
// 后面
0, 1, 2,
2, 3, 0,
// 前面
4, 5, 6,
6, 7, 4,
// 左面
0, 4, 7,
7, 3, 0,
// 右面
1, 5, 6,
6, 2, 1,
// 底面
0, 1, 5,
5, 4, 0,
// 顶面
3, 2, 6,
6, 7, 3
};
static const char* vertexShaderSource =
"#version 300 es \n"
"precision mediump float;\n"
"uniform mat4 u_mvpMatrix; \n"
"layout(location = 0) in vec3 a_position; \n"
"layout(location = 1) in vec3 a_color; \n"
"out vec3 v_color; \n"
"void main() {\n"
" gl_Position = u_mvpMatrix*vec4(a_position,1.0);\n"
" v_color = a_color; \n"
"}\n";
// Fragment Shader source code
static const char* fragmentShaderSource =
"#version 300 es \n"
"precision mediump float;\n"
"layout(location = 0) out vec4 outColor; \n"
"in vec3 v_color; \n"
"void main() {\n"
" outColor = vec4(v_color, 1.0);\n"
"}\n";
static int initInternal(ESContext* esContext) {
UserData *userData = esContext->userData;
// Vertex Shader source code
GLuint programObject = esLoadProgram(vertexShaderSource, fragmentShaderSource);
if (programObject == 0) {
return GL_FALSE;
}
// turn on depth test
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glClearColor(0.0, 0.0, 0.0, 1.0);
// Store the program object
userData->programObject = programObject;
userData->vboIds[0] = userData->vboIds[1] = 0;
userData->mvpLoc = glGetUniformLocation(userData->programObject, "u_mvpMatrix");
userData->angle = 0.0f;
return GL_TRUE;
}
static void uMVPMatrixUpdateRotate(ESContext *esContext, GLuint deltaTime)
{
UserData *userData = esContext->userData;
ESMatrix perspective;
ESMatrix modelview;
float aspect;
userData->angle += 1.0f;
if (userData->angle >= 360.0f) {
userData->angle -= 360.0f;
}
aspect = (GLfloat) esContext->width / (GLfloat) esContext->height;
esMatrixLoadIdentity(&perspective);
esMatrixLoadIdentity(&modelview);
esPerspective(&perspective, 45.0f, aspect, 1.0f, 10.0f);
esTranslate(&modelview, 0.0, 0.0, -4.0);
esRotate(&modelview, userData->angle, 0.0, 1.0, 0.0);
esMatrixMultiply(&userData->mvpMatrix, &modelview, &perspective);
}
static void DrawPrimitiveWithVBOs(ESContext *esContext)
{
UserData *userData = esContext->userData;
GLuint offset = 0;
// vboIds[0] - used to store vertex attribute data
// vboIds[l] - used to store element indices
if (userData->vboIds[0] == 0 && userData->vboIds[1] == 0) {
// Only allocate on the first draw
glGenBuffers(2, userData->vboIds);
glGenVertexArrays(1, &userData->vaoId);
printf("gen vbo id:%d %d vao id:%d.\n",userData->vboIds[0],userData->vboIds[1],userData->vaoId);
glBindVertexArray(userData->vaoId);
glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3,GL_FLOAT, GL_FALSE, 6*sizeof(GLfloat) , NULL);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(GLfloat) ,(void*)(3*sizeof(GLfloat)));
glEnableVertexAttribArray(1);
// notice using GL_ARRAY_BUFFER
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices), indices, GL_STATIC_DRAW);
glBindVertexArray(0);
}
glBindVertexArray(userData->vaoId);
glUniformMatrix4fv(userData->mvpLoc, 1, GL_FALSE, (GLfloat *)&userData->mvpMatrix.m[0][0]);
glDrawElements(GL_TRIANGLES,36,GL_UNSIGNED_INT,0);
glBindVertexArray(0);
}
static int drawLoopInternal(ESContext* esContext) {
UserData *userData = esContext->userData;
struct timespec currentts;
clock_gettime(CLOCK_REALTIME, ¤tts);
uint64_t milliseconds = currentts.tv_sec * 1000LL + currentts.tv_nsec / 1000000;
int periodInMs = milliseconds - userData->timeInMiliSeconds;
userData->timeInMiliSeconds = milliseconds;
userData->deltaTime++;
//printf("current time in milliseconds %lld period:%d\n",milliseconds, (periodInMs > 0 ? periodInMs: -1));
// Set the viewport
glViewport(0, 0, 640, 480);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(userData->programObject);
uMVPMatrixUpdateRotate(esContext,userData->deltaTime);
DrawPrimitiveWithVBOs(esContext);
// Swap buffers
eglSwapBuffers(esContext->eglDisplay, esContext->eglSurface);
}
static int cleanupInternal(ESContext* esContext) {
printf("%s enter!.\n",__FUNCTION__);
UserData *userData = esContext->userData;
glDeleteProgram(userData->programObject);
eglDestroySurface(esContext->eglDisplay, esContext->eglSurface);
eglDestroyContext(esContext->eglDisplay, esContext->eglContext);
eglTerminate(esContext->eglDisplay);
XDestroyWindow(esContext->x_display, esContext->win);
XCloseDisplay(esContext->x_display);
return GL_TRUE;
}
int testbasicDrawCube(ESContext* esContext) {
printf("%s enter!.\n", __FUNCTION__);
esContext->userData = (UserData*)malloc(sizeof(UserData));
esCreateWindow(esContext, esContext->testcaseName,640, 480, ES_WINDOW_DEPTH);
initInternal(esContext);
while (1) {
XEvent xev;
while (XPending(esContext->x_display)) {
XNextEvent(esContext->x_display, &xev);
if (xev.type == KeyPress) {
cleanupInternal(esContext);
}
}
drawLoopInternal(esContext);
}
}