OpenGL管线:C++/OpenGL应用将图形数据发送到顶点着色器,随着管线处理,最终生成显示器上显示的像素点,顶点着色器、曲面细分着色器、几何着色器、片段着色器可以使用GLSL编程,将GLSL程序载入这些着色器也是C++/OpenGL职责:
step1、首先使用C++获取GLSL着色器代码,既可以从文件中读取,也可以硬编码在字符串中;
step2、接下来创建OpenGL着色器对象并将GLSL着色器代码加载到着色器对象;
step3、最后使用OpenGL命令编译并连接着色器对,并将他们安装到GPU;
1.1 创建一个GLFWwindow实例并设置其背景色
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <iostream>
using namespace std;
void init(GLFWwindow* window) { } //do nothing
void display(GLFWwindow* window, double currentTime) {
glClearColor(1.0, 0.0, 0.0, 1.0); //glClearColor能够指定颜色缓冲区清除后的值, (1,0,0,1)红色
glClear(GL_COLOR_BUFFER_BIT); //GL_COLOR_BUFFER_BIT应用了包含渲染后像素的颜色缓冲区,OPENGL有很多颜色缓冲区
}
int main(void) {
if (!glfwInit()) { exit(EXIT_FAILURE); } //GLFW使用glfwinit初始化
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //WindowHints指定必须与OpenGL版本4.3兼容
GLFWwindow* window = glfwCreateWindow(600, 600, "Harry's hobby display21", NULL, NULL); //创建指定宽高(以像素为单位)及窗口顶部的标题
glfwMakeContextCurrent(window); //使创建的窗口与OPENGL上下文关联起来
if (glewInit() != GLEW_OK) { exit(EXIT_FAILURE); }
glfwSwapInterval(1); //使用glfwSwapInterval和glfwSwapBuffers用来开启垂直同步Vsync
init(window);
while (!glfwWindowShouldClose(window)) { //简单的渲染循环
display(window, glfwGetTime());
glfwSwapBuffers(window);
glfwPollEvents(); //处理窗口相关事件,探测到关闭窗口的事件,循环就会终止
}
glfwDestroyWindow(window); //关闭窗口时,程序将退出渲染,此时通过glfwDestroyWindow和glfwTerminate通知GLFW销毁窗口以及终止运行
glfwTerminate();
exit(EXIT_SUCCESS);
}
显示结果
1.2 着色器画一个点
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <iostream>
using namespace std;
#define numVAOs 1 //即使应用程序完全没有用到任何缓冲区,OpenGL仍然需要在使用着色器的时候至少有一个创建按好的VAO,顶点数组对象
GLuint renderingProgram;
GLuint vao[numVAOs];
GLuint createShaderProgram() {
const char* vshaderSource =
"#version 430 \n"
"void main(void) \n"
"{ gl_Position = vec4(0.0, 0.0, 0.0, 1.0);}"; //坐标原点,顶点接下来将沿着管线移动到光栅着色器,会在这里转换成像素位置【片面】
const char* fshaderSource =
"#version 430 \n"
"out vec4 color: \n"
"void main(void) \n"
"{ color = vec4(0.0, 0.0, 1.0, 1.0); }"; //最终这些像素达到片段着色器
GLuint vShader = glCreateShader(GL_VERTEX_SHADER);
GLuint fShader = glCreateShader(GL_FRAGMENT_SHADER);
GLuint vfprogram = glCreateProgram();
glShaderSource(vShader, 1, &vshaderSource, NULL); //将GLSL代码从字符串载入空着色器对象中
glShaderSource(fShader, 1, &fshaderSource, NULL); //glShaderSource含有四个参数,1用来存放着色器的着色器对象,2表示着色器源码中的字符串数量
//3表示包含源代码的字符串指针
glCompileShader(vShader); //使用glCompileShader编译着色器对象
glCompileShader(fShader);
glAttachShader(vfprogram, vShader); //将着色器对象加入到程序对象
glAttachShader(vfprogram, fShader);
glLinkProgram(vfprogram); //请求GLSL编译器确保兼容性
return vfprogram;
}
void init(GLFWwindow* window) {
renderingProgram = createShaderProgram();
glGenVertexArrays(numVAOs, vao);
glBindVertexArray(vao[0]);
}
void display(GLFWwindow* window, double currentTime){
glUseProgram(renderingProgram); //将含有两个已完成编译着色器程序载入OpenGL管线阶段,glUseProgram并没有运行着色器,只是将着色器加载进GPU
glPointSize(30.0f); //从顶点着色器出来的顶点如何转换成片段着色器中的像素?在顶点处理和像素处理之间存在光栅化阶段,这个阶段图元转换成像素集合,30像素
glDrawArrays(GL_POINTS, 0, 1); //启动管线处理
}
int main(void) {
if (!glfwInit()) { exit(EXIT_FAILURE); } //GLFW使用glfwinit初始化
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //WindowHints指定必须与OpenGL版本4.3兼容
GLFWwindow* window = glfwCreateWindow(600, 600, "harry's hobby- Rendering 22", NULL, NULL); //创建指定宽高(以像素为单位)及窗口顶部的标题
glfwMakeContextCurrent(window); //使创建的窗口与OPENGL上下文关联起来
if (glewInit() != GLEW_OK) { exit(EXIT_FAILURE); }
glfwSwapInterval(1); //使用glfwSwapInterval和glfwSwapBuffers用来开启垂直同步Vsync
init(window);
while (!glfwWindowShouldClose(window)) { //简单的渲染循环
display(window, glfwGetTime());
glfwSwapBuffers(window);
glfwPollEvents(); //处理窗口相关事件,探测到关闭窗口的事件,循环就会终止
}
glfwDestroyWindow(window); //关闭窗口时,程序将退出渲染,此时通过glfwDestroyWindow和glfwTerminate通知GLFW销毁窗口以及终止运行
glfwTerminate();
exit(EXIT_SUCCESS);
}
显示结果
1.3 着色器画一个三角形
/*Utils.h*/
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <SOIL2\soil2.h>
#include <string>
#include <iostream>
#include <fstream>
#include <cmath>
#include <vector>
#include <glm\glm.hpp>
#include <glm\gtc\type_ptr.hpp>
#include <glm\gtc\matrix_transform.hpp>
class Utils
{
private:
static std::string readShaderFile(const char *filePath);
static void printShaderLog(GLuint shader);
static void printProgramLog(int prog);
static GLuint prepareShader(int shaderTYPE, const char *shaderPath);
static int finalizeShaderProgram(GLuint sprogram);
public:
Utils();
static bool checkOpenGLError();
static GLuint createShaderProgram(const char *vp, const char *fp);
static GLuint createShaderProgram(const char *vp, const char *gp, const char *fp);
static GLuint createShaderProgram(const char *vp, const char *tCS, const char* tES, const char *fp);
static GLuint createShaderProgram(const char *vp, const char *tCS, const char* tES, char *gp, const char *fp);
static GLuint loadTexture(const char *texImagePath);
static GLuint loadCubeMap(const char *mapDir);
static float* goldAmbient();
static float* goldDiffuse();
static float* goldSpecular();
static float goldShininess();
static float* silverAmbient();
static float* silverDiffuse();
static float* silverSpecular();
static float silverShininess();
static float* bronzeAmbient();
static float* bronzeDiffuse();
static float* bronzeSpecular();
static float bronzeShininess();
};
//*********************************************************************
/*Utils.cpp*/
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <SOIL2\soil2.h>
#include <string>
#include <iostream>
#include <fstream>
#include <cmath>
#include <glm\glm.hpp>
#include <glm\gtc\type_ptr.hpp> // glm::value_ptr
#include <glm\gtc\matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective
#include "Utils.h"
using namespace std;
Utils::Utils() {}
string Utils::readShaderFile(const char *filePath) {
string content;
ifstream fileStream(filePath, ios::in);
string line = "";
while (!fileStream.eof()) {
getline(fileStream, line);
content.append(line + "\n");
}
std::cout << content;
fileStream.close();
return content;
}
bool Utils::checkOpenGLError() {
bool foundError = false;
int glErr = glGetError();
while (glErr != GL_NO_ERROR) {
cout << "glError: " << glErr << endl;
foundError = true;
glErr = glGetError();
}
return foundError;
}
void Utils::printShaderLog(GLuint shader) {
int len = 0;
int chWrittn = 0;
char *log;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
if (len > 0) {
log = (char *)malloc(len);
glGetShaderInfoLog(shader, len, &chWrittn, log);
cout << "Shader Info Log: " << log << endl;
free(log);
}
}
GLuint Utils::prepareShader(int shaderTYPE, const char *shaderPath)
{
GLint shaderCompiled;
string shaderStr = readShaderFile(shaderPath);
const char *shaderSrc = shaderStr.c_str();
GLuint shaderRef = glCreateShader(shaderTYPE);
glShaderSource(shaderRef, 1, &shaderSrc, NULL);
glCompileShader(shaderRef);
checkOpenGLError();
glGetShaderiv(shaderRef, GL_COMPILE_STATUS, &shaderCompiled);
if (shaderCompiled != 1)
{
if (shaderTYPE == 35633) cout << "Vertex ";
if (shaderTYPE == 36488) cout << "Tess Control ";
if (shaderTYPE == 36487) cout << "Tess Eval ";
if (shaderTYPE == 36313) cout << "Geometry ";
if (shaderTYPE == 35632) cout << "Fragment ";
cout << "shader compilation error." << endl;
printShaderLog(shaderRef);
}
return shaderRef;
}
void Utils::printProgramLog(int prog) {
int len = 0;
int chWrittn = 0;
char *log;
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len);
if (len > 0) {
log = (char *)malloc(len);
glGetProgramInfoLog(prog, len, &chWrittn, log);
cout << "Program Info Log: " << log << endl;
free(log);
}
}
int Utils::finalizeShaderProgram(GLuint sprogram)
{
GLint linked;
glLinkProgram(sprogram);
checkOpenGLError();
glGetProgramiv(sprogram, GL_LINK_STATUS, &linked);
if (linked != 1)
{
cout << "linking failed" << endl;
printProgramLog(sprogram);
}
return sprogram;
}
GLuint Utils::createShaderProgram(const char *vp, const char *fp) {
GLuint vShader = prepareShader(GL_VERTEX_SHADER, vp);
GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fp);
GLuint vfprogram = glCreateProgram();
glAttachShader(vfprogram, vShader);
glAttachShader(vfprogram, fShader);
finalizeShaderProgram(vfprogram);
return vfprogram;
}
GLuint Utils::createShaderProgram(const char *vp, const char *gp, const char *fp) {
GLuint vShader = prepareShader(GL_VERTEX_SHADER, vp);
GLuint gShader = prepareShader(GL_GEOMETRY_SHADER, gp);
GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fp);
GLuint vgfprogram = glCreateProgram();
glAttachShader(vgfprogram, vShader);
glAttachShader(vgfprogram, gShader);
glAttachShader(vgfprogram, fShader);
finalizeShaderProgram(vgfprogram);
return vgfprogram;
}
GLuint Utils::createShaderProgram(const char *vp, const char *tCS, const char* tES, const char *fp) {
GLuint vShader = prepareShader(GL_VERTEX_SHADER, vp);
GLuint tcShader = prepareShader(GL_TESS_CONTROL_SHADER, tCS);
GLuint teShader = prepareShader(GL_TESS_EVALUATION_SHADER, tES);
GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fp);
GLuint vtfprogram = glCreateProgram();
glAttachShader(vtfprogram, vShader);
glAttachShader(vtfprogram, tcShader);
glAttachShader(vtfprogram, teShader);
glAttachShader(vtfprogram, fShader);
finalizeShaderProgram(vtfprogram);
return vtfprogram;
}
GLuint Utils::createShaderProgram(const char *vp, const char *tCS, const char* tES, char *gp, const char *fp) {
GLuint vShader = prepareShader(GL_VERTEX_SHADER, vp);
GLuint tcShader = prepareShader(GL_TESS_CONTROL_SHADER, tCS);
GLuint teShader = prepareShader(GL_TESS_EVALUATION_SHADER, tES);
GLuint gShader = prepareShader(GL_GEOMETRY_SHADER, gp);
GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fp);
GLuint vtgfprogram = glCreateProgram();
glAttachShader(vtgfprogram, vShader);
glAttachShader(vtgfprogram, tcShader);
glAttachShader(vtgfprogram, teShader);
glAttachShader(vtgfprogram, gShader);
glAttachShader(vtgfprogram, fShader);
finalizeShaderProgram(vtgfprogram);
return vtgfprogram;
}
GLuint Utils::loadCubeMap(const char *mapDir) {
GLuint textureRef;
string xp = mapDir; xp = xp + "/xp.jpg";
string xn = mapDir; xn = xn + "/xn.jpg";
string yp = mapDir; yp = yp + "/yp.jpg";
string yn = mapDir; yn = yn + "/yn.jpg";
string zp = mapDir; zp = zp + "/zp.jpg";
string zn = mapDir; zn = zn + "/zn.jpg";
textureRef = SOIL_load_OGL_cubemap(xp.c_str(), xn.c_str(), yp.c_str(), yn.c_str(), zp.c_str(), zn.c_str(),
SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS);
if (textureRef == 0) cout << "didnt find cube map image file" << endl;
// glBindTexture(GL_TEXTURE_CUBE_MAP, textureRef);
// reduce seams
// glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
// glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
return textureRef;
}
GLuint Utils::loadTexture(const char *texImagePath)
{
GLuint textureRef;
textureRef = SOIL_load_OGL_texture(texImagePath, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
if (textureRef == 0) cout << "didnt find texture file " << texImagePath << endl;
// ----- mipmap/anisotropic section
glBindTexture(GL_TEXTURE_2D, textureRef);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
if (glewIsSupported("GL_EXT_texture_filter_anisotropic")) {
GLfloat anisoset = 0.0f;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisoset);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoset);
}
// ----- end of mipmap/anisotropic section
return textureRef;
}
// GOLD material - ambient, diffuse, specular, and shininess
float* Utils::goldAmbient() { static float a[4] = { 0.2473f, 0.1995f, 0.0745f, 1 }; return (float*)a; }
float* Utils::goldDiffuse() { static float a[4] = { 0.7516f, 0.6065f, 0.2265f, 1 }; return (float*)a; }
float* Utils::goldSpecular() { static float a[4] = { 0.6283f, 0.5559f, 0.3661f, 1 }; return (float*)a; }
float Utils::goldShininess() { return 51.2f; }
// SILVER material - ambient, diffuse, specular, and shininess
float* Utils::silverAmbient() { static float a[4] = { 0.1923f, 0.1923f, 0.1923f, 1 }; return (float*)a; }
float* Utils::silverDiffuse() { static float a[4] = { 0.5075f, 0.5075f, 0.5075f, 1 }; return (float*)a; }
float* Utils::silverSpecular() { static float a[4] = { 0.5083f, 0.5083f, 0.5083f, 1 }; return (float*)a; }
float Utils::silverShininess() { return 51.2f; }
// BRONZE material - ambient, diffuse, specular, and shininess
float* Utils::bronzeAmbient() { static float a[4] = { 0.2125f, 0.1275f, 0.0540f, 1 }; return (float*)a; }
float* Utils::bronzeDiffuse() { static float a[4] = { 0.7140f, 0.4284f, 0.1814f, 1 }; return (float*)a; }
float* Utils::bronzeSpecular() { static float a[4] = { 0.3936f, 0.2719f, 0.1667f, 1 }; return (float*)a; }
float Utils::bronzeShininess() { return 25.6f; }
//************************************************************************************
/*main.cpp*/
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <iostream>
#include <string>
#include <iostream>
#include <fstream>
#include "Utils.h"
using namespace std;
#define numVAOs 1
GLuint renderingProgram;
GLuint vao[numVAOs];
void init(GLFWwindow* window) {
renderingProgram = Utils::createShaderProgram("vertShader.glsl", "fragShader.glsl");
glGenVertexArrays(numVAOs, vao);
glBindVertexArray(vao[0]);
}
void display(GLFWwindow* window, double currentTime) {
glUseProgram(renderingProgram);
glDrawArrays(GL_TRIANGLES, 0, 3); //制定管线中含有3个顶点,顶点着色器会在每个迭代运行3遍,内置变脸gl_VertexID会自增
}
int main(void) {
if (!glfwInit()) { exit(EXIT_FAILURE); }
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFWwindow* window = glfwCreateWindow(600, 600, "harry's hobby- Rendering 23", NULL, NULL);
glfwMakeContextCurrent(window);
if (glewInit() != GLEW_OK) { exit(EXIT_FAILURE); }
glfwSwapInterval(1);
init(window);
while (!glfwWindowShouldClose(window)) {
display(window, glfwGetTime());
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
显示结果:
1.4 重复渲染移动三角形
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <iostream>
#include <string>
#include <iostream>
#include <fstream>
#include "Utils.h"
using namespace std;
#define numVAOs 1
GLuint renderingProgram;
GLuint vao[numVAOs];
GLuint offsetLoc;
float x = 0.0f; //三角形在x轴的位置
float inc = 0.01f; //移动三角形的偏移量
void init(GLFWwindow* window) {
renderingProgram = Utils::createShaderProgram("vertShader.glsl", "fragShader.glsl");
glGenVertexArrays(numVAOs, vao);
glBindVertexArray(vao[0]);
}
void display(GLFWwindow* window, double currentTime) {
glClear(GL_DEPTH_BUFFER_BIT);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(renderingProgram);
x += inc;
if (x > 1.0f) inc = -0.01f;
if (x < -1.0f) inc = 0.01f;
offsetLoc = glGetUniformLocation(renderingProgram, "offset"); //获取offset指针
glProgramUniform1f(renderingProgram, offsetLoc, x); //将x值传给offset
glDrawArrays(GL_TRIANGLES, 0, 3);
}
int main(void) {
if (!glfwInit()) { exit(EXIT_FAILURE); }
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFWwindow* window = glfwCreateWindow(600, 600, "Chapter 2 - program 5", NULL, NULL);
glfwMakeContextCurrent(window);
if (glewInit() != GLEW_OK) { exit(EXIT_FAILURE); }
glfwSwapInterval(1);
init(window);
while (!glfwWindowShouldClose(window)) {
display(window, glfwGetTime());
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
显示结果