草庐IT

c++ - 渲染引擎设计 - 抽象出资源的 API 特定代码

coder 2023-06-02 原文

关闭。这个问题是opinion-based .它目前不接受答案。












想改善这个问题吗?更新问题,以便可以通过 editing this post 用事实和引文回答问题.

2年前关闭。




Improve this question




我的渲染代码中有一个非常大的设计绊脚石。基本上这是什么,不需要特定于 API 的代码(例如 OpenGL 代码或 DirectX)。现在我想了很多方法来解决这个问题,但是我不确定使用哪一种,或者我应该如何改进这些想法。

举一个简单的例子,我将使用一个纹理作为例子。纹理是表示 GPU 内存中纹理的对象,在实现方面它可以以任何特定方式相似,即实现是否使用 GLuintLPDIRECT3DTEXTURE9以类似于纹理。

现在这里是我想到的实际实现这一点的方法。我不确定是否有更好的方法,或者哪种方法比另一种更好。

方法一:继承

我可以使用继承,这似乎是最明显的选择。但是,此方法需要虚函数,并且需要 TextureFactory 类才能创建 Texture 对象。这需要调用 new每个Texture对象(例如 renderer->getTextureFactory()->create() )。

这是我在这种情况下考虑使用继承的方式:

class Texture
{
public:

    virtual ~Texture() {}

    // Override-able Methods:
    virtual bool load(const Image&, const urect2& subRect);
    virtual bool reload(const Image&, const urect2& subRect);
    virtual Image getImage() const;

    // ... other texture-related methods, such as wrappers for
    // load/reload in order to load/reload the whole image

    unsigned int getWidth() const;
    unsigned int getHeight() const;
    unsigned int getDepth() const;

    bool is1D() const;
    bool is2D() const;
    bool is3D() const;

protected:

    void setWidth(unsigned int);
    void setHeight(unsigned int);
    void setDepth(unsigned int);

private:
    unsigned int _width, _height, _depth;
};

然后为了创建 OpenGL(或任何其他 API 特定的)纹理,必须创建一个子类,例如 OglTexture .

方法 2:使用“TextureLoader”或其他一些类

这个方法听起来很简单,我使用另一个类来处理纹理的加载。这可能会或可能不会使用虚函数,这取决于具体情况(或者我是否觉得有必要)。

例如一个多态纹理加载器
 class TextureLoader
 {
 public:

      virtual ~TextureLoader() {}


      virtual bool load(Texture* texture, const Image&, const urect2& subRect);
      virtual bool reload(Texture* texture, const Image&, const urect2& subRect);
      virtual Image getImage(Texture* texture) const;
 };

如果我要使用它,一个 Texture object 只会是 POD 类型。但是,为了使其工作,句柄对象/ID 必须存在于 Texture 中。类(class)。

例如,this is how I would more than likely implement it .虽然,我可以使用基类来概括整个 ID 的事情。如Resource基类在这种情况下保存图形资源的 ID。

方法 3:Pimpl 成语

我可以使用 pimpl 成语,它实现了如何加载/重新加载/等。纹理。这很可能需要一个抽象工厂类来创建纹理。我不确定这比使用继承更好。这个 pimpl 习惯用法可以与方法 2 结合使用,即纹理对象将有一个指向它们的加载器的引用(指针)。

方法 4:使用概念/编译时多态

另一方面,我可以使用编译时多态性并且基本上使用我在继承方法中介绍的内容,除非不声明虚函数。这会起作用,但如果我想从 OpenGL 渲染动态切换到 DirectX 渲染,这将不是最佳解决方案。我会简单地将 OpenGL/D3D 特定代码放在 Texture 类中,其中将有多个纹理类具有相同的接口(interface)(加载/重新加载/getImage/等),包裹在某个命名空间中(类似于它使用的 API,例如 ogld3d 等)。

方法 5:使用整数

我可以只使用整数来存储纹理对象的句柄,这看起来很简单,但可能会产生一些“困惑”的代码。

其他 GPU 资源(例如 Geometry、Shaders 和 ShaderPrograms)也存在此问题。

我还想过让 Renderer 类处理图形资源的创建、加载等。但是这会违反 SPR .例如
Texture* texture = renderer->createTexture(Image("something.png"));
Image image = renderer->getImage(texture);

有人可以指导我吗,我想我想太多了。我尝试观察了各种渲染引擎,例如 Irrlicht、Ogre3D 以及我在网上找到的其他引擎。 Ogre 和 Irrlicht 使用继承,但是我不确定这是最好的选择。正如其他一些人只是使用 void*、整数,或者只是将 API 特定(主要是 OpenGL)代码放在他们的类中(例如,GLuint 直接放在 Texture 类中)。我真的无法决定哪种设计最适合我。

我要定位的平台是:
  • Windows/Linux/Mac
  • iOS
  • 可能是安卓

  • 我考虑过只使用 OpenGL 特定的代码,因为 OpenGL 适用于所有这些平台。但是,我觉得如果我这样做,如果我想移植到其他无法使用 OpenGL 的平台(例如 PS3),我将不得不大量更改我的代码。对我的情况的任何建议将不胜感激。

    最佳答案

    从高层次的角度考虑。您的渲染代码将如何与其他游戏/应用程序模型配合使用?换句话说,您计划如何在场景中创建对象以及模块化程度如何?在我之前使用引擎的工作中,一个设计良好的引擎的最终结果通常有一个遵循模式的循序渐进的过程。例如:

    //Components in an engine could be game objects such as sprites, meshes, lights, audio sources etc. 
    //These resources can be created via component factories for convenience
    CRenderComponentFactory* pFactory = GET_COMPONENT_FACTORY(CRenderComponentFactory);
    

    一旦获得了组件,通常可以使用多种重载方法来构造对象。以 Sprite 为例,SpriteComponent可以以子组件的形式包含 Sprite 可能需要的一切;像 TextureComponent例如。
    //Create a blank sprite of size 100x100 
    SpriteComponentPtr pSprite = pFactory->CreateSpriteComponent(Core::CVector2(100, 100));
    
    //Create a sprite from a sprite sheet texture page using the given frame number.
    SpriteComponentPtr pSprite = pFactory->CreateSpriteComponent("SpriteSheet", TPAGE_INDEX_SPRITE_SHEET_FRAME_1);
    
    //Create a textured sprite of size 100x50, where `pTexture` is your TextureComponent that you've set-up elsewhere.
    SpriteComponentPtr pSprite = pFactory->CreateSpriteComponent(Core::CVector2(100, 50), pTexture);
    

    然后只需将对象添加到场景中即可。这可以通过创建一个实体来完成,它只是一个通用的信息集合,包含场景操作所需的一切;位置、方向等。对于场景中的每个实体,您的 AddEntity方法默认情况下会将该新实体添加到您的渲染工厂,从子组件中提取其他依赖于渲染的信息。例如:
    //Put our sprite onto the scene to be drawn
    pSprite->SetColour(CColour::YELLOW);
    EntityPtr pEntity = CreateEntity(pSprite);
    mpScene->AddEntity(pEntity);
    

    然后,您将拥有一种创建对象的好方法和一种模块化的应用程序编码方式,而无需引用“绘制”或其他特定于渲染的代码。一个好的图形管道应该是这样的:



    This是渲染引擎设计的一个很好的资源(也是上图的来源)。跳到第 21 页并继续阅读,在那里您将看到有关场景图如何操作和一般发动机设计理论的深入解释。

    关于c++ - 渲染引擎设计 - 抽象出资源的 API 特定代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15548776/

    有关c++ - 渲染引擎设计 - 抽象出资源的 API 特定代码的更多相关文章

    1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

      我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

    2. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

      如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

    3. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

      在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

    4. ruby-on-rails - 渲染另一个 Controller 的 View - 2

      我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

    5. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

      我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

    6. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

      在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

    7. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

      我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

    8. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

      我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

    9. ruby-on-rails - 浏览 Ruby 源代码 - 2

      我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

    10. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

      我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

    随机推荐