先说明一下Blender在使用OpenGL 的uniform时,需要通过在程序中生成相关的布局限定语句字符串,然后传递给对应的着色器程序,而存储对应uniform的名称和绑定点的信息的对象是一个ShaderCreateInfo结构体。
1.创建Uniform变量:
通过填充这个结构体来实现创建Uniform:
GPU_SHADER_CREATE_INFO(draw_view)
.uniform_buf(DRW_VIEW_UBO_SLOT, "ViewMatrices", "drw_view_[DRW_VIEW_LEN]", Frequency::PASS)
.define("drw_view", "drw_view_[drw_view_id]")
.typedef_source("draw_shader_shared.h");
这段代码是使用GPU_SHADER_CREATE_INFO宏来创建着色器信息,并在其中指定变量的创建和数据来源。让我们逐步解释这个过程:
-
GPU_SHADER_CREATE_INFO(draw_view)
:这是一个宏,用于创建一个ShaderCreateInfo结构体的实例,命名为draw_view。这个结构体用于存储着色器的相关信息。 -
.uniform_buf(DRW_VIEW_UBO_SLOT, "ViewMatrices", "drw_view_[DRW_VIEW_LEN]", Frequency::PASS)
:这是在ShaderCreateInfo结构体中使用uniform_buf方法来设置uniform缓冲区的信息,具体含义和用法在之前的回答中已经解释过了。 -
.define("drw_view", "drw_view_[drw_view_id]")
:这是在ShaderCreateInfo结构体中使用define方法来定义一个宏。这个宏的作用是将"drw_view"替换为"drw_view_[drw_view_id]"。这样可以在着色器代码中使用宏来实现更灵活的变量命名。 -
.typedef_source("draw_shader_shared.h")
:这是在ShaderCreateInfo结构体中使用typedef_source方法来指定一个头文件。这个头文件中可能包含了一些类型定义或者其他的预处理指令,可以在着色器代码中使用。
GPU_SHADER_CREATE_INFO宏的定义如下:
#define GPU_SHADER_CREATE_INFO(_info) \
ShaderCreateInfo *ptr_##_info = new ShaderCreateInfo(#_info); \
ShaderCreateInfo &_info = *ptr_##_info; \
g_create_infos->add_new(#_info, ptr_##_info); \
_info
这段代码是一个宏定义,用于创建ShaderCreateInfo结构体的实例,并将其添加到一个全局的创建信息列表中。让我们逐步解释这个过程:
-
ShaderCreateInfo *ptr_##_info = new ShaderCreateInfo(#_info);
:这行代码创建了一个指向ShaderCreateInfo结构体的指针,并使用宏参数_info的字符串形式作为构造函数的参数,从而创建了一个ShaderCreateInfo实例。 -
ShaderCreateInfo &_info = *ptr_##_info;
:这行代码创建了一个ShaderCreateInfo的引用,将其指向刚刚创建的ShaderCreateInfo实例。这样可以通过_info来访问和操作ShaderCreateInfo的成员。 -
g_create_infos->add_new(#_info, ptr_##_info);
:这行代码将刚刚创建的ShaderCreateInfo实例添加到一个全局的创建信息列表中。这个列表可能是一个全局变量或者一个单例对象,用于存储所有的ShaderCreateInfo实例。 -
_info
:最后一行代码返回刚刚创建的ShaderCreateInfo实例,以便在代码中使用。
通过这个宏定义,你可以方便地创建和管理多个ShaderCreateInfo实例,并将它们添加到一个全局的创建信息列表中。这样可以更方便地组织和管理着色器的创建过程,并在需要的时候使用创建好的实例。具体的使用方法可能会根据你的代码结构和需求有所不同,但基本原理是相似的。
2.编码Uniform的绑定信息
在使用解析具体信息,生成对应的着色器代码时,使用下面的方法(以顶点着色器为例)
std::string GLShader::vertex_interface_declare(const ShaderCreateInfo &info) const
{
std::stringstream ss;
std::string post_main;
ss << "\n/* Inputs. */\n";
for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) {
if (GLContext::explicit_location_support &&
/* Fix issue with AMDGPU-PRO + workbench_prepass_mesh_vert.glsl being quantized. */
GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL) == false)
{
ss << "layout(location = " << attr.index << ") ";
}
ss << "in " << to_string(attr.type) << " " << attr.name << ";\n";
}
/* NOTE(D4490): Fix a bug where shader without any vertex attributes do not behave correctly. */
if (GPU_type_matches_ex(GPU_DEVICE_APPLE, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL) &&
info.vertex_inputs_.is_empty())
{
ss << "in float gpu_dummy_workaround;\n";
}
ss << "\n/* Interfaces. */\n";
for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) {
print_interface(ss, "out", *iface);
}
if (!GLContext::layered_rendering_support && bool(info.builtins_ & BuiltinBits::LAYER)) {
ss << "out int gpu_Layer;\n";
}
if (!GLContext::layered_rendering_support && bool(info.builtins_ & BuiltinBits::VIEWPORT_INDEX))
{
ss << "out int gpu_ViewportIndex;\n";
}
if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) {
if (!GLContext::native_barycentric_support) {
/* Disabled or unsupported. */
}
else if (epoxy_has_gl_extension("GL_AMD_shader_explicit_vertex_parameter")) {
/* Need this for stable barycentric. */
ss << "flat out vec4 gpu_pos_flat;\n";
ss << "out vec4 gpu_pos;\n";
post_main += " gpu_pos = gpu_pos_flat = gl_Position;\n";
}
}
ss << "\n";
if (post_main.empty() == false) {
std::string pre_main;
ss << main_function_wrapper(pre_main, post_main);
}
return ss.str();
}