簡介
先看最終效果
今天要學習的重點是怎樣在場景中繪制兩個(或者以上的)物體,
方框的繪制
方框其實是由兩個三角形組成,看一下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";
Shader的編譯和連結
// 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/