对于 OpenGL 着色器,您将如何用 C++ 编写一个接受所有类型的统一函数?

Posted

技术标签:

【中文标题】对于 OpenGL 着色器,您将如何用 C++ 编写一个接受所有类型的统一函数?【英文标题】:For OpenGL Shaders, how would you write a Uniform Function in C++ that accepts all types? 【发布时间】:2019-11-14 19:08:41 【问题描述】:

假设我有一个 Shader 类,我想要一个 Uniform 函数将我传递的数据发送到加载的 Shader 程序

class Shader
   unsigned int programid;
   template<typename Type>
   void Uniform(unsigned int location, Type object)
       //Some logic that passes the type data to the uniform using glUniform[?]()
   

如何编写 Uniform 函数(使用 C++ 中的模板)来接受任何类型(原始 OR 对象)并将其传递给着色器?

例子:

GLSL:uniform float Amount; C++:shader.Uniform(somefloat);

GLSL:uniform vec3 Position;

C++:

template<typename Type, size_t Size>
Vector Type data[Size]; 

Vector<float, 3> position = 0.0f, 1.0f, 1.0f
shader.Uniform(position);

GLSL:

struct Light

  vec3 position;
  vec4 rotation;
  float luminosity;
  bool status;
;

uniform Light object;

C++:

struct Light 
  Vector<float, 3> position;
  Vector<float, 4> rotation;
  float luminosity;
  bool status;

Light object = 1.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f,true;
shader.Uniform(object);

【问题讨论】:

如果你接受anything,你怎么知道着色器能够处理它?它需要知道如何处理你扔给它的任何东西,它怎么可能做到这一点?魔法?当它无法处理你扔给它的东西时,你会期望它如何表现? 好吧,我会事先知道着色器想要什么(因为我对其进行了编码),Uniform 函数只会有一些逻辑将类型路由到正确的 glUniform 函数(或结构/数组的多个函数调用) .如果我传递了着色器不接受的东西,它应该在内部出错并且不使用传递的对象。 阅读explicit (full) template specialization。请注意,您必须为每种类型编写单独的函数、方法或特化,因为 OpenGL API 为每种类型提供了不同的命名接口函数 (glUniform*) 【参考方案1】:

首先,C++ 和 GLSL 是静态类型语言,而不是像 javascript 或 Python 那样动态类型。所以没有实际的方法来编写一个接受任何类型的 C++ 函数。您的 C++ 模板函数的作用本质上是文本替换。每次 C++ 编译器看到使用的模板时,例如“Vector”,它都会获取原始模板声明并制作一个新副本,其中“Type”和“Size”分别替换为“float”和“3”。并且编译器会生成一个唯一的重整名称以防止链接器错误,例如 __Vector_TypeFOO_SizeBAR...

(为了完整起见,是的,可以使用联合和/或指针强制转换在 C/C++ 中实现您自己的动态类型。但由于您不能在 GLSL 中执行其中任何一项,因此无助于回答问题。)

因此,由于 GLSL 没有模板来进行文本替换,因此您必须自己实现它。从文件或其他任何内容加载着色器的源。在将其传递给 glCompileShader 之前,请使用您最喜欢的字符串处理库将实际字符串插入占位符文本中。

例如,在您的着色器中,您可以编写如下内容:

<TYPE><SIZE> position;

你的主程序会做类似的事情

src = loadShaderCode("example.template");
src.replace("<TYPE>", "vec");
src.replace("<SIZE>", "3");
shader = compileShader(src, ...);

希望这会有所帮助。

【讨论】:

GLSL 模板系统的好主意,但我一直在寻找 C++ 中的函数系统,它可以接受任何原语或对象或向量并将其发送到着色器。使用模板和类型比较的东西。 (我认为我的问题标题有点混乱) 那么您想要接受一系列 C++ 类型并将它们转换为固定的 GLSL 统一类型的 C++ 函数吗?几个浮点数的简单情况,比如一个标量或 1 到 4 个值的向量,编写 5 个重载变体可能比弄清楚如何模板化更快。除此之外,复杂性会迅速升级,并且在很大程度上取决于谁使用代码以及用于什么目的。 “任何原语或对象或向量”可以是字符串吗? B树? OBJ模型?不知道更多是不可能回答的 是的,昨天才意识到。我只是用复制和粘贴来制作所有原语和矢量函数。如果没有反射,对任何结构对象使用模板都是不可能的。

以上是关于对于 OpenGL 着色器,您将如何用 C++ 编写一个接受所有类型的统一函数?的主要内容,如果未能解决你的问题,请参考以下文章

您将如何用外行术语解释这个查询?

如何使用 OpenGL ARB_gl_spirv 扩展?

我的OpenGL学习进阶之旅 C++ 长行字符串多行书写的方法以及如何书写正确的OpenGL Shader着色器代码

带和不带着色器的 VBO OpenGL C++

无法获得简单的 2D 着色器以在 C++ openGL 中绘制红色三角形(无编译器错误)

我的OpenGL学习进阶之旅如何抽取着色器代码到assets目录下的GLSL文件,以及如何通过Java或者C++代码来加载着GLSL文件?