Skip to content

SpriteComponent#color is applied in linear space, unlike every other colour API #9240

Description

@mvaligursky

Every colour-valued public API in the engine is sRGB/gamma-encoded and converted to linear at upload — except SpriteComponent#color, which is passed through raw.

The inconsistency

SpriteComponent writes the tint straight into the material_emissive uniform with no conversion:

// src/framework/components/sprite/component.js:419-422 (and again at 903)
this._colorUniform[0] = this._color.r;
this._colorUniform[1] = this._color.g;
this._colorUniform[2] = this._color.b;
this._meshInstance.setParameter(PARAM_EMISSIVE, this._colorUniform);

ElementComponent writes the same uniform but converts first, and says so:

// src/framework/components/element/image-element.js:_updateRenderableEmissive
// color uniforms are in linear space
_tempColor.linear(this._color);
this._renderable.setParameter('material_emissive', this._colorUniform);

Same pattern in text-element.js for color, outlineColor and shadowColor.

Sprites use a StandardMaterial (src/framework/components/sprite/system.js), and StandardMaterial's own emissive also goes through the conversion — _defineColor in src/scene/materials/standard-material.js does _tempColor.linear(color) with the comment "uniforms are always in linear space". So the same uniform, on the same material class, has two different conventions depending on which component sets it.

Effect

sprite.color = new Color(0.5, 0.5, 0.5) reaches the shader as linear 0.5, whereas the same tint on an ElementComponent image — or material.emissive = (0.5, 0.5, 0.5) — reaches it as ~0.218. That is roughly 2.3x brighter in linear terms at mid-grey.

The two conventions agree exactly at 0 and 1, which is presumably why this has gone unnoticed: only mid-tone tints diverge.

Notes

  • Found by source inspection while auditing colour spaces across the engine; not yet confirmed by rendering a sprite side by side with an equivalent Element image, so worth a visual check first.
  • Fixing it changes the appearance of existing mid-tone sprite tints, so it is effectively a breaking visual change and probably wants to land on a major.
  • The alternative (documenting SpriteComponent#color as linear) leaves the engine internally inconsistent, and inconsistent with ElementComponent, which seems worse.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions