绘制四边形的时候,实际上是绘制两个三角形,即6个点,但是实际上有两个点重复存储了,解决点重复存储的方法是使用EBO,详细见代码部分。
Shader.h
#pragma once
#include<string>
#define GLEW_STATIC //glew32s.lib
#include<GL\glew.h>
#include<GLFW\glfw3.h>
class Shader
{
public:
Shader(const char* vertexPath, const char* fragmentPath);
void use();
//检查编译错误
void checkCompileErrors(GLuint ID, std::string type);
private:
std::string vertexString;
std::string fragmentString;
const char* vertexSource;
const char* fragmentSource;
GLuint shaderProgramID; //Shader program ID
};
Shader.cpp
#include "Shader.h"
#include<iostream>
#include<fstream>
#include<sstream>
Shader::Shader(const char * vertexPath, const char * fragmentPath)
{
std::ifstream vertexFile;
std::ifstream fragmentFile;
std::stringstream vertexSStream;
std::stringstream fragmentSStream;
vertexFile.open(vertexPath);
fragmentFile.open(fragmentPath);
// 保证ifstream对象可以抛出异常:
vertexFile.exceptions(std::ifstream::failbit || std::ifstream::badbit);
fragmentFile.exceptions(std::ifstream::failbit || std::ifstream::badbit);
try
{
if (!vertexFile.is_open() || !fragmentFile.is_open())
{
throw std::exception("open file error!");
}
// step1:读取文件的缓冲内容到 字符串流(数据流)
vertexSStream << vertexFile.rdbuf();
fragmentSStream << fragmentFile.rdbuf();
// step2:转换 字符串流(数据流)到 string
vertexString = vertexSStream.str();
fragmentString = fragmentSStream.str();
// step3:转换 string 到 char*
vertexSource = vertexString.c_str();
fragmentSource = fragmentString.c_str();
GLuint vertexShaderID, fragmentShaderID;
vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderID, 1, &vertexSource, NULL);
glCompileShader(vertexShaderID);
checkCompileErrors(vertexShaderID, "VERTEX");
fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShaderID, 1, &fragmentSource, NULL);
glCompileShader(fragmentShaderID);
checkCompileErrors(fragmentShaderID, "FRAGMENT");
shaderProgramID = glCreateProgram();
glAttachShader(shaderProgramID, vertexShaderID);
glAttachShader(shaderProgramID, fragmentShaderID);
glLinkProgram(shaderProgramID);
checkCompileErrors(shaderProgramID, "PROGRAM");
glDeleteShader(vertexShaderID);
glDeleteShader(fragmentShaderID);
}
catch (const std::exception& ex)
{
std::cout << ex.what() << std::endl;
}
}
void Shader::use()
{
glUseProgram(shaderProgramID);
}
void Shader::checkCompileErrors(GLuint ID, std::string type)
{
GLint success;
GLchar infoLog[512];
if (type != "PROGRAM")
{
glGetShaderiv(ID, GL_COMPILE_STATUS, &success);
if (!success) //编译不成功
{
glGetShaderInfoLog(ID, 512, NULL, infoLog); //取得到底报的什么错误
std::cout << "shader compile error!" << infoLog << std::endl;
}
}
else
{
glGetProgramiv(ID, GL_LINK_STATUS, &success);
if (!success) //链接不成功
{
glGetProgramInfoLog(ID, 512, NULL, infoLog);
std::cout << "program linking error!" << infoLog << std::endl;
}
}
}
main.cpp
#define GLEW_STATIC //glew32s.lib
#include<GL\glew.h>
#include<GLFW\glfw3.h>
#include<iostream>
#include<vector>
#include"Shader.h"
//键盘回调函数原型声明
void processInput(GLFWwindow* window);
//定义程序常量
const int WINDOW_WIDTH = 800, WINDOW_HEIGHT = 600;
int main(int argc, char** argv)
{
// 开启OpenGL 3.3 core profile
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //主版本号
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //次版本号
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//create OpenGL GLFW Window
GLFWwindow *window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "OpenglWindow", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window!" << std::endl;
glfwTerminate(); //终止glfw
return -1;
}
glfwMakeContextCurrent(window); // 创建的窗口的context指定为当前context
// 初始化GLEW 获取OpenGL函数
glewExperimental = true; // 让glew获取所有拓展函数
if (glewInit() != GLEW_OK)
{
std::cout << "Failed to Init GLEW" << std::endl;
glfwTerminate();
return -1;
}
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); //设置视口参数
Shader *shader = new Shader("vertexSource.txt", "fragmentSource.txt");
// 指定顶点属性数据
GLfloat vertices[] = {
//顶点位置 //顶点颜色
0.5f, 0.5f, 0.0f, 1.0f,0.0f,0.0f, // 右上角 0
0.5f, -0.5f, 0.0f, 0.0f,1.0f,0.0f, // 右下角 1
-0.5f, -0.5f, 0.0f, 0.0f,0.0f,1.0f, // 左下角 2
-0.5f, 0.5f, 0.0f, 0.3f,0.8f,0.5f // 左上角 3
};
//索引数据(用于EBO)
GLuint indices[] = { // 注意索引从0开始!
0, 1, 3, // 第一个三角形
1, 2, 3 // 第二个三角形
};
//创建缓存对象
GLuint VAO, VBO;
//step1:创建并绑定VAO对象(VAO:vertex array object)
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
//step2:创建并绑定VBO对象 (VBO:vertex buffer object)
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//step3:分配空间,将数据传送到GPU中
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//step4:指定解析方式,并启用顶点属性
//位置属性
glVertexAttribPointer(6, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GL_FLOAT), (GLvoid*)0);
glEnableVertexAttribArray(6);
//颜色属性
glVertexAttribPointer(7, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GL_FLOAT), (GLvoid*)(3*sizeof(GLfloat)));
glEnableVertexAttribArray(7);
//EBO(绘制四边形,解决顶点重复存储)
GLuint EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 开始游戏主循环
while (!glfwWindowShouldClose(window))
{
glfwPollEvents(); // 处理例如鼠标 键盘等事件
// 清除颜色缓冲区 重置为指定颜色
glClearColor(0.18f, 0.04f, 0.14f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 这里填写场景绘制代码
glBindVertexArray(VAO); //使用VAO信息
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); //使用EBO信息
shader->use();
//glDrawArrays(GL_TRIANGLES, 0, 3); //使用VBO数据绘制物体
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); //使用EBO数据绘制物体,第4个参数是EBO中的偏移量
glBindVertexArray(0);
glUseProgram(0);
glfwSwapBuffers(window); // 交换缓存
glfwPollEvents();
}
// 释放资源
//glDeleteProgram(shaderProgram);
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glfwTerminate();
return 0;
}
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true);
}
}
运行结果: