OpenGL学习记录(三)

参考资料:
https://learnopengl.com/
https://learnopengl-cn.github.io/

这次实现一个Shader着色器类,让之前以字符串形式写的顶点、片元着色器编码能够通过.txt文件读入,并把编译、链接着色器等过程移动到着色器类中来,使主程序中的代码变得简洁一些。

首先,glShaderSource函数接收的是一个const char* 类型,这不变,为了得到这样一种类型,要读取.txt文件的缓冲内容到数据流中,再将数据流转换到String,最后通过.c_str()方法转换为const char* 。

之前在主函数中的一系列定义、创建、附加、编译顶点/片元着色器的过程,以及创建程序对象,将着色器附加到这个对象上并链接的过程都可以拿到Shader类中来实现,以优化main函数中的代码量。

除此之外,还实现了一个打印错误信息的方法,思路是使用glGetShaderiv和glGetProgramiv两个方法检验着色器是否编译、链接成功,以引用的形式传入一个int类型success,返回结果将传递给success,根据success的值判断是否出现错误,若出现错误,则分别调用glGetShaderInfoLog和glGetProgramInfoLog,传入一个字符数组接收信息,使用cout将信息打印出来即可。

建立好着色器类后,原本的用const char* 表示的着色器编码就可以移动到各自的.txt文件中了,修改也更加方便。在main函数中使用这个类时,只需new一个Shader类对象,传入顶点和片元着色器的文件名称即可,在循环渲染阶段,直接使用着色器对象的use()方法就可以了。

.h:

#pragma once
#include <string>

class Shader
{
    
    
public:
	Shader(const char* vertexPath, const char* fragmentPath);
	
	//读取文件的缓冲内容到数据流中->数据流转换到String->String转换到const char*
	//String用作中转
	std::string vertexString;
	std::string fragmentString;
	
	//着色器最终需要的是const char*
	const char* vertexSource;
	const char* fragmentSource;

	unsigned int ID; //程序ID

	void use(); //glUseProgram激活程序

private:
	void checkCompileErrors(unsigned int ID, std::string type);
};

.cpp:

#include "Shader.h"
#include <fstream>
#include <sstream>

#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>

Shader::Shader(const char* vertexPath, const char* fragmentPath)
{
    
    
	//从文件路径中获取顶点/片段着色器
	std::ifstream vertexFile;
	std::ifstream fragmentFile;
	std::stringstream vertexSStream;
	std::stringstream fragmentSStream;

	vertexFile.open(vertexPath); //打开文件
	fragmentFile.open(fragmentPath);
	vertexFile.exceptions(std::ifstream::failbit || std::ifstream::badbit); //保证ifstream对象可以抛出异常
	fragmentFile.exceptions(std::ifstream::failbit || std::ifstream::badbit);

	try
	{
    
    
		if (!vertexFile.is_open() || !fragmentFile.is_open())
		{
    
    
			throw std::exception("open file error."); //打开失败抛出异常
		}
		vertexSStream << vertexFile.rdbuf(); //读取文件的缓冲内容到数据流中
		fragmentSStream << fragmentFile.rdbuf();

		vertexString = vertexSStream.str(); //数据流转换到String
		fragmentString = fragmentSStream.str();

		vertexSource = vertexString.c_str(); //String转换到const char*
		fragmentSource = fragmentString.c_str();

		unsigned int vertex, fragment; //声明顶点/片元着色器

		//编译顶点着色器
		vertex = glCreateShader(GL_VERTEX_SHADER);
		glShaderSource(vertex, 1, &vertexSource, nullptr);
		glCompileShader(vertex);
		checkCompileErrors(vertex, "VERTEX"); //检查错误

		//编译片元着色器
		fragment = glCreateShader(GL_FRAGMENT_SHADER);
		glShaderSource(fragment, 1, &fragmentSource, nullptr);
		glCompileShader(fragment);
		checkCompileErrors(fragment, "FRAGMENT"); //检查错误

		//创建、链接着色器程序对象
		ID = glCreateProgram();
		glAttachShader(ID, vertex);
		glAttachShader(ID, fragment);
		glLinkProgram(ID);
		checkCompileErrors(ID, "PROGRAM"); //检查错误

		glDeleteShader(vertex); //着色器已经链接到程序中了,不再需要所以删除
		glDeleteShader(fragment);
	}
	catch (const std::exception& ex)
	{
    
    
		printf(ex.what());
	}
}

void Shader::use()
{
    
    
	glUseProgram(ID);
}

//打印错误
void Shader::checkCompileErrors(unsigned int ID, std::string type)
{
    
    
	int success;
	char infoLog[512];

	if (type != "PROGRAM")
	{
    
    
		glGetShaderiv(ID, GL_COMPILE_STATUS, &success);
		if (!success)
		{
    
    
			glGetShaderInfoLog(ID, 512, nullptr, infoLog);
			std::cout << "Shader compile error: " << infoLog << std::endl; //打印编译错误
		}
	}
	else
	{
    
    
		glGetProgramiv(ID, GL_LINK_STATUS, &success);
		if (!success)
		{
    
    
			glGetProgramInfoLog(ID, 512, nullptr, infoLog);
			std::cout << "Program link error: " << infoLog << std::endl; //打印链接错误
		}
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_47260762/article/details/128191728