和传统opengl相比,主要差别就是需要手写着色器,具体看这篇添加链接描述
底下是我的中文注释版
把shader写入main中
#include <iostream>
// GLEW
#define GLEW_STATIC //链接静态库需要宏定义
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
// Window size
const int Width = 800, Height = 600;
// 当用户按下ESC键,我们设置window窗口的WindowShouldClose属性为true,关闭应用程序
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
// 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, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
int main(int argc, char** argv)
{
// Init GLFW
glfwInit();
// 设置opengl基本设置
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//最高版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//最低版本
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//Core file
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);//不可以调整窗口大小
// 创建窗口
GLFWwindow* window = glfwCreateWindow(Width, Height, "A Triangle", nullptr, nullptr);
glfwMakeContextCurrent(window);
// 绑定按键回调函数
glfwSetKeyCallback(window, key_callback);
// 开启modern opengl
glewExperimental = GL_TRUE;
// 初始化glew(modern opengl)
glewInit();
// 设置窗口大小
int width, height;
glfwGetFramebufferSize(window, &width, &height);
glViewport(0, 0, width, height);
// Vertex shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
// 绑定源文件,第一个创建的着色器,第二个是绑定了多少个glsl,第三个是glsl源文件
// 最后一个是每个段占多少字符,因为就一个文件,所以可以不填
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
// 编译着色器对象
glCompileShader(vertexShader);
// 处理error
// 因为着色器很可能会写错,而着色器出错的log需要手动输出
GLint success;
GLchar infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// 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);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// 链接 shaders
GLuint 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);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// 删除着色器对象
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// 顶点buffer
GLfloat vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
// VAO:Vertex Array Object
// 其实就是索引,因为可能会有很多不同的VBO,如果每个都生成一个索引太麻烦
// VBO:vertex buffer object
// 每个vertex的具体数据
GLuint VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// 绑定VAO
glBindVertexArray(VAO);
// 绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 绑定数组
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 确定VAO具体参数
// 第一个参数是着色器中的layout,即这个数组对应着色器中的哪个属性
// 第二个参数是指定顶点属性大小,代表一个顶点三个数字vec3
// 第三个参数是数据类型
// 第四个参数确定是否将数据映射到0-1之间
// 第五个参数是步长,代表下一个数据在多少byte之后
// 第六个数据是偏移量,即数组开始的位置
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
// 开启VAO,参数是index,这个index其实就是绑定的VBO的layout
glEnableVertexAttribArray(0);
// 我觉得应该是VAO已经通过VBO绑定了,只有在更换VBO的时候才需要换绑
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)
// main loop
while (!glfwWindowShouldClose(window))
{
// 轮询事件
glfwPollEvents();
// 清空颜色
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 使用编译好的着色器程序
glUseProgram(shaderProgram);
// 绑定VAO
glBindVertexArray(VAO);
//绘制三角形,第二个参数是数组起始位置,第三个参数是定点数量
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
// double buffer
glfwSwapBuffers(window);
}
// 删除VAO和VBO
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
// 释放窗口资源
glfwTerminate();
return 0;
}
额外使用写一个shader类帮忙读取着色器
main:
#include <iostream>
#include <shader.h>
// GLEW
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
// Window size
const int Width = 800, Height = 600;
// 当用户按下ESC键,我们设置window窗口的WindowShouldClose属性为true,关闭应用程序
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
int main(int argc, char** argv)
{
// Init GLFW
glfwInit();
// 设置opengl基本设置
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//最高版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//最低版本
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//Core file
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);//不可以调整窗口大小
// 创建窗口
GLFWwindow* window = glfwCreateWindow(Width, Height, "A Triangle", nullptr, nullptr);
glfwMakeContextCurrent(window);
// 绑定按键回调函数
glfwSetKeyCallback(window, key_callback);
// 开启modern opengl
glewExperimental = GL_TRUE;
// 初始化glew(modern opengl)
glewInit();
// 设置窗口大小
int width, height;
glfwGetFramebufferSize(window, &width, &height);
glViewport(0, 0, width, height);
Shader ourShader("Shaders/shader.vert", "Shaders/shader.frag");
// 顶点buffer
GLfloat vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
// VAO:Vertex Array Object
// 其实就是索引,因为可能会有很多不同的VBO,如果每个都生成一个索引太麻烦
// VBO:vertex buffer object
// 每个vertex的具体数据
GLuint VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// 绑定VAO
glBindVertexArray(VAO);
// 绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 绑定数组
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 确定VAO具体参数
// 第一个参数是着色器中的layout,即这个数组对应着色器中的哪个属性
// 第二个参数是指定顶点属性大小,代表一个顶点三个数字vec3
// 第三个参数是数据类型
// 第四个参数确定是否将数据映射到0-1之间
// 第五个参数是步长,代表下一个数据在多少byte之后
// 第六个数据是偏移量,即数组开始的位置
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
// 开启VAO,参数是index,这个index其实就是绑定的VBO的layout
glEnableVertexAttribArray(0);
// 我觉得应该是VAO已经通过VBO绑定了,只有在更换VBO的时候才需要换绑
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)
// main loop
while (!glfwWindowShouldClose(window))
{
// 轮询事件
glfwPollEvents();
// 清空颜色
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 使用编译好的着色器程序
ourShader.Use();
// 绑定VAO
glBindVertexArray(VAO);
//绘制三角形,第二个参数是数组起始位置,第三个参数是定点数量
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
// double buffer
glfwSwapBuffers(window);
}
// 删除VAO和VBO
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
// 释放窗口资源
glfwTerminate();
return 0;
}
shader.vertx
#version 330 core
layout (location = 0) in vec3 position;
void main()
{
gl_Position = vec4(position.x, position.y, position.z, 1.0);
};
shader.frag
#version 330 core
out vec4 color;
void main()
{
color = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
shader.h
#pragma once
#ifndef SHADER_H
#define SHADER_H
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <GL/glew.h>
class Shader
{
public:
GLuint Program;
// Constructor generates the shader on the fly
Shader(const GLchar* vertexPath, const GLchar* fragmentPath)
{
// 1. Retrieve the vertex/fragment source code from filePath
std::string vertexCode;
std::string fragmentCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
// ensures ifstream objects can throw exceptions:
vShaderFile.exceptions(std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::badbit);
try
{
// Open files
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// Read file's buffer contents into streams
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// close file handlers
vShaderFile.close();
fShaderFile.close();
// Convert stream into string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const GLchar* vShaderCode = vertexCode.c_str();
const GLchar* fShaderCode = fragmentCode.c_str();
// 2. Compile shaders
GLuint vertex, fragment;
GLint success;
GLchar infoLog[512];
// Vertex Shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
// Print compile errors if any
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertex, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// Fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
// Print compile errors if any
glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragment, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// Shader Program
this->Program = glCreateProgram();
glAttachShader(this->Program, vertex);
glAttachShader(this->Program, fragment);
glLinkProgram(this->Program);
// Print linking errors if any
glGetProgramiv(this->Program, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(this->Program, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// Delete the shaders as they're linked into our program now and no longer necessery
glDeleteShader(vertex);
glDeleteShader(fragment);
}
// Uses the current shader
void Use()
{
glUseProgram(this->Program);
}
};
#endif