glfw+shader的程序框架

最近学图形学学到了shader,刚开始接触简直要吐了,但是看过几个例子程序后逐渐习惯了,也体会到用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);
	}
}

你可能感兴趣的:(计算机图形学学习总结)