# 第二课：绘制第一个三角形

## 顶点数组对象(VAO)

``````GLuint VertexArrayID;
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);
``````

## 屏幕坐标系

• X轴朝右
• Y轴朝上
• Z轴指向您后面（没错，是后面，不是前面）

• 拇指代表X轴
• 食指代表Y轴
• 中指代表Z轴。如果您的拇指指向右边，食指指向天空，那么中指将指向您的后面。

``````// An array of 3 vectors which represents 3 vertices
static const GLfloat g_vertex_buffer_data[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f,  1.0f, 0.0f,
};
``````

## 绘制三角形

``````// This will identify our vertex buffer
GLuint vertexbuffer;

// Generate 1 buffer, put the resulting identifier in vertexbuffer
glGenBuffers(1, &vertexbuffer);

// The following commands will talk about our 'vertexbuffer' buffer
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);

// Give our vertices to OpenGL.
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
``````

``````// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(
0,                  // attribute 0. No particular reason for 0, but must match the layout in the shader.
3,                  // size
GL_FLOAT,           // type
GL_FALSE,           // normalized?
0,                  // stride
(void*)0            // array buffer offset
);
// Draw the triangle !
glDrawArrays(GL_TRIANGLES, 0, 3); // Starting from vertex 0; 3 vertices total -> 1 triangle
glDisableVertexAttribArray(0);
``````

## 编译着色器

``````GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path){

// Create the shaders

// Read the Vertex Shader code from the file
std::stringstream sstr;
}else{
printf("Impossible to open %s. Are you in the right directory ? Don't forget to read the FAQ !\n", vertex_file_path);
getchar();
return 0;
}

// Read the Fragment Shader code from the file
std::stringstream sstr;
}

GLint Result = GL_FALSE;
int InfoLogLength;

// Compile Vertex Shader
printf("Compiling shader : %s\n", vertex_file_path);
char const * VertexSourcePointer = VertexShaderCode.c_str();

// Check Vertex Shader
if ( InfoLogLength > 0 ){
}

// Compile Fragment Shader
printf("Compiling shader : %s\n", fragment_file_path);
char const * FragmentSourcePointer = FragmentShaderCode.c_str();

// Check Fragment Shader
if ( InfoLogLength > 0 ){
}

// Link the program
GLuint ProgramID = glCreateProgram();

// Check the program
glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){
std::vector<char> ProgramErrorMessage(InfoLogLength+1);
glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
printf("%s\n", &ProgramErrorMessage[0]);
}

return ProgramID;
}
``````

## 顶点着色器

``````#version 330 core
``````

``````layout(location = 0) in vec3 vertexPosition_modelspace;
``````

• 在GLSL中“`vec3`”代表一个三维向量。类似但不等同于之前声明三角形的`glm::vec3`。最重要的是，如果我们在C++中使用三维向量，那么在GLSL中也要相应地使用三维向量。
• `layout(location = 0)`“指向存储`vertexPosition_modelspace`属性（attribute）的缓冲。每个顶点有多种属性：位置，一种或多种颜色，一个或多个纹理坐标等等。OpenGL并不清楚什么是颜色，它只能识别`vec3`这样的数据类型。因此我们必须将`glvertexAttribPointer`函数的第一个参数值赋给`layout`，以此告知OpenGL每个缓冲对应的是哪种属性数据。第二个参数“0”并不重要，也可以换成12（但是不能超过`glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &v)）`，关键是C++和GLSL两边数值必须保持一致。
• `vertexPosition_modelspace`”这个变量名可以任取，其中保存的是顶点位置，顶点着色器每次运行时都会用到。
• `in`”表明是这是输入数据。不久我们将会看到“out”关键字。

``````void main(){
``````

``````gl_Position.xyz = vertexPosition_modelspace;
gl_Position.w = 1.0;
}
``````

gl_Position是仅有的几个内置变量之一：您必须对其赋值。其他操作都是可选的，我们将在第四课中看到究竟有哪些“其他操作”。

## 片段着色器

``````#version 330 core
out vec3 color;

void main(){
color = vec3(1,0,0);
}
``````

vec3(1,0,0)代表红色。因为在计算机屏幕上，颜色由红、绿、蓝三元组表示。因此(1,0,0)代表纯红色，无绿、蓝分量。

# 汇总

``````// Create and compile our GLSL program from the shaders
``````

``````glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
``````

``````// Use our shader
glUseProgram(programID);

// Draw triangle...
``````

…然后，哒哒，就看到您亲手绘制的红色三角形啦！