【问题标题】:Class Hierarchy for Colors颜色的类层次结构
【发布时间】:2012-01-31 04:23:00
【问题描述】:

我无法确定使用哪个类结构来存储 RGB/RGBA 颜色信息。

我正在制作一个非常简单的 3D 游戏引擎来获得乐趣并磨练我的 OO 编程能力。

我希望我的引擎同时支持 RGB 和 RGBA 颜色。最重要的是,我希望它可以将 RGB 和 RGBA 值指定为固定为 [0, 1] 的浮点数或无符号字符。我认为出于存储目的,最好使用无符号字符,因为它们使用的内存是浮点数的四分之一。

所以现在,我设计了以下类用于此目的:Color、Color3、Color3f、Color3c、Color4、Color4f 和 Color4c。 Color 是 Color3 和 Color4 的超类,而 Color3 是 Color3f 和 Color3c 的超类,Color4 是 Color4f 和 Color4c 的超类。

这似乎是一种比实际需要复杂得多的方法。我也考虑过使用泛型,所以用 Color3 和 Color3 代替 Color3f 和 Color3c。但是,这种方法似乎也不对。

理想情况下,我希望能够编写如下代码:

Color3 red1(255, 0, 0);
Color3 red2(1.0f, 0.0f, 0.0f);
Color* someColor = new Color3(1.0f, 0.0f, 0.5f);
Color4 anotherColor = *someColor;
Color4 thisColor = anotherColor.toChar();
//thisColor.r = 255, thisColor.g = 0, thisColor.b = 127
thisColor = thisColor.toFloat();
//thisColor.r = 1.0f, thisColor.g = 0.0f, thisColor.b = 0.5f

现在这不能在 C++ 中实现(至少据我所知),但是我如何在不创建 7 个单独的类的情况下集结相同的功能呢?并且不存储浪费的信息?例如,在内存中绘制 1024x1024 的图像。那将是一个包含超过一百万种颜色的数组,那么如何创建一个灵活且可重用的颜色类层次结构?即,通过无符号字符将 RGB 和 RGBA 值存储在不同的结构中,但提供检索浮点值的功能?

对不起,如果这不够具体或什么,这是我的第一个问题!让我知道还有什么有用的。如果你们愿意,我可以发布迄今为止我尝试过的代码,但希望你们理解我想要完成的工作。

【问题讨论】:

  • 过度工程太多了,嗯? :) 请注意,您作为示例编写的内容完全可以在 C++ 中完成,并带有一些 pimpl 习惯用法和接口。并不是说那样做是个好主意……

标签: c++ class colors hierarchy


【解决方案1】:

您想为所有可能性创建一个类。这是错误的。一个班级就可以应付所有这些问题。

首先,您的 GPU 将期望 4x [0, 1] 浮点数用于颜色数据,并且两个浮点数 [0, 1] 比 [0, 255] RGB 颜色更精确。这意味着从 float 0,1 变为 char 0, 255 是有损的。这两个事实意味着您实际上别无选择,只能使用 4x [0, 1] 作为实现。是否为 [0, 255] 提供访问器和构造器取决于您。

其次,您可以通过简单地将 A 设置为 1 在同一类中轻松支持 RGB 和 RGBA - 这样,任何不想处理 A 的人都不必处理。

【讨论】:

  • 除了使用 4x[0,1] 浮点数需要 4 倍的内存。这对于一些处理非常大的图像(例如 2K 视频帧)的应用程序是不可接受的。
  • 我的想法与 André 完全一致 - 虽然您提出了很好的观点 DeadMG,但使用 16 个字节来表示 1024x1024 纹理的 RGB 颜色是否合理?不过,我怀疑我会走得更高。也许是 2048x2048?当它可以用 3 个字节相当好地表示时,这似乎浪费了很多内存......
  • @Andre:据我所知,GPU 使用自己的压缩算法,您不必自己实现。其次,现代高端 GPU 配备 大量 VRAM。
  • @berserkguard:可能是这样。但是,一旦您将其移至 GPU,它就必须进入 12 个字节无论如何,因此您实际上并没有为自己节省一个字节的空间。无论如何,2048x2048x12 仅 48MB - 您现在可以在显卡上找到的 2 或 4 GB 是杯水车薪。
  • 感谢您从 GPU 的角度描述问题,因为这肯定与我想要在更深层次上完成的工作有关。 +1
【解决方案2】:

你只需要一种颜色(或者最好是颜色)

您可以在内部随意存储值,通常使用 32 位无符号整数 RGBA(或 BGRA),然后只需使用掩码和位移位来提取每个组件。

您可以拥有采用Color(unsigned char red,unsigned char green,unsigned char blue, unsigned char alpha=0xff) 或浮点数的构造函数

对于每个表单中的每个组件,类似的 getter/setter。

编辑:但是如果您正在计划任何类型的高性能,那么由每个像素的颜色对象数组组成的图像可能不是要走的路。您可能想查看一组静态函数来设置/获取每个组件和一个构造函数,该构造函数采用图像格式格式的像素(即 BGRA 或 RGB 无符号整数)

【讨论】:

  • 如果您想要节省空间,位压缩是您的最佳选择。
  • 虽然只包含 4 个字符的结构应该以相同的效率打包。
  • 错,对,nvm我。我脑子里有一些关于填充的奇怪想法。
  • 有趣的是,您注意到使用颜色而不是颜色。在搜索 stackoverflow 以查看是否已经提出了类似的问题时,我遇到了 this post 提出的完全相反的问题。不在这个问题的范围内,只是想指出这一点。另外,我正在查看每个人的cmets。都非常有帮助;我什至没有想过使用位掩码。我稍后会回复进展 - 再次感谢大家!
  • @berserkguard - color 是英国(和加拿大等)拼写,color 是美国拼写,所以几乎所有软件都使用颜色。让英国人很烦恼!
【解决方案3】:

这只是编码其他人所说的,因为它看起来很有趣。这不是答案。

class color {
    unsigned char red_, green_, blue_, alpha_;
public:
    color() 
    :red_(0), green_(0), blue_(0), alpha_(0xFF) {}
    color(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha=0xFF)
    :red_(red), green_(green), blue_(blue), alpha_(alpha) {}
    color(float red, float green, float blue, float alpha=1.0)
    :red_(red*255.0+.5), green_(green*255.0+.5), blue_(blue*255.0+.5), alpha_(alpha*255.0+.5) {}
    color(const color& rhs)
    :red_(rhs.red_), green_(rhs.green_), blue_(rhs.blue_), alpha_(rhs.alpha_) {}
    //operator= and dtor automatically generated
    unsigned char& c_red() {return red_;}
    unsigned char& c_green() {return green_;}
    unsigned char& c_blue() {return blue_;}
    unsigned char& c_alpha() {return alpha_;}
    const unsigned char& c_red() const {return red_;}
    const unsigned char& c_green() const {return green_;}
    const unsigned char& c_blue() const {return blue_;}
    const unsigned char& c_alpha() const {return alpha_;}
    void set_f_red(float val) {red_=val*255.0+.5;}
    void set_f_green(float val) {green_=val*255.0+.5;}
    void set_f_blue(float val) {blue_=val*255.0+.5;}
    void set_f_alpha(float val) {alpha_=val*255.0+.5;}
    float get_f_red() const {return red_/255.0;}
    float get_f_green() const {return green_/255.0;}
    float get_f_blue() const {return blue_/255.0;}
    float get_f_alpha() const {return alpha_/255.0;}

    unsigned int rgba() {return (red_<<24)|(green_<<16)|(blue_<<8)|(alpha_<<0);}
};

【讨论】:

  • 有趣...我会存储为无符号整数,因为与 ctor 和 getter/setter 相比,我希望 rgba 方法被调用得更多。这种偏好有什么理由吗?
  • 基本上,便携性。一些应用程序需要 argb、一些 rgba、一些 rbg...此外,不用担心字节序问题...您是对的,但 unsigned int rgba 在大多数情况下会更快。
【解决方案4】:

Xeo 关于过度工程的观点是一个很好的观点,您可能想阅读我关于避免over-engineering 的回答。但 OO 的全部意义在于隐藏复杂性,而不是鼓励它。

从具有一些有效内部表示的 Color 类开始,并为您想要作为参数的数据类型重载 ctor。除非您有一些需要来公开实现,否则不要打扰所有子类。采取可能可行的最简单方法。

【讨论】:

  • 感谢您提供的信息量很大的帖子。我总是倾向于过度考虑每一个细节,但我真的很喜欢你只做绝对需要的事情并只在必要时添加其他功能的方法。
猜你喜欢
  • 2010-11-19
  • 2012-06-16
  • 2011-05-19
  • 2017-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-09
相关资源
最近更新 更多