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

news2025/2/24 1:25:06

文章目录

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

知识点整理[(GraphGeo) DATA AND PROBLEM DEFINITION]

3 DATA AND PROBLEM DEFINITION 3.1 Data Collection 问题一:IP定位数据集构成 回答: 包含数以百万计的IP地址,这些IP地址包括: (1)它们具有自己的知识(如自主系统(AS)和WHOIS数据); (2)网络测量

Python-抖音无法拒绝的表白代码【附源码】

一个无法被拒绝的表白代码 运行效果&#xff1a; 一&#xff1a;主程序&#xff1a; import sys import cfg import random import pygame from tkinter import Tk, messagebox Function:按钮类 Initial Args:--x, y: 按钮左上角坐标--width, height: 按钮宽高--text: 按钮显…

Vant2组件库van-list+Toast下拉加载滚动条回顶问题

目录 List 列表 Toast 轻提示 解决方案 1、不使用 Toast 的 加载提示 2、修改调整 pointer-event 属性值 3、判断是否为第一次加载再使用 背景 &#xff1a; 移动端项目 开发时&#xff0c;有数据长列表展示的场景需求&#xff0c;此时就用到了 Vant2 组件库里面的 <v…

【信息安全】深度分析邮件安全及钓鱼攻击防范

本博文共计3100余字&#xff0c;预计需阅读20分钟 【邮件安全建设】 一、前言 邮件系统作为企业办公网络架构中重要的组成部分&#xff0c;同时也是业务高频使用的办公应用&#xff0c;一旦出现安全问题&#xff0c;业务将会被严重干扰甚至中断&#xff0c;本篇博客通过攻守两…

react中实现拓扑图

react中实现拓扑图关系图 需求echarts代码react代码 需求 项目中的原型图需要使用react实现一个拓扑图&#xff08;关系图&#xff09; 通过查找&#xff0c;找到了可以使用的类似的原型&#xff1a;以下图片地址。 通过项目需要以及修改&#xff0c;形成了下边的样式 echar…

Java--业务场景:获取请求的ip属地信息

文章目录 前言步骤在pom文件中引入下列依赖IpUtil工具类在Controller层编写接口&#xff0c;获取请求的IP属地测试接口 IpInfo类中的方法 前言 很多时候&#xff0c;项目里需要展示用户的IP属地信息&#xff0c;所以这篇文章就记录一下如何在Java Spring boot项目里获取请求的…

Java--业务场景:在Spring项目启动时加载Java枚举类到Redis中(补充)

文章目录 前言步骤测试结果 前言 通过Java–业务场景&#xff1a;在Spring项目启动时加载Java枚举类到Redis中,我们成功将Java项目里的枚举类加载到Redis中了&#xff0c;接下来我们只需要写接口获取需要的枚举值数据就可以了&#xff0c;下面一起来编写这个接口吧。 步骤 在…

[算法与数据结构][c++][python]:C++与Python中的赋值、浅拷贝与深拷贝

C与Python中的赋值、浅拷贝与深拷贝 写在前面&#xff1a;Python和C中的赋值与深浅拷贝&#xff0c;由于其各自语言特性的问题&#xff0c;在概念和实现上稍微有点差异&#xff0c;本文将这C和Python中的拷贝与赋值放到一起&#xff0c;希望通过对比学习两语言实现上的异同点&a…

超市商品管理系统设计 C++实现

超市商品管理系统设计—C实现 文章目录 超市商品管理系统设计---C实现一、内容要求大纲图 二、源代码&#xff08;包含大量注释&#xff09;1、main.cpp文件2、supermarket.h文件3、supermarket.cpp文件4、administrator.h文件5、administrator.cpp文件6、user.h文件7、user.cp…

系列十二、数组

一、数组 1.1、概述 数组是指可以同时存放固定长度的同一类型的数据。 1.2、数组的声明 数组的声明有三种方式&#xff0c;具体如下&#xff1a; 1.2.1、方式一 /*** 声明数组的第一种方式* cityArray&#xff1a;数组名&#xff0c;注意:不包括[]*/ Test public void d…

MySQL的导入导出及备份

一.准备导入之前 二.navicat导入导出 ​编辑 三.MySQLdump命令导入导出 四.load data file命令的导入导出 五.远程备份 六. 思维导图 一.准备导入之前 需要注意&#xff1a; 在导出和导入之前&#xff0c;确保你有足够的权限。在进行导入操作之前&#xff0c;确保目标数据…

C2-3.3.2 机器学习/深度学习——数据增强

C2-3.3.2 数据增强 参考链接 1、为什么要使用数据增强&#xff1f; ※总结最经典的一句话&#xff1a;希望模型学习的更稳健 当数据量不足时候&#xff1a; 人工智能三要素之一为数据&#xff0c;但获取大量数据成本高&#xff0c;但数据又是提高模型精度和泛化效果的重要因…

代码随想录-刷题第五十二天

300. 最长递增子序列 题目链接&#xff1a;300. 最长递增子序列 思路&#xff1a;动态规划五步曲&#xff1a; dp[i]表示从0到i&#xff0c;以nums[i]结尾的最长递增子序列的长度。 递推公式&#xff1a;if(nums[i]>nums[j]) dp[i] max(dp[i], dp[j] 1) 位置i的最长升序…

Spring cloud聚合父工程project

文章目录 本次微服务版本一. 新建父工程project1.1设置字符集utf-81.2注解生效激活1.3. Java8编译版本 二. 父工程 pom.xml 本次微服务版本 一. 新建父工程project 1.1设置字符集utf-8 1.2注解生效激活 1.3. Java8编译版本 二. 父工程 pom.xml <?xml version"1.0&quo…

HTTP 3xx状态码:重定向的场景与区别

HTTP 状态码是服务器响应请求时传递给客户端的重要信息。3xx 系列的状态码主要与重定向有关&#xff0c;用于指示请求的资源已被移动到不同的位置&#xff0c;需要采取不同的操作来访问。 一、301 Moved Permanently 定义&#xff1a; 服务器表明请求的资源已永久移动到一个新…

Python多线程同步

同步条件(Event) 在Python中&#xff0c;多线程同步可以通过threading模块中的Event对象来实现。Event对象允许一个或多个线程等待某个事件的发生&#xff0c;当事件发生时&#xff0c;等待的线程将被唤醒。 event.isSet()&#xff1a;返回event的状态值 event.wait()&#x…

Vue-11、Vue计算属性

Vue计算属性是Vue实例的属性&#xff0c;用来根据已有的数据进行计算得到新的数据。计算属性的值会根据它的依赖缓存起来&#xff0c;在依赖没有发生改变时直接返回缓存的值&#xff0c;提高了性能。 计算属性的定义方式为在Vue实例中使用computed关键字&#xff0c;并将计算属…

Pycharm中如何配置python环境(conda)

首先在pycharm中点击 "File" > "Settings" 再次点击如下操作&#xff1a; 点击Python Interpreter的最右侧按钮&#xff0c;点击Show All... 找到python文件 最后点击OK

YOLOv8改进 | 主干篇 | 12月最新成果UniRepLknet特征提取网络(附对比试验效果图)

一、本文介绍 本文给大家带来的改进机制是特征提取网络UniRepLknet,其也是发表于今年12月份的最新特征提取网络,该网络结构的重点在于使用Dilated Reparam Block和大核心指导原则,强调了高效的结构进行通道间通讯和空间聚合,以及使用带扩张的小核心进行重新参数化,该网络…

实现LCM在docker之间的通信

目录 1.docker容器互联 新建网络 连接容器 2.设置环境变量 3.在两个docker之间实现通信 1.docker容器互联 新建网络 $ docker network create -d bridge test-net 连接容器 运行一个容器并连接到新建的 test-net 网络: $ docker run -itd --name lcm_1 --network tes…