我尝试为着色器程序设计类。 着色器程序可能包含多个不同类型的着色器对象。 我不想为每个组合创建不同的consturctor或创建setter,并在此调用成员函数之后创建程序。我试图找到好看的解决方案或使用设计模式,但我不知道好的解决方案。 我有5种不同类型的着色器,并且我总是必须至少向两个着色程序传递着色器(始终是顶点和片段着色器)。我需要着色器才能创建着色器程序,在此之后我不再需要它们了。
选项#1:
ShaderPogram(VertexShader vs, FragmentShader fs);
ShaderPogram(VertexShader vs, GeometryShader geometry, FragmentShader fs);
选项#2:
struct Collection {
VertexShader vs; GeometryShader gs; FragmentShader fs;
};
ShaderProgram(Collection c);
选项#3:
class ShaderProgram {
public:
void SetVertexShader(VertexShader vs);
void SetGeometryShader(GeometryShader gs);
void SetFragmentShader(FramgentShader fs);
};
也许有人可以提出其他解决方案?
答案 0 :(得分:0)
你的选择#2和#3看起来很好。除了选项#3之外,因为你说顶点和片段着色器总是需要的,所以我给这个类一个构造函数,它只为那两种类型和setter提供可选类型。
但是如果你真的想要让这个类的用户更容易一些,那么一些模板工作可以使你只使用一个构造函数,并以你喜欢的任何顺序传递任何有效的着色器参数集:
#include <type_traits>
namespace ShaderProgramDetail {
template <typename Type, typename... TypeList> struct type_in_list;
template <typename Type>
struct type_in_list<Type>
: public std::false_type {};
template <typename Type, typename... Rest>
struct type_in_list<Type, Type, Rest...>
: public std::true_type {};
template <typename Type, typename First, typename... Rest>
struct type_in_list<Type, First, Rest...>
: public std::integral_constant<bool,
type_in_list<Type, Rest...>::value> {};
template <typename... Types> struct types_unique;
template <>
struct types_unique<> : public std::true_type {};
template <typename Type1, typename... Rest>
struct types_unique<Type1, Rest...>
: public std::integral_constant<bool,
!type_in_list<Type1, Rest...>::value &&
types_unique<Rest...>::value> {};
template <bool... B> struct all_of;
template <>
struct all_of<> : public std::true_type {};
template <bool... B>
struct all_of<false, B...> : public std::false_type {};
template <bool... B>
struct all_of<true, B...>
: public std::integral_constant<bool, all_of<B...>::value> {};
}
class ShaderProgram
{
public:
template <typename... Shaders>
explicit ShaderProgram(Shaders&& ... shaders) {
using namespace ShaderProgramDetail;
static_assert(types_unique<std::decay_t<Shaders>...>::value,
"Cannot provide two shaders of the same type");
static_assert(all_of<type_in_list<std::decay_t<Shaders>,
VertexShader, FragmentShader, GeometryShader>
::value...>::value,
"Invalid argument type");
static_assert(type_in_list<VertexShader, std::decay_t<Shaders>...>::value,
"Missing VertexShader argument");
static_assert(type_in_list<FragmentShader, std::decay_t<Shaders>...>::value,
"Missing FragmentShader argument");
using int_arr = int[];
(void) int_arr{ ( init_shader(std::forward<Shaders>(shaders)), 0 )... };
}
private:
void init_shader(VertexShader vs) { vertex_shader = std::move(vs); }
void init_shader(FragmentShader fs) { fragment_shader = std::move(fs); }
void init_shader(GeometryShader gs) { geometry_shader = std::move(gs); }
VertexShader vertex_shader;
FragmentShader fragment_shader;
GeometryShader geometry_shader;
};
注意我在构造函数中使用了四个static_assert
语句来验证:
VertexShader
。FragmentShader
。 namespace ShaderProgramDetail
中的类模板非常通用。如果您认为可以在其他地方重用它们,可以将它们放在自己的头文件中,并将命名空间重命名为更合适的名称。
以上是有效的C ++ 14代码。
如果您坚持使用C ++ 11,则需要将每个std::decay_t<X>
替换为typename std::decay<X>::type
,或者定义自己的decay_t
:
template <typename T>
using decay_t = typename std::decay_t<T>::type;
如果您使用的是C ++ 17,则可以使用std::conjunction
代替ShaderProgramDetail::all_of
;并且可以更简单地编写构造函数的最后两行:
init_shader(std::forward<Shaders>(shaders)), ... ;