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

文章目录

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

二、【着色文件转换为字符数组】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++代码中,以便在应用程序中使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/306484.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spark与Cassandra的集成与数据存储

Apache Spark和Apache Cassandra是大数据领域中两个重要的工具&#xff0c;用于数据处理和分布式数据存储。本文将深入探讨如何在Spark中集成Cassandra&#xff0c;并演示如何将Spark数据存储到Cassandra中。将提供丰富的示例代码&#xff0c;以帮助大家更好地理解这一集成过程…

vue2源码解析之第一步(对数据进行劫持)

###环境搭建 第一步 创建项目&#xff1a; npm init -y 第二步 安装对应的插件&#xff1a; npm i rollup rollup-plugin-babel babel/core babel/preset-env --save-dev 第三步 全局下创建rollup配置文件 rollup.config.js import babel from rollup-plug…

mysql的导入导出

mysql的导入导出 1.使用navicat导入导出1.1导入1.2导出 2.使用.mysqldump命令导入导出2.1导出表结构和数据2.2导出表结构2.3导入 3..LOAD DATA INFILE命令导入导出3.1在mysqlini 文件的[mysqld] 代码下增加 secure_file_privE:/TEST 再重启 mysql3.2导出3.3导入 4.远程备份导入…

Ubuntu20二进制方式安装nginx

文章目录 1.下载nginx安装包2.安装nginx3.安装出现的问题及解决方案错误1&#xff1a;错误2&#xff1a;错误3&#xff1a; 4.常用命令5.知识扩展&#xff1a; 1.下载nginx安装包 nginx官网&#xff1a;http://nginx.org/en/download.html 选择稳定的nginx版本下载。 2.安装ngi…

[redis] redis主从复制,哨兵模式和集群

一、redis的高可用 1.1 redis高可用的概念 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999%等等)。 高可用的计算公式是1-&#xff08;宕机时间&#xff09;/&#xff08;宕机时…

WPS或word中英文字母自动调整大小写,取消自动首字母大写,全部英文单词首字母大小写变换方法

提示&#xff1a;写英文论文时&#xff0c;如何实现英文字母大小写的自动切换&#xff0c;不用再傻傻的一个字母一个字母的编辑了&#xff0c;一篇文章搞定WPS与Word中字母大小写切换 文章目录 一、WPS英文单词大小写自动修改与首字母大写调整英文字母全部由大写变成小写 或 小…

Python进阶之元类

Python进阶之元类 目录 什么是元类&#xff1f; 元类的调用流程 根据类自定义元类 __new__方法以及参数 ----------cls ----------name ----------bases ----------attrs __call__方法 生成对象的完整代码 什么是元类&#xff1f; 在python面向对象中&#xff0c;我们知道所有…

【AI】Pytorch 系列:预训练模型使用

1. 模型下载 import re import os import glob import torch from torch.hub import download_url_to_file from torch.hub import urlparse import torchvision.models as modelsdef download_

jenkins构建git项目timeout

问题点&#xff1a; Started by user unknown or anonymous Running as SYSTEM Building in workspace /var/jenkins_home/workspace/test-one using credential f28d956-8ee1-4f20-a32b-06879b487c70 Cloning the remote Git repository Cloning repository http://git.cc.co…

TextDiffuser-2:超越DALLE-3的文本图像融合技术

概述 近年来&#xff0c;扩散模型在图像生成领域取得了显著进展&#xff0c;但在文本图像融合方面依然存在挑战。TextDiffuser-2的出现&#xff0c;标志着在这一领域的一个重要突破&#xff0c;它成功地结合了大型语言模型的能力&#xff0c;以实现更高效、多样化且美观的文本…

HarmonyOS中的@ohos.promptAction 模块中弹框

在各种APP中会根据不同的业务场景显示不同的弹框情况&#xff0c;针对这些场景API中提示了那些弹框呢&#xff1f;今天就看下&#xff1a; 首先弹框分为模态弹框和非模态弹框&#xff0c;模态弹框必须用户点击反馈后进行下一步操作&#xff0c;非模态弹框只是告知用户信息&…

arduino安装DHT11库

步骤操作如下&#xff1a; 打开Arduino IDE。 在菜单栏中选择“项目” -> “加载库” -> “管理库…”。 在库管理器的搜索框中&#xff0c;输入“DHT sensor library”。 找到“DHT sensor library by Adafruit”&#xff0c;点击“安装”。 安装完成后&#xff0c;…

FineBI实战项目一(2):案例架构说明及数据准备

1 系统架构 基于MySQL搭建数据仓库基于Kettle进行数据处理帆软FineBI基于MySQL搭建的数据仓库进行数据分析 2 数据流程图 通过Kettle将MySQL业务系统数据库中&#xff0c;将数据抽取出来&#xff0c;然后装载到MySQL数据仓库中。编写SQL脚本&#xff0c;对MySQL数据仓库中的数…

<windows>Edge浏览器侧边栏无法关闭问题的解决方法

1 问题现象 如图&#xff0c;Edge浏览器默认安装后&#xff0c;打开了侧边栏 但是&#xff0c;关闭按钮却是灰色无法点击。 2 解决方法 在该设置页面&#xff0c;右键选择检查 在右侧的代码区域&#xff0c;选中一行便会在左侧有蓝色的标记&#xff0c;能够看到对应的页面位…

使用 CSS : 伪元素:after、过渡动画transition实现过渡效果(鼠标悬浮或点击 标签时,底部边框从左到右过渡)

首先&#xff0c;给 <span> 标签添加一个父元素&#xff0c;定义属性类名&#xff1a;nav-wrapper &#xff0c;父级设置相对定位。然后&#xff0c;使用 ::after 伪元素来创建一个与底部边框相同宽度的元素&#xff0c;初始时宽度为 0&#xff0c;通过过渡动画transitio…

IBM DS5020硬盘状态Impending failure(reported by controller)

这个状态说明硬盘还没有完全坏掉&#xff0c;但是也需要注意更换新硬盘了 磁盘状态详细信息&#xff1a; 报错信息 按照恢复步骤&#xff1a; 选中该硬盘&#xff0c;手动failed&#xff0c;之后可以将该硬盘拔掉&#xff0c;重新插入新硬盘 此时&#xff0c;会有一块热备盘启…

Angular - 笔记

文章目录 语法属性绑定引用模板变量组件绑定父组件传子组件 input子组件传父组件 outputEventEmitter ViewChildViewChildren获取子组件对象列表 管道常用模块 参考文档 语法 属性绑定 Angular 的双向绑定语法是方括号和圆括号的组合 [()]。[] 进行属性绑定&#xff0c;() 进行…

x-cmd pkg | grex - 用于生成正则表达的命令行工具

目录 简介首次用户生成的正则表达式与 perl 和 rust 兼容支持 Unicode 符号友好的用户体验进一步阅读 简介 grex 是一个旨在简化创作正则表达式的复杂且繁琐任务的库和命令行程序。这个项目最初是 Devon Govett 编写的 JavaScript 工具 regexgen 的 Rust 移植。但 regexgen 在…

RT-Thread基于AT32单片机的CAN应用

1 硬件电路 2 RT-Thread驱动配置 RT-Studio中没有CAN相关的图形配置&#xff0c;需要手动修改board.h。在board.h的末尾&#xff0c;增加相关的BSP配置。 #define RT_CAN_USING_HDR #define BSP_USING_CAN13 IO配置 at32_msp.c中的IO配置是PB9和PB10&#xff0c;掌上实验室V…

松鼠目标检测数据集VOC格式400张

松鼠&#xff0c;一种小巧玲珑、活泼可爱的啮齿类动物&#xff0c;以其蓬松的大尾巴和机敏的动作而广受欢迎。 松鼠通常体型小巧&#xff0c;四肢灵活&#xff0c;尾巴蓬松。它们的耳朵大而直立&#xff0c;眼睛明亮&#xff0c;给人留下了深刻的印象。松鼠的毛色因种类而异&a…