# 前言
文本嵌入在编写着色器时特别有用。它能够使我们将着色器代码从项目代码中剥离出来,从而能够利用相应的lsp进行提示、高亮以及格式化,并且进一步降低代码耦合度。
对于Rust而言,嵌入一段字符串十分简单:
include!("path_to_file/file")
对于C++,一般以下有几种方法:
# 字符串
const char* vertex_shader_code =
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}";
这是最传统的方式,运行时初始化字符串。缺点很明显,性能较低,且修改困难。
# embed
embed是C的提案,但在C++中也得到支持:
const char vertex_shader_code[] = {
#embed "path_to_file/file"
};
缺点在于目前只有clang得到支持,并且在编译时会显示警告:#embed is an extension of clang
。在打印时,通过这种方式嵌入的字符会在末尾打印出未知字符(乱码),可以猜到以上数组并不包括字符串末尾的\0
。但我目前没找到好的填\0
方法。
另外,C++的std::embed
还在日程中……
# xxd
在Linux下,有xxd
命令,它提供了将一个文件转换为C语言头文件的功能,使用方法如下:
xxd -n variable_name -i input.glsl > output.h
对于上面的例子,有:
xxd -n _vertex_shader_code -i ./assets/shader/vertex.glsl > ./include/shader/vertex.h
它在对应位置生成如下的文件:
unsigned char _vertex_shader_code[] = {
0x23, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x33, 0x33, 0x30,
0x20, 0x63, 0x6f, 0x72, 0x65, 0x0a, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74,
0x28, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20,
0x30, 0x29, 0x20, 0x69, 0x6e, 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x61,
0x50, 0x6f, 0x73, 0x3b, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61,
0x69, 0x6e, 0x28, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x67,
0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d,
0x20, 0x76, 0x65, 0x63, 0x34, 0x28, 0x61, 0x50, 0x6f, 0x73, 0x2e, 0x78,
0x2c, 0x20, 0x61, 0x50, 0x6f, 0x73, 0x2e, 0x79, 0x2c, 0x20, 0x61, 0x50,
0x6f, 0x73, 0x2e, 0x7a, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a,
0x7d, 0x0a
};
unsigned int _vertex_shader_code_len = 122;
可以注意到上面的数组并不是以\0
结尾,如果需要转换到C字符串,则需要手动填\0
。或是使用sed
进行替换:
xxd -n _vertex_shader_code -i ./assets/shader/vertex.glsl | sed "s/}/,\'\\\0\'}/" > ./include/shader/vertex.h
如果对代码格式化有强迫症,使用clang-format
进行后处理格式化:
clang-format -i ./include/hierro/shader/*
然后在代码中include对应头文件:
#include "hierro/shader/vertex.h"
调用变量:
auto vertex_shader_code = (const char*)_vertex_shader_code;