08.处理着色器
显而易见,在07中我们编写着色器代码的方式很麻烦,所以改为从文件中读取着色器字符串。
新建一个文件Basic.shader
来编辑我们的着色器代码。
GLSL
//目录结构 /OpenGL/res/shaders/Basic.shader
//将顶点着色器和片元着色器存储在同一文件中,读取时以#shader vertex/fragment区分
#shader vertex
#version 330 core
layout(location = 0) in vec4 position;
void main()
{
gl_Position = position;
};
#shader fragment
#version 330 core
layout(location = 0) out vec4 color;
void main()
{
color = vec4(0.2, 0.3, 0.8, 1.0);
};
在Application.cpp
中,需要引入可以读取文件流的库并进行对应操作。
C++
//目录结构 /OpenGL/src/Application.cpp
#include <fstream>
#include <string>
#include <sstream>
/** 需要返回多类型 */
struct ShaderProgramSource
{
std::string VertexSource;
std::string FragmentSource;
};
/** 从文件中读取着色器 */
static ShaderProgramSource ParseShader(const std::string& filepath)
{
std::ifstream stream(filepath);
enum class ShaderType
{
NONE = -1, VERTEX = 0, FRAGMENT = 1
};
std::stringstream ss[2];
std::string line;
ShaderType type = ShaderType::NONE;
while (getline(stream, line))
{
// std::string::npos是C++标准库中string类的静态成员变量,它表示一个无效的或者不存在的字符串位置或索引。
// 这个值在string类中通常用于查找或搜索某个子字符串或字符的位置,当find()或rfind()等函数无法找到所需的子字符串或字符时,它们会返回std::string::npos作为标记表示查找失败。
if (line.find("#shader") != std::string::npos)
{
if (line.find("vertex") != std::string::npos)
type = ShaderType::VERTEX;
else if(line.find("fragment") != std::string::npos)
type = ShaderType::FRAGMENT;
}
else
{
ss[(int)type] << line << '\n';
}
}
return { ss[0].str(), ss[1].str() };
}
ShaderProgramSource source = ParseShader("res/shaders/Basic.shader");
std::cout << "Vertex Shader" << std::endl;
std::cout << source.VertexSource << std::endl;
std::cout << "Fragment Shader" << std::endl;
std::cout << source.FragmentSource << std::endl;
unsigned int shader = CreateShader(source.VertexSource, source.FragmentSource);
glUseProgram(shader);
...
glDeleteProgram(shader);
这样可以优化代码的规整性。