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

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

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

问题

shader.h:

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

shader.cpp:

template <> void ShaderProgram::SetUniform<int>(const GLchar* name, const int& data) {
    glUniform1i(GetUniformLocation(name), data);
}

template <> void ShaderProgram::SetUniform<float>(const GLchar* name, const float& data) {
    glUniform1f(GetUniformLocation(name), data);
}

template <> void ShaderProgram::SetUniform(const GLchar* name, const glm::vec2& data) {
    glUniform2f(GetUniformLocation(name), data.x, data.y);
}
// etc...

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

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

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

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

是否有一种方法可以只使用一个函数名来接受任何类型的参数,还是我应该妥协并为每种不同的类型创建一堆不同命名的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:

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

shader.cpp

template &lt;&gt; void ShaderProgram::SetUniform&lt;int&gt;(const GLchar* name, const int&amp; data) {
	glUniform1i(GetUniformLocation(name), data);
}

template &lt;&gt; void ShaderProgram::SetUniform&lt;float&gt;(const GLchar* name, const float&amp; data) {
	glUniform1f(GetUniformLocation(name), data);
}

template &lt;&gt; void ShaderProgram::SetUniform(const GLchar* name, const glm::vec2&amp; data) {
	glUniform2f(GetUniformLocation(name), data.x, data.y);
}
//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.

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

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.

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

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 等。可以像这样实现:

trait SetUniform {
    fn set_uniform(location: GLint, value: Self);
}

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

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

impl SetUniform for f32 {
    fn set_uniform(location: GLint, value: Self) {
        glUniform1f(location, value)
    }
}

impl SetUniform for i32 {
    fn set_uniform(location: GLint, value: Self) {
        glUniform1i(location, value)
    }
}

// 这里只是猜测类型,但原理应该很清楚
impl SetUniform for &Vec2 {
    fn set_uniform(location: GLint, value: Self) {
        glUniform2f(location, value.x, value.y)
    }
}

// 对于其他类型等等

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

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

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:

trait SetUniform {
    fn set_uniform(location: GLint, value: Self);
}

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

Now we implement it for our argument types:

impl SetUniform for f32 {
    fn set_uniform(location: GLint, value: Self) {
        glUniform1f(location, value)
    }
}

impl SetUniform for i32 {
    fn set_uniform(location: GLint, value: Self) {
        glUniform1i(location, value)
    }
}

// guessing the types here, but the principle should be clear
impl SetUniform for &amp;Vec2 {
    fn set_uniform(location: GLint, value: Self) {
        glUniform2f(location, value.x, value.y)
    }
}

// etc for other types

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

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

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:

确定