阅读《计算机图形学编程(使用OpenGL和C++)》3
2022/1/28 20:10:05
本文主要是介绍阅读《计算机图形学编程(使用OpenGL和C++)》3,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
GLSL运行在GPU上,想要调试需要一个用于捕获和显示GLSL错误的模块。
Utils.h
#pragma once #include "GL\glew.h" #include <string> class Utils { public: Utils(); ~Utils();static void printShaderLog(GLuint shader); static void printProgramLog(int prog); static bool checkOpenGLError(); static GLuint createShaderProgram(); };
Utils.cpp
#include "Utils.h" #include <iostream> #include <fstream> #include <SOIL2/SOIL2.h> Utils::Utils() { } Utils::~Utils() { } // 当GLSL编译失败时,显示OpenGL日志内容 void Utils::printShaderLog(GLuint shader) { int len = 0; int chWritten = 0; char *log; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); // 提供有关编译过的GLSL着色器的信息 if (len > 0) { log = (char *)malloc(len); glGetShaderInfoLog(shader, len, &chWritten, log); std::cout << "Shader Info Log: " << log << std::endl; free(log); } } // 当GLSL链接失败时,显示OpenGL日志内容 void Utils::printProgramLog(int prog) { int len = 0; int chWritten = 0; char *log; glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len); // 提供有关编译过的程序的信息 if (len > 0) { log = (char *)malloc(len); glGetProgramInfoLog(prog, len, &chWritten, log); std::cout << "Program Info Log: " << log << std::endl; free(log); } } // 检查OpenGL错误标志,即是否发生OpenGL错误,既用于检测GLSL编译错误,又用于检测OpenGL运行时的错误 bool Utils::checkOpenGLError() { bool foundError = false; int glErr = glGetError(); while (glErr != GL_NO_ERROR) { std::cout << "glError: " << glErr << std::endl; foundError = true; glErr = glGetError(); } return foundError; } GLuint Utils::createShaderProgram() { GLint vertCompiled; GLint fragCompiled; GLint linked; const char* vshaderSource = "#version 460 \n" "void main(void) \n" "{ gl_Position = vec4(0.0, 0.0, 0.0, 1.0); }"; const char* fshaderSource = "#version 460 \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); glShaderSource(vShader, 1, &vshaderSource, NULL); glShaderSource(fShader, 1, &fshaderSource, NULL); // 捕获编译着色器时的错误 glCompileShader(vShader); checkOpenGLError(); glGetShaderiv(vShader, GL_COMPILE_STATUS, &vertCompiled); if (vertCompiled != 1) { std::cout << "vertex compilation failed" << std::endl; printShaderLog(vShader); } glCompileShader(fShader); checkOpenGLError(); glGetShaderiv(fShader, GL_COMPILE_STATUS, &fragCompiled); if (fragCompiled != 1) { std::cout << "fragment compilation failed" << std::endl; printShaderLog(fShader); } GLuint vfProgram = glCreateProgram(); // 捕获链接着色器时的错误 glAttachShader(vfProgram, vShader); glAttachShader(vfProgram, fShader); glLinkProgram(vfProgram); checkOpenGLError(); glGetProgramiv(vfProgram, GL_LINK_STATUS, &linked); if (linked != 1) { std::cout << "linking failed" << std::endl; printProgramLog(vfProgram); } return vfProgram; }
当程序更复杂时,GLSL着色器代码存储在字符串中就不可取了,我们应当将着色器代码存在文件中并读入它们。
顶点着色器和片段着色器代码分别放在 "vertShader.glsl" 和 "fragShader.glsl" 中。
Utils.h 增加如下代码:
static std::string readShaderSource(const char *filePath); static GLuint createShaderProgram(const char *vp, const char *fp);
Utils.cpp
// 用于读取着色器的代码 std::string Utils::readShaderSource(const char * filePath) { std::string content; std::ifstream fileStream(filePath, std::ios::in); std::string line = ""; while (!fileStream.eof()) { getline(fileStream, line); content.append(line + "\n"); } fileStream.close(); return content; } // 读取着色器代码并构建渲染程序,该类很适合重载 GLuint Utils::createShaderProgram(const char * vp, const char * fp) { GLint vertCompiled; GLint fragCompiled; GLint linked; std::string vertShaderStr = readShaderSource(vp); std::string fragShaderStr = readShaderSource(fp); const char * vertShaderSrc = vertShaderStr.c_str(); const char * fragShaderSrc = fragShaderStr.c_str(); GLuint vShader = glCreateShader(GL_VERTEX_SHADER); GLuint fShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(vShader, 1, &vertShaderSrc, NULL); glShaderSource(fShader, 1, &fragShaderSrc, NULL); glCompileShader(vShader); checkOpenGLError(); glGetShaderiv(vShader, GL_COMPILE_STATUS, &vertCompiled); if (vertCompiled != 1) { std::cout << "vertex compilation failed" << std::endl; printShaderLog(vShader); } glCompileShader(fShader); checkOpenGLError(); glGetShaderiv(fShader, GL_COMPILE_STATUS, &fragCompiled); if (fragCompiled != 1) { std::cout << "fragment compilation failed" << std::endl; printShaderLog(fShader); } GLuint vfProgram = glCreateProgram(); glAttachShader(vfProgram, vShader); glAttachShader(vfProgram, fShader); glLinkProgram(vfProgram); checkOpenGLError(); glGetProgramiv(vfProgram, GL_LINK_STATUS, &linked); if (linked != 1) { std::cout << "linking failed" << std::endl; printProgramLog(vfProgram); } return vfProgram; }
从文件着色器读取并生成一个三角形的程序,如下:
顶点着色器,vertShader.glsl
#version 460 void main(void) { if (gl_VertexID == 0) gl_Position = vec4(0.25, -0.25, 0.0, 1.0); else if (gl_VertexID == 1) gl_Position = vec4(-0.25, -0.25, 0.0, 1.0); else gl_Position = vec4(0.25, 0.25, 0.0, 1.0);}
display() 函数中加入代码
... glDrawArrays(GL_TRIANGLES, 0, 3);
结果如下:
可以使三角形动起来实现动画效果,我们在 main() 函数中只调用了 init() 一次,之后重复调用 display(),在循环中可以一次又一次绘制,只要设计 display() 函数随时间改变绘制的东西。
场景的每一次绘制都叫作一帧,调用 display() 的频率叫作帧率。在程序逻辑中移动的速率可以通过自前一帧到目前经过的时间来控制。
main.cpp
// #include和定义与之前相同 float x = 0.0f; // 三角形在x轴的位置 float inc = 1.0f; // 移动三角形的偏移量 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) // 沿x轴移动三角形 inc = -0.01f; if (x < -1.0f) // 切换至让三角形向右移动 inc = 0.01f; GLuint offsetLoc = glGetUniformLocation(renderingProgram, "offset"); // 获取"offset"指针 glProgramUniform1f(renderingProgram, offsetLoc, x); // 将"x"中的值传给"offset" glDrawArrays(GL_TRIANGLES, 0, 3); } // 其余函数同前
顶点着色器
#version 460 uniform float offset; void main(void) { if (gl_VertexID == 0) gl_Position = vec4(0.25 + offset, -0.25, 0.0, 1.0); else if (gl_VertexID == 1) gl_Position = vec4(-0.25 + offset, -0.25, 0.0, 1.0); else gl_Position = vec4(0.25 + offset, 0.25, 0.0, 1.0);}
结果如下:
这篇关于阅读《计算机图形学编程(使用OpenGL和C++)》3的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享