简介
先看最终效果
今天要学习的重点是怎样在场景中绘制两个(或者以上的)物体,
方框的绘制
方框其实是由两个三角形组成,看一下VBO, VAO, EBO的定义
GLfloat vertices[] = {
0.5f, 0.5f, -1.0f,
0.5f, -0.5f, -1.0f,
-0.5f, -0.5f, -1.0f,
-0.5f, 0.5f, -1.0f
};
GLuint indices[] = {
0, 1, 3,
1, 2, 3
};
//First: Square
// Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s).
glBindVertexArray(VAOs[0]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // Note that this is allowed, the call to glVertexAttribPointer registered VBO as the currently bound vertex buffer object so afterwards we can safely unbind
glBindVertexArray(0); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs)
Shader的定义
shader就直接写到一个string里面先
// Shaders
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"}\0";
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
"color = vec4(1.0f, 0f, 0f, 1.0f);\n"
"}\n\0";
Shader的创建和删除
// Vertex shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// Check for compile time errors
GLint success;
GLchar infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
qDebug() << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog ;
}
else
{
qDebug() << "Vertex Shader compile success!";
}
// Fragment shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// Check for compile time errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
qDebug() << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog ;
}
else
{
qDebug() << "Fragment Shader compile success!";
}
// Link shaders
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// Check for linking errors
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
qDebug() << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog ;
}
else
{
qDebug() << "Link Program success!";
}
用了EBO,所以在绘制的时候用glDrawElements,要绘制线框模式,所以在最前面加 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glUseProgram(shaderProgram);
glBindVertexArray(VAOs[0]);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
这里再补一下VBO,VAO,,EBO 的概念
顶点缓冲对象(Vertex Buffer Objects, VBO)在GPU内存(通常被称为显存)中储存大量顶点。使用这些缓冲对象的好处是我们可以一次性的发送一大批数据到显卡上,而不是每个顶点发送一次。从CPU把数据发送到显卡相对较慢,所以只要可能我们都要尝试尽量一次性发送尽可能多的数据。当数据发送至显卡的内存中后,顶点着色器几乎能立即访问顶点,这是个非常快的过程。
顶点数组对象(Vertex Array Object, VAO)可以像顶点缓冲对象那样被绑定,任何随后的顶点属性调用都会储存在这个VAO中。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。这使在不同顶点数据和属性配置之间切换变得非常简单,只需要绑定不同的VAO就行了。
索引缓冲对象(Element Buffer Object,EBO,也叫Index Buffer Object,IBO),和顶点缓冲对象一样,EBO也是一个缓冲,它专门储存索引,OpenGL调用这些顶点的索引来决定该绘制哪个顶点。
三者的关系
绘制三角形
三角形比较简单,只用了没用用EBO,数据定义如下
GLfloat secondTriangle[] = {
0.0f, -0.5f, -1.0, // Left
0.9f, -0.5f, -1.0, // Right
0.45f, 0.5f, -1.0 // Top
};
VertexShader还是用原来的那个,FragmentShader换个颜色
const GLchar* fragmentShader2Source = "#version 330 core\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
"color = vec4(0.0f, 1f, 1f, 1.0f);\n"
"}\n\0";
// Fragment shader2
GLuint fragmentShader2 = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader2, 1, &fragmentShader2Source, NULL);
glCompileShader(fragmentShader2);
// Check for compile time errors
glGetShaderiv(fragmentShader2, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader2, 512, NULL, infoLog);
qDebug() << "ERROR::SHADER::FRAGMENT 2::COMPILATION_FAILED\n" << infoLog;
}
else
{
qDebug() << "Fragment Shader 2 compile success!";
}
// Link shaders 2
shaderProgram2 = glCreateProgram();
glAttachShader(shaderProgram2, vertexShader);
glAttachShader(shaderProgram2, fragmentShader2);
glLinkProgram(shaderProgram2);
// Check for linking errors
glGetProgramiv(shaderProgram2, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram2, 512, NULL, infoLog);
qDebug() << "ERROR::SHADER::PROGRAM 2::LINKING_FAILED\n" << infoLog ;
}
else
{
qDebug() << "Link Program2 success!";
}
Shader使用完了记得要delete
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glDeleteShader(fragmentShader2);
VAO和VBO的绑定如下
//Second: Triangle
glBindVertexArray(VAOs[1]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(secondTriangle), secondTriangle, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // Note that this is allowed, the call to glVertexAttribPointer registered VBO as the currently bound vertex buffer object so afterwards we can safely unbind
glBindVertexArray(0); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs)
最后绘制
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glUseProgram(shaderProgram2);
glBindVertexArray(VAOs[1]);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
参考
learnopengl - http://learnopengl.com/