如何在Rust中创建一个类似于C++的模板(用于设置着色器uniform变量)?

huangapple go评论83阅读模式
英文:

How would I do a template (like in C++) for setting shader uniforms in Rust?

问题

shader.h:

  1. template <typename T> void SetUniform(const GLchar* name, const T& data);

shader.cpp:

  1. template <> void ShaderProgram::SetUniform<int>(const GLchar* name, const int& data) {
  2. glUniform1i(GetUniformLocation(name), data);
  3. }
  4. template <> void ShaderProgram::SetUniform<float>(const GLchar* name, const float& data) {
  5. glUniform1f(GetUniformLocation(name), data);
  6. }
  7. template <> void ShaderProgram::SetUniform(const GLchar* name, const glm::vec2& data) {
  8. glUniform2f(GetUniformLocation(name), data.x, data.y);
  9. }
  10. // etc...

在我有限的Rust知识下,我尝试使用匹配语句,因为迄今为止它已经为我节省了很多麻烦,但没有成功。

  1. pub fn set_uniform<T>(&mut self, name: &str, value: T) {
  2. match value {
  3. value if value.is::<u32>() => {/* 在这里插入GL调用 */},
  4. value if value.is::<f32>() => {/* 在这里插入GL调用 */},
  5. // 等等...
  6. _ => {panic!()}
  7. }
  8. }

我还遇到了traits,但尝试调整它们以实现我需要的功能时没有成功。我希望它会像下面这样,尽管我知道语法非常奇怪。

  1. trait SetUniform<T> {
  2. fn set_uniform(&mut self, name: &str, value: T);
  3. }

是否有一种方法可以只使用一个函数名来接受任何类型的参数,还是我应该妥协并为每种不同的类型创建一堆不同命名的set_uniform函数?

英文:

I've been learning Rust recently and am working on a 3d engine using glfw3 and gl. I've got as far as trying to get a shader program that worked in my C++ engine working on my Rust one but have hit a snag on the implementation of setting shader uniforms.

shader.h:

  1. template &lt;typename T&gt; void SetUniform(const GLchar* name, const T&amp; data);

shader.cpp

  1. template &lt;&gt; void ShaderProgram::SetUniform&lt;int&gt;(const GLchar* name, const int&amp; data) {
  2. glUniform1i(GetUniformLocation(name), data);
  3. }
  4. template &lt;&gt; void ShaderProgram::SetUniform&lt;float&gt;(const GLchar* name, const float&amp; data) {
  5. glUniform1f(GetUniformLocation(name), data);
  6. }
  7. template &lt;&gt; void ShaderProgram::SetUniform(const GLchar* name, const glm::vec2&amp; data) {
  8. glUniform2f(GetUniformLocation(name), data.x, data.y);
  9. }
  10. //etc...

With my limited knowledge of Rust, I tried using a match statement, as it has so far saved me a lot of hassle, to no avail.

  1. pub fn set_uniform&lt;T&gt;(&amp;mut self, value: T){
  2. match T {
  3. u32 =&gt; {/*insert gl call here*/},
  4. f32 =&gt; {/*insert gl call here*/},
  5. //etc...
  6. _ =&gt; {panic!()}
  7. }
  8. }

I also came across traits, but had no luck trying to tweak them to do what I needed. I'd hoped it would have been something like this although I understand the syntax is very wonky.

  1. trait SetUniform&lt;T, Self = ShaderProgram&gt; {
  2. fn set_uniform(self: &amp;mut Self, value: T)
  3. where
  4. T: Sized;
  5. }

Is there any way to do this with just one function name that can take in any type of parameter or should I just give in and make a bunch of differently named set_uniform functions for each different type?

答案1

得分: 14

这可以通过特质来完成。

改变的是 value 的类型,所以我们需要一个可以在这些类型上实现的特质,比如 i32f32 等。可以像这样实现:

  1. trait SetUniform {
  2. fn set_uniform(location: GLint, value: Self);
  3. }

(我假设 glUniform 函数都具有类似 fn(GLint, ...) 的签名)

现在我们为我们的参数类型实现它:

  1. impl SetUniform for f32 {
  2. fn set_uniform(location: GLint, value: Self) {
  3. glUniform1f(location, value)
  4. }
  5. }
  6. impl SetUniform for i32 {
  7. fn set_uniform(location: GLint, value: Self) {
  8. glUniform1i(location, value)
  9. }
  10. }
  11. // 这里只是猜测类型,但原理应该很清楚
  12. impl SetUniform for &Vec2 {
  13. fn set_uniform(location: GLint, value: Self) {
  14. glUniform2f(location, value.x, value.y)
  15. }
  16. }
  17. // 对于其他类型等等

然后,你的调用函数(为了说明简化了)可以看起来像这样:

  1. fn set_uniform<T: SetUniform>(location: GLint, value: T) {
  2. // ^^^^^^^^^^^^^ `T` is now bound on the `SetUniform` trait...
  3. SetUniform::set_uniform(location, value)
  4. // ^^^^^ ... which is why this works
  5. }
英文:

This can be done with traits.

What changes is the type of value so we need a trait that we can implement on those types, i32, f32, etc. This could look something like this:

  1. trait SetUniform {
  2. fn set_uniform(location: GLint, value: Self);
  3. }

(I'm assuming that the glUniform functions all have a signature like fn(GLint, ...) or similar)

Now we implement it for our argument types:

  1. impl SetUniform for f32 {
  2. fn set_uniform(location: GLint, value: Self) {
  3. glUniform1f(location, value)
  4. }
  5. }
  6. impl SetUniform for i32 {
  7. fn set_uniform(location: GLint, value: Self) {
  8. glUniform1i(location, value)
  9. }
  10. }
  11. // guessing the types here, but the principle should be clear
  12. impl SetUniform for &amp;Vec2 {
  13. fn set_uniform(location: GLint, value: Self) {
  14. glUniform2f(location, value.x, value.y)
  15. }
  16. }
  17. // etc for other types

Then your calling function (simplified for this illustration) can look like this:

  1. fn set_uniform&lt;T: SetUniform&gt;(location: GLint, value: T) {
  2. // ^^^^^^^^^^^^^ `T` is now bound on the `SetUniform` trait...
  3. SetUniform::set_uniform(location, value)
  4. // ^^^^^ ... which is why this works
  5. }

huangapple
  • 本文由 发表于 2023年6月12日 05:00:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/76452498.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定