最近学图形学学到了shader,刚开始接触简直要吐了,但是看过几个例子程序后逐渐习惯了,也体会到用shader要远比正常绘制香很多,尤其是当数据过大时,但是他有一套繁琐的机制,在看过很多不同教程后我总结了一套程序框架,重点还是在于shader的配置。
void setShaders(void);
其中有对shader的基本配置链接等等超全的集合。
void printShaderInfoLog(GLuint shaderObject);
获取shader文件的日志
void printProgramInfoLog(GLuint programObject);
获取着色器程序的日志
char* textFileRead(const char *textFileName);
让着色器文件的代码以字符串形式返回
还有就是别忘了在渲染循环中启用着色器程序glUseProgram(着色器程序名称);
#include
#include
#include
using namespace std;
//当用户改变窗口的大小的时候,视口也应该被调整。我们可以对窗口注册一个回调函数(Callback Function),它会在每次窗口大小被调整的时候被调用
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);//键盘交互
//shader相关函数
void setShaders(void);
void printShaderInfoLog(GLuint shaderObject);
void printProgramInfoLog(GLuint programObject);
char* textFileRead(const char *textFileName);
// settings
GLuint G_vShader_simple;
GLuint G_fShader_simple;
GLuint G_shaderProgram;
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
int main()
{
//初始化GLFW
glfwInit();
//我们将主版本号(Major)和次版本号(Minor)都设为3
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//明确告诉GLFW我们使用的是核心模式(Core-profile)
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// glfw window creation
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
//glfwCreateWindow(int width,int height,const char * title,GLFWmonitor * monitor,GLFWwindow * share);
if (window == NULL)//如果窗口创建失败,NULL将被返回,因此有必要检查返回值。
{
cout << "Failed to create GLFW window" <<endl;
glfwTerminate();
return -1;
}
//通知GLFW将我们窗口的上下文设置为当前线程的主上下文
glfwMakeContextCurrent(window);
//告诉GLFW我们希望每当窗口调整大小的时候调用这个函数(相当于freeglut的glutReshapeFunc(myReshape);)
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
//GLAD是用来管理OpenGL的函数指针的,所以在调用任何OpenGL的函数之前我们需要初始化GLAD。
//我们给GLAD传入了用来加载系统相关的OpenGL函数指针地址的函数。GLFW给我们的是glfwGetProcAddress,它根据我们编译的系统定义了正确的函数。
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
cout << "Failed to initialize GLAD" << endl;
return -1;
}
//设置着色器
setShaders();
//在这里可以初始化想画东西的数据
// 渲染循环(Render Loop)
while (!glfwWindowShouldClose(window))//glfwWindowShouldClose函数在我们每次循环的开始前检查一次GLFW是否被要求退出
{
//键盘交互事件。
processInput(window);
//正常绘制
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(G_shaderProgram);//激活这个程序对象
//与shader建立联系(可有可无)
//绘制
glfwPollEvents();//glfwPollEvents函数检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数
glfwSwapBuffers(window);//glfwSwapBuffers函数会交换颜色缓冲
}
//当渲染循环结束后我们需要正确释放/删除之前的分配的所有资源
glfwTerminate();
return 0;
}
//键盘交互
void processInput(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)//检查用户是否按下了返回键(Esc)
glfwSetWindowShouldClose(window, true);//关闭窗口
}
//当用户改变窗口的大小的时候,视口也应该被调整。我们可以对窗口注册一个回调函数(Callback Function),它会在每次窗口大小被调整的时候被调用
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
void setShaders(void)
{
char *vs,*fs;
//创建着色器对象vs和fs
G_vShader_simple = glCreateShader(GL_VERTEX_SHADER);
G_fShader_simple = glCreateShader(GL_FRAGMENT_SHADER);
//把vs、fs以字符串的形式读入到两个char*变量中
vs = textFileRead("simple.vert");
fs = textFileRead("simple.frag");
const char *vv = vs;
const char *ff = fs;
//把着色器源码附加到vs、fs的着色器对象上,第二参数指定了传递的源码字符串数量
glShaderSource(G_vShader_simple, 1, &vv, NULL);
glShaderSource(G_fShader_simple, 1, &ff, NULL);
free(vs);
free(fs);
/////////////////////////////////////////////////////////
//编译着色器中的内容
glCompileShader(G_vShader_simple);
glCompileShader(G_fShader_simple);
//你可能会希望检测在调用glCompileShader后编译是否成功了,
//如果没成功的话,你还会希望知道错误是什么,这样你才能修复它们。
//检测编译时错误可以通过以下代码来实现:
int checkResult;
glGetShaderiv(G_vShader_simple, GL_COMPILE_STATUS, &checkResult);
if(GL_FALSE == checkResult)
{
cout<<"vertex shader compile error"<<endl;
printShaderInfoLog(G_vShader_simple);
}
glGetShaderiv(G_fShader_simple, GL_COMPILE_STATUS, &checkResult);
if(GL_FALSE == checkResult)
{
cout<<"fragment shader compile error"<<endl;
printShaderInfoLog(G_fShader_simple);
}
////////////////////////////////////////////////////////////
//着色器程序对象(Shader Program Object)是多个着色器合并之后并最终链接完成的版本。
//如果要使用刚才编译的着色器我们必须把它们链接(Link)为一个着色器程序对象,然后在渲染对象的时候激活这个着色器程序。
//已激活着色器程序的着色器将在我们发送渲染调用的时候被使用。
G_shaderProgram = glCreateProgram();//创建一个程序,并返回新创建程序对象的ID引用
//把之前编译的着色器附加到程序对象上
glAttachShader(G_shaderProgram, G_vShader_simple);
glAttachShader(G_shaderProgram, G_fShader_simple);
//用glLinkProgram链接它们
glLinkProgram(G_shaderProgram);
//检测链接着色器程序是否失败,并获取相应的日志。
glGetProgramiv(G_fShader_simple, GL_LINK_STATUS, &checkResult);
if(GL_FALSE == checkResult)
{
cout<<"shader link error"<<endl;
printProgramInfoLog(G_shaderProgram);
}
//我们可以调用glUseProgram函数,用刚创建的程序对象作为它的参数,以激活这个程序对象:
//glUseProgram(G_shaderProgram); //在glUseProgram函数调用之后,每个着色器调用和渲染调用都会使用这个程序对象
//在把着色器对象链接到程序对象以后,记得删除着色器对象
glDeleteShader(G_vShader_simple);
glDeleteShader(G_fShader_simple);
}
char* textFileRead(const char *textFileName)
{
FILE *fp;
if(NULL == (fp = fopen(textFileName, "r")))
{
cout<<"text file read error"<<endl;
exit(1);
}
char ch;
int fileLen = 0;
//首先得到文件长度
while(EOF != (ch=fgetc(fp)))
{
fileLen ++;
}
char *fileStr = (char *)malloc((fileLen+1)*sizeof(char));
//第二次读取文件
rewind(fp);
int i = 0;
while(EOF != (ch=fgetc(fp)))
{
fileStr[i] = ch;
i++;
}
fileStr[fileLen] = '\0'; //注意这个一定要加。
fclose(fp);
return fileStr;
}
void printShaderInfoLog(GLuint shaderObject)
{
GLint logLen = 0;
GLint writtenLen = 0;
GLchar* info_log;
glGetShaderiv(shaderObject, GL_INFO_LOG_LENGTH , &logLen);
if (logLen > 1)
{
info_log = (GLchar*)malloc(logLen);
// glGetInfoLogARB(shaderObject, logLen, &writtenLen, info_log); //也许这是老版本的函数了。
glGetShaderInfoLog(shaderObject, logLen, &writtenLen, info_log);
// printf("Information log: \n");
cout<<info_log<<endl;
free (info_log);
}
}
void printProgramInfoLog(GLuint programObject)
{
GLint logLen = 0;
GLint writtenLen = 0;
GLchar* info_log;
glGetShaderiv(programObject, GL_INFO_LOG_LENGTH , &logLen);
if (logLen > 1)
{
info_log = (GLchar*)malloc(logLen);
// glGetInfoLogARB(shaderObject, logLen, &writtenLen, info_log);
glGetProgramInfoLog(programObject, logLen, &writtenLen, info_log);
// printf("Information log: \n");
cout<<info_log<<endl;
free (info_log);
}
}