Skip to content

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);

这样可以优化代码的规整性。