前言
练习使用Markdown编辑器,回顾OpenGL基本环境的建立。
OpenGL渲染
程序从文件中读取数据(顶点、纹理等)并载入到缓冲区中,然后将数据发送给GLSL,经过顶点着色器、曲面细分着色器(可选)、几何着色器(可选)、片段着色器,最终发送到屏幕上进行绘制。在顶点着色器中,程序“按顶点”处理数据;在几何着色器中,程序“按图元”处理数据;在片段着色器中,程序“按像素”处理数据。
以前忽视的地方:在调用glfwDrawArrays()函数时,管线中的GLSL代码才开始执行。C++/OpenGL先将数据加载到缓冲区中,在调用了该函数后,才开始进入顶点着色器处理,图元装配、光栅化等阶段,最后给像素上色,然后输出到屏幕空间。之前一直以为顶点一开始就进入到所谓VAO,VBO,EBO时就有着色器参与处理。慢慢理解清楚,之后写代码就可以更好地分模块了,免得全写在main里面,太不清晰了。
OpenGL环境配置
之前都是看网上的教程,下载glfw,glew,glm之类的,然后在项目属性中添加对应的包含目录和库目录,还要在链接器里添加相应的.lib文件,实在是麻烦。后来看到不知道哪一篇文章里,说直接使用VS的NuGet包管理器,搜索NupenGL安装后就可以直接使用了。瞬间打开任督二脉,再也不用担心环境配不对了哈哈哈。不过有些时候好像还是要链接opengl32.lib?
代码
//代码段
#include<GL/glew.h>
#include<GLFW/glfw3.h>
#include<iostream>
using namespace std;
GLuint VAO,renderProgram;
GLuint createShaderProgram(){
const char *vShaderSource=
"#version 430 \n"
"void main(){ \n"
"gl_Position = vec4(vec3(0.0), 1.0); \n"
"}";
const char *fShaderSource=
"#version 4.0 \n"
"out vec4 fragColor; \n"
"void main(){ \n"
"fragColor= vec4(1.0, 0.0, 0.0, 1.0); \n"
"}";
GLuint vShader = glCreateShader(GL_VERTEX_SHADER); //创建空着色器对象并返回该对象的ID
GLuint FShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vShader, 1, &vShaderSource, NULL); //将着色器代码载入上述对象中
glShaderSource(fShader, 1, &fShaderSource, NULL);
glCompileShader(vShader); //编译着色器
glCompileShader(fShader);
GLuint shaderProgram = glCreateProgram(); //创建着色器程序对象并返回该对象的ID
glAttachShader(shaderProgram, vShader); //将着色器代码绑定到上述程序对象上
glAttachShader(shaderProgram, fShader);
glLinkProgram(shaderProgram); //链接着色器程序
return shaderProgram; //函数返回值是创建的着色器程序的ID
}
void init(GLFWwindow*window){
renderProgram = createProgram();
glGenVertexArray(1, &VAO);
glBindVertexArray(VAO);
}
void display(GLFWwindow*window, double currentTime){
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(renderProgram); //使用着色器程序
glDrawArrays(GL_POINTS, 0, 1);
}
void main(){
if(!glfwInit()){ exit(EXIT_FAILURE); }
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); //主次版本号,和GLSL程序中一致
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFWwindow* window = glfwCreateWindow(960, 480, "myWindow", NULL, NULL);
glfwMakeContextCurrent(window);
if(!glewInit()){ exit(EXIT_FAILURE); }
glfwSwapInterval(1); //此函数作用尚未明确,之前程序中没使用也不影响
init(window);
while(!glfwWindowShouldClose(window)){
display(window, glfwGetTime());
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminte();
exit(EXIT_SUCCESS);
}
上述代码使用在屏幕空间上输出一个红色的点,该点的位置使用硬编码方式写在GLSL顶点着色器程序中。着色器创建、加载、链接的过程是固定的,可以单独封装成一个类,参见learnOpenGL CN。注意即使只使用硬编码方式渲染一个点而没有经过缓冲区,也需要定义至少一个顶点数组对象,这是必须的。