【OSG案例详细分析与讲解】之二:【着色文件转换为字符数组】

2024-01-10 07:50:53

文章目录

一、【着色文件转换为字符数组】前言

二、【着色文件转换为字符数组】Shader转换

三、【着色文件转换为字符数组】转换函数

1.转换函数

2.字符替换函数

四、【着色文件转换为字符数组】示例

1.GLSL2Cpp.cpp文件:

2.Qt pro文件:

五、【着色文件转换为字符数组】运行效果

六、【着色文件转换为字符数组】总结


一、【着色文件转换为字符数组】前言

? ? ? ?对于着色语言,我们常常采用单独的文件进行管理和存储,然后通过程序读取对应的着色语言文件,再加载到场景中,实现三维渲染。

? ? ? ?但是,单独存放的着色语言文件存在以下问题:

  • 程序部署时,要把着色语言文件同步拷贝的指定的目录下。
  • 着色语言的具体内容暴露在外,不利于代码保密。
  • 若用户自行修改了着色语言文件的内容,可能会导致程序无法运行。

? ? ? ?若把着色语言书写为.cpp文件,加入到程序的编译文件之中,那么可快速解决上述问题。

? ? ??在OpenSceneGraph(OSG)的核心库osgDB中,提供了osgDB::readRefShaderFile方法,可读取GLSL文件,构建osg::Shader的智能指针。


二、【着色文件转换为字符数组】Shader转换

GLSL转为字符数组,形成.cpp的步骤流程如下:

  1. 打开一个ofstream对象fout,以写入模式打开名为cppFileName的文件。

  2. 检查文件是否成功打开,如果没有打开成功,则输出错误信息并退出函数。

  3. 获取shader对象的着色器源代码,并保存在字符串变量shaderSource中。

  4. 调用searchAndReplace函数,将shaderSource中的"\r\n"、"\r"和"""分别替换为"\n"和"\""。

  5. 构造变量字符串variableString,例如char variableName[] =

  6. 初始化变量startOfLine为0,用于记录每行的起始位置。

  7. 查找shaderSource中第一个换行符的位置,保存在endOfLine中。

  8. 如果找不到换行符,则表示shaderSource只有一行代码,直接将variableStringshaderSource和"\\n\";"写入文件中。

  9. 否则,进入循环,循环结束条件为查找不到换行符。

  10. 将当前行的内容写入文件,注意在行末加上"\\n\"

  11. 更新startOfLine的值,使其指向下一行的起始位置。

  12. 继续查找下一个换行符的位置,保存在endOfLine中。

  13. 循环结束后,最后一行的内容仍未写入文件中,将其写入,并在行末加上"\\n\";"。

  14. 输出写入完成的提示信息。


三、【着色文件转换为字符数组】转换函数

1.转换函数

// 将shader的源代码写入到cppFileName指定的文件中,并将变量名设置为variableName
void writeShader(osg::Shader* shader, const std::string& cppFileName, const std::string& variableName)
{
    osgDB::ofstream fout(cppFileName.c_str()); // 打开要写入的文件
    if (!fout)
    {
        std::cout<<"Error: could not open file `"<<cppFileName<<"` for writing."<<std::endl; // 如果打开失败,则输出错误信息并退出函数
        return;
    }

    std::string shaderSource = shader->getShaderSource(); // 获取shader的源代码
    searchAndReplace(shaderSource, "\r\n", "\n"); // 将Windows风格的换行符替换为Unix风格的
    searchAndReplace(shaderSource, "\r", "\n"); // 将Macintosh风格的换行符替换为Unix风格的
    searchAndReplace(shaderSource, "\"", "\\\""); // 将所有双引号转义

    std::string variableString = std::string("char ") + variableName + std::string("[] = "); // 构造变量字符串

    std::string::size_type startOfLine = 0; // 记录每行的起始位置
    std::string::size_type endOfLine = shaderSource.find_first_of('\n', startOfLine); // 查找第一个换行符的位置

    if (endOfLine == std::string::npos) // 如果找不到换行符,则表示该shader只有一行
    {
        fout << variableString << shaderSource << "\\n\";" << std::endl; // 直接将该行代码写入文件中
    }
    else
    {
        // 逐行将shader的源代码写入文件中
        std::string padding(variableString.size(), ' '); // 构造填充字符串,用于保持格式一致

        fout << variableString << "\"" << shaderSource.substr(startOfLine, endOfLine - startOfLine) << "\\n\"" << std::endl;
        startOfLine = endOfLine + 1;
        endOfLine = shaderSource.find_first_of('\n', startOfLine);

        while (endOfLine != std::string::npos)
        {
            fout << padding << "\"" << shaderSource.substr(startOfLine, endOfLine - startOfLine) << "\\n\"" << std::endl;
            startOfLine = endOfLine + 1;
            endOfLine = shaderSource.find_first_of('\n', startOfLine);
        }

        // 最后一行的内容需要单独处理
        fout << padding << "\"" << shaderSource.substr(startOfLine, endOfLine - startOfLine) << "\\n\";" << std::endl;
    }

    std::cout << "Written shader to `" << cppFileName << "`" << std::endl; // 输出写入完成的提示信息
}

2.字符替换函数

// 在字符串str中搜索所有与spat匹配的子串,并用rpat替换它们。
void searchAndReplace(std::string& str, const std::string& spat, const std::string& rpat)
{
    std::string::size_type pos = 0;
    while ((pos = str.find(spat, pos)) != std::string::npos) // 查找所有spat出现的位置
    {
        str.replace(pos, spat.length(), rpat); // 用rpat替换spat
        pos += rpat.length(); // 更新pos的位置
    }
}

四、【着色文件转换为字符数组】示例

1.GLSL2Cpp.cpp文件:

#include <osg/ArgumentParser>
#include <osg/ApplicationUsage>

#include <osgDB/ReadFile>
#include <osgDB/FileNameUtils>
#include <osgDB/fstream>

#include <iostream>

// 在字符串str中搜索所有出现的spat并替换为rpat
void searchAndReplace(std::string& str, const std::string& spat, const std::string& rpat)
{
    std::string::size_type pos = 0;
    while ((pos = str.find(spat, pos)) != std::string::npos)
    {
        str.replace(pos, spat.length(), rpat);
        pos += rpat.length();
    }
}

// 将shader写入cppFileName所指定的文件中,变量名为variableName
void writeShader(osg::Shader* shader, const std::string& cppFileName, const std::string& variableName)
{
    osgDB::ofstream fout(cppFileName.c_str());
    if (!fout)
    {
        std::cout<<"Error: could not open file `"<<cppFileName<<"` for writing."<<std::endl;
    }

    std::string shaderSource = shader->getShaderSource();
    searchAndReplace(shaderSource, "\r\n", "\n");
    searchAndReplace(shaderSource, "\r", "\n");
    searchAndReplace(shaderSource, "\"", "\\\"");

    std::string variableString = std::string("char ")+variableName+std::string("[] = ");

    std::string::size_type startOfLine = 0;
    std::string::size_type endOfLine = shaderSource.find_first_of('\n', startOfLine);

    if (endOfLine==std::string::npos)
    {
        fout<<variableString<<shaderSource<<"\\n\";"<<std::endl;
    }
    else
    {
        std::string padding(variableString.size(),' ');

        fout<<variableString<<"\""<<shaderSource.substr(startOfLine,endOfLine-startOfLine)<<"\\n\""<<std::endl;
        startOfLine = endOfLine+1;
        endOfLine = shaderSource.find_first_of('\n', startOfLine);

        while (endOfLine != std::string::npos)
        {
            fout<<padding<<"\""<<shaderSource.substr(startOfLine,endOfLine-startOfLine)<<"\\n\""<<std::endl;
            startOfLine = endOfLine + 1;
            endOfLine = shaderSource.find_first_of('\n', startOfLine);
        }
        fout<<padding<<"\""<<shaderSource.substr(startOfLine,endOfLine-startOfLine)<<"\\n\";"<<std::endl;
    }
    std::cout<<"Written shader to `"<<cppFileName<<"`"<<std::endl;
}

int main( int argc, char **argv )
{
    osg::ArgumentParser arguments(&argc,argv); // 创建ArgumentParser对象以处理程序参数

    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName()); // 设置应用程序名称为参数中的应用程序名称
    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is a utility for converting glsl shader files into char arrays that can be compiled into applications."); // 设置应用程序描述
    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ..."); // 设置命令行使用说明
    arguments.getApplicationUsage()->addCommandLineOption("--shader <filename>","Shader file to create a .cpp file for."); // 添加命令行选项--shader <filename>
    arguments.getApplicationUsage()->addCommandLineOption("--write-to-source-file-directory","Use the path to the source filename as the directory to write to."); // 添加命令行选项--write-to-source-file-directory
    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display command line parameters"); // 添加命令行选项-h或--help

    if (arguments.read("-h") || arguments.read("--help")) // 如果用户输入了-h或--help参数,则输出命令行参数帮助文档并返回1
    {
        arguments.getApplicationUsage()->write(std::cout);
        return 1;
    }

    bool useSamePathAsSourceFile = false;
    if (arguments.read("--write-to-source-file-directory")) useSamePathAsSourceFile = true; // 如果用户使用了--write-to-source-file-directory选项,则将useSamePathAsSourceFile设置为true

    std::string filename;
    if (arguments.read("--shader",filename)) // 如果用户使用了--shader选项,则读取shader文件
    {
        osg::ref_ptr<osg::Shader> shader = osgDB::readRefShaderFile(filename); // 读取shader文件
        if (shader.valid())
        {
            std::string name = osgDB::getStrippedName(filename); // 获取文件名
            std::string path = osgDB::getFilePath(filename); // 获取文件所在路径
            std::string invalidCharacters = "-+/\\*=(){}[]:;<>,.?@'~#`!\""; // 无效的字符
            std::string numbericCharacters = "0123456789"; // 数字字符
            std::string::size_type pos = name.find_first_of(invalidCharacters);
            while (pos != std::string::npos) // 将文件名中的无效字符替换为下划线
            {
                name[pos] = '_';
                pos = name.find_first_of(invalidCharacters);
            }

            std::string ext = osgDB::getFileExtension(filename); // 获取文件扩展名
            std::string cppFileName = name + "_" + ext + ".cpp"; // 生成输出的cpp文件名
            if (useSamePathAsSourceFile) cppFileName = osgDB::concatPaths(path, cppFileName);

            std::string variableName = name + "_" + ext; // 变量名
            writeShader(shader.get(), cppFileName, variableName); // 将shader写入cpp文件

            return 0;
        }
        else
        {
            std::cout<<"Error: could not find file '"<<filename<<"'"<<std::endl;
            return 1;
        }

    }

    std::cout<<"No appropriate command line options used."<<std::endl;

    arguments.getApplicationUsage()->write(std::cout);
    return 1;
}

2.Qt pro文件:

QT += core

TEMPLATE = app
CONFIG += console

DESTDIR = ../3rdParty
if(contains(DEFINES,MSVC2015)){
    DESTDIR = ../3rdParty-2015
    CONFIG(debug, debug|release){
        TARGET = eg_GLSL2Cppd
        MOC_DIR = ../build-OpenSceneGraph-2015/eg_GLSL2Cpp/Debug/moc
        RCC_DIR = ../build-OpenSceneGraph-2015/eg_GLSL2Cpp/Debug/rcc
        UI_DIR = ../build-OpenSceneGraph-2015/eg_GLSL2Cpp/Debug/ui
        OBJECTS_DIR = ../build-OpenSceneGraph-2015/eg_GLSL2Cpp/Debug/obj
    }else{
        TARGET = eg_GLSL2Cpp
        MOC_DIR = ../build-OpenSceneGraph-2015/eg_GLSL2Cpp/Release/moc
        RCC_DIR = ../build-OpenSceneGraph-2015/eg_GLSL2Cpp/Release/rcc
        UI_DIR = ../build-OpenSceneGraph-2015/eg_GLSL2Cpp/Release/ui
        OBJECTS_DIR = ../build-OpenSceneGraph-2015/eg_GLSL2Cpp/Release/obj
    }
}else{
    DESTDIR = ../3rdParty
    CONFIG(debug, debug|release){
        TARGET = eg_GLSL2Cppd
        MOC_DIR = ../build-OpenSceneGraph/eg_GLSL2Cpp/Debug/moc
        RCC_DIR = ../build-OpenSceneGraph/eg_GLSL2Cpp/Debug/rcc
        UI_DIR = ../build-OpenSceneGraph/eg_GLSL2Cpp/Debug/ui
        OBJECTS_DIR = ../build-OpenSceneGraph/eg_GLSL2Cpp/Debug/obj
    }else{
        TARGET = eg_GLSL2Cpp
        MOC_DIR = ../build-OpenSceneGraph/eg_GLSL2Cpp/Release/moc
        RCC_DIR = ../build-OpenSceneGraph/eg_GLSL2Cpp/Release/rcc
        UI_DIR = ../build-OpenSceneGraph/eg_GLSL2Cpp/Release/ui
        OBJECTS_DIR = ../build-OpenSceneGraph/eg_GLSL2Cpp/Release/obj
    }
}

DEFINES -= UNICODE _UNICODE
win32 {
    DEFINES += _CRT_SECURE_NO_DEPRECATE _CRT_NONSTDC_NO_DEPRECATE
}

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

#当前目录
INCLUDEPATH += ./ ./include
#LIBS
#LIBS
if(contains(DEFINES,MSVC2015)){
    LIBS += -L../3rdParty-2015
}else{
    LIBS += -L../3rdParty
}
CONFIG(debug, debug|release){
    LIBS += -lOpenThreadsd -losgd -losgDBd
}else{
    LIBS += -lOpenThreads -losg -losgDB
}
#win32: LIBS += -lopengl32

SOURCES +=  ./examples/osg2cpp/osg2cpp.cpp


# Default rules for deployment.
#unix {
#    target.path = /usr/lib
#}
#!isEmpty(target.path): INSTALLS += target

五、【着色文件转换为字符数组】运行效果

原始的blocky.frag文件:

形成的blocky_frag.cpp文件:


六、【着色文件转换为字符数组】总结

? ? ? ?上述代码是一个用于将GLSL着色器文件转换为字符数组实用程序。它通过读取着色器文件的内容,并将其以字符数组的形式写入一个与源文件同名但扩展名为.cpp的文件中。这个程序可以方便地将着色器源代码嵌入到C++代码中,以便在应用程序中使用。

文章来源:https://blog.csdn.net/LuckyHanMo/article/details/135490576
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。