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

时间:2011-10-18 03:06:33

标签: opengl glsl shader game-engine cg

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

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

  1. 我需要更多控制如何以及何时传递着色器参数。
  2. 为了重用shader片段,我想创建一些类似c ++宏的机制。使用宏来进行一些条件编译,以及CryEngine用来产生各种效果的方式也很有用。
  3. 看起来GLSL没有这样的效果系统
  4. 所以我想知道如何创建效果系统?我是否需要从头开始编写语法分析器,或者已经有一些代码/工具能够做到这一点?

    PS:我在GLSL和CG上使用OpenGL。

2 个答案:

答案 0 :(得分:4)

在我使用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
    // ...
};

根据相同的想法,您可以创建“动态着色器参数”类型。例如,如果您希望能够将灯光的参数绑定到着色器,请创建专用参数的类型。在它的构造函数中,传递light的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,而position是要获取的参数。参数和实际值之间的绑定是在加载期间完成的,因此没有太多开销。

无论如何,如果这回答你的问题,我不会这样做,并且不要太认真地对待这些示例代码(HLSL和OpenGL着色器工作方式完全不同,我不是OpenGL专家^^)但希望它能给出你有几个线索:)

答案 1 :(得分:0)

  1. 你能详细说明一下吗?通过直接使用OpenGL,您可以完全控制传递给GPU的参数。你究竟错过了什么?

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