如何创建类似CgFx的效果系统?

发布于 2024-12-10 04:41:11 字数 418 浏览 3 评论 0原文

像CryEngine3、Unreal Engine 3这样的严肃的图形引擎都有自己定制的着色器语言和效果系统。在尝试为我的小型图形框架找到一些效果系统时,看起来nvidia CgFx是唯一的选择(似乎Khronos有一个名为glFx的项目,但项目页面现在是404)。

我有几个理由制作自己的效果系统:

  1. 我需要更多地控制如何以及何时传递着色器参数。
  2. 为了重用着色器片段,我想创建一些类似 C++ 宏的机制。使用宏来进行一些条件编译也很有用,CryEngine 用来产生各种效果的方式。
  3. 看起来 GLSL 没有这样的效果系统,

所以我想知道如何创建一个效果系统?我是否需要从头开始编写语法解析器,或者已经有一些代码/工具能够做到这一点?

PS:我将 OpenGL 与 GLSL 和 CG 一起使用。

Seriouse graphics engine like CryEngine3, Unreal Engine 3 have their customized shader language and effect system. While trying to find some effect system for my small graphics framework, it looks like nvidia CgFx is the only choice (seems Khronos had a project called glFx, but the project page is 404 now).

I have several reasons to make a effect system of my own:

  1. I need more control about how and when to pass the shader parameters.
  2. In order to reuse shader snippets, I want to create some c++ macro like mechanism. It's also useful to use macro to do some conditional compilation, and that the way CryEngine used to produces various effects.
  3. Looks like GLSL don't have such effect system

so I am wondering how to create a effect system? Do I need to write grammar parser from scratch or there's already some code/tools able to do this thing?

PS: I am using OpenGL with both GLSL and CG.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

Hello爱情风 2024-12-17 04:41:11

早在我使用 HLSL 的时候,我开发了一个小型着色器系统,它允许我通过数据指定所有参数,这样我就可以编辑一种包含参数列表和着色器代码的 XML 文件,然后保存后,引擎会自动重新加载它,重新绑定所有参数等。

与UDK中的内容相比,这没什么,但相当方便,我猜你正在尝试实现类似的东西?

我是,那么这里有一些事情要做。首先,您需要创建一个类来抽象着色器参数处理(绑定、设置等)。

class IShaderParameter
{
protected:
    IShaderParameter(const std::string & name)
        : m_Uniform(-1)
        , m_Name(name)
    {}
    GLuint m_Uniform;
    std::string m_Name;
public:
    virtual void Set(GLuint program) = 0;
};

然后,对于静态参数,您可以简单地创建这样的重载:

template < typename Type >
class StaticParameter
    : public IShaderParameter
{
public:
    StaticParameter(const std::string & name, const Type & value)
        : IShaderParameter(name)
        , m_Value(value)
    {}
    virtual void Set(GLuint program)
    {
        if (m_Uniform == -1)
            m_Uniform = glGetUniformLocation(program, m_Name.c_str());
        this->SetUniform(m_Value);
    }
protected:
    Type m_Value;
    void SetUniform(float value) { glUniform1f(m_Uniform, value); }
    // write all SetUniform specializations that you need here
    // ...
};

并且按照相同的想法,您可以创建“动态着色器参数”类型。例如,如果您希望能够将灯光的参数绑定到着色器,请创建专门的参数类型。在其构造函数中,传递灯光的 id,以便它知道如何在 Set 方法中检索灯光。只需做一点工作,您就可以拥有一大堆参数,然后可以自动将它们绑定到引擎的实体(材质参数、灯光参数等)。

最后要做的就是创建一个小的自定义文件格式(我使用了xml)来定义各种参数和加载程序。例如,在我的例子中,它看起来像这样:

<shader>
    <param type="vec3" name="lightPos">light_0_position</param>
    <param type="vec4" name="diffuse">material_10_diffuse</param>
    <vertexShader>
      ... a CDATA containing your shader code
    </vertexShader>
</shader>

在我的引擎中,“light_0_position”表示灯光参数,0是灯光的ID,位置是要获取的参数。参数和实际值之间的绑定是在加载期间完成的,因此没有太多开销。

不管怎样,我不知道这是否能回答你的问题,也不要太认真地对待这些示例代码(HLSL 和 OpenGL 着色器的工作方式完全不同,而且我不是 OpenGL 专家 ^^),但希望它能给你一些帮助线索:)

Back in the day when I was using HLSL, I developped a little shader system which allowed me to specify all my parameters through data, so that I could just edit a sort of XML file containing the list of parameters and the shader codes, and after saving, the engine would automatically reload it, rebind all parameters, etc.

It's nothing compared to what's found in the UDK, but pretty convenient, and I guess you're trying to implement something like that ?

I it is, then here are a few stuff to do. First, you need to create a class to abstract shader parameter handling (binding, setting, etc.) Something along these lines :

class IShaderParameter
{
protected:
    IShaderParameter(const std::string & name)
        : m_Uniform(-1)
        , m_Name(name)
    {}
    GLuint m_Uniform;
    std::string m_Name;
public:
    virtual void Set(GLuint program) = 0;
};

Then, for static parameters, you can simply create an overload like this :

template < typename Type >
class StaticParameter
    : public IShaderParameter
{
public:
    StaticParameter(const std::string & name, const Type & value)
        : IShaderParameter(name)
        , m_Value(value)
    {}
    virtual void Set(GLuint program)
    {
        if (m_Uniform == -1)
            m_Uniform = glGetUniformLocation(program, m_Name.c_str());
        this->SetUniform(m_Value);
    }
protected:
    Type m_Value;
    void SetUniform(float value) { glUniform1f(m_Uniform, value); }
    // write all SetUniform specializations that you need here
    // ...
};

And along the same idea, you can create a "dynamic shader parameter" type. For example, if you want to be able to bind a light's parameter to your shader, create a specialized parameter's type. In its constructor, pass the light's id so that it will know how to retrieve the light in the Set method. With a little work, you can have a whole bunch of parameters that you can then automatically bind to an entity of your engine (material parameters, light parameters, etc.)

The last thing to do is create a little custom file format (I used xml) to define your various parameters, and a loader. For example, in my case, it looked like this :

<shader>
    <param type="vec3" name="lightPos">light_0_position</param>
    <param type="vec4" name="diffuse">material_10_diffuse</param>
    <vertexShader>
      ... a CDATA containing your shader code
    </vertexShader>
</shader>

In my engine, "light_0_position" would mean a light parameter, 0 is the light's ID, and position is the parameter to get. Binding between the parameter and the actual value was done during loading so there was not much overhead.

Anyway, I don't if that answer your question, and don't take these sample codes too seriously (HLSL and OpenGL shaders work quite differently, and I'm no OpenGL expert ^^) but hopefully it'll give you a few leads :)

冷情 2024-12-17 04:41:11
  1. 您能详细说明一下吗?通过直接使用 OpenGL,您可以完全控制传递给 GPU 的参数。您究竟缺少什么?

  2. (和 3.)GLSL 确实支持重用代码。您可以拥有一个提供不同功能的着色器库。为了使用任何函数,您只需在客户端着色器中预先声明它(vec4 get_diffuse();),并在链接之前将实现该函数的着色器对象附加到着色器程序。

  1. Could you elaborate on this? By working with OpenGL directly you have a full control over the parameters being passed to the GPU. What exactly are you missing?

  2. (and 3.) GLSL does support re-using the code. You can have a library of shaders providing different functions. In order to use any function you just need to pre-declare it in the client shader (vec4 get_diffuse();) and attach the shader object implementing the function to the shader program before linking.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文