Set vertex color of sprite in 3.5

Hello,

I could not find an up to date example of how to set the vertices colors of a sprite in 3.5.
Can someone explain to me how it’s done, or link me an example/doc that I might have missed ?
Thanks in advance.

If I adapt the older examples I found for the 3.5 API, it gives something like this :

@ccclass("ColorSprite")
export class ColorSprite extends SpriteComponent {
  @property({ type: [Color] })
  private colors: Color[] = [];


  protected _updateColor() {
    super._updateColor();

    const vData = this.renderData.vertexFormat;
    let colorOffset = 5;
    for (let i = 0; i < 4; i++) {
      const color = this.colors[i] || this.color;
      const colorR = color.r / 255;
      const colorG = color.g / 255;
      const colorB = color.b / 255;
      const colorA = this.node._uiProps.opacity;
      vData![colorOffset] = colorR;
      vData![colorOffset + 1] = colorG;
      vData![colorOffset + 2] = colorB;
      vData![colorOffset + 3] = colorA;
      colorOffset += 9;
    }
  }
}

But this gives an error :

Uncaught TypeError: Cannot read properties of undefined (reading ‘format’)
at updateOpacity (utils.ts:83:35)
at Batcher2D.walk (batcher-2d.ts:670:13)
at Batcher2D.walk (batcher-2d.ts:680:22)
at Batcher2D.walk (batcher-2d.ts:680:22)
at Batcher2D.update (batcher-2d.ts:204:18)
at Root.frameMove (root.ts:437:31)
at Director.tick (director.ts:715:25)
at callback (game.ts:838:26)

By the way, the reason I need this is because I want to apply a shader to a sprite that takes its sprite frame through an atlas. Because of the atlas, I no longer have access to uv coordinates from 0 to 1 in order to know where I am in the sprite in the fragment shader.
My idea to counter this was to use vertex color interpolation to simulates a 0 to 1 coordinate.
If someone has a better idea for this, let me know.

Ok, I found the way to do it :

import { _decorator, Sprite, Color } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('ColorSprite')
export class ColorSprite extends Sprite {
    @property([Color])
    private colors: Array<Color> = [Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE];

    start() {
        let colorOffset = 5;
        const vertexCount = this.renderData.vertexCount;
        if (vertexCount === 0) return;
        const vData = this.renderData.chunk.vb;
        const stride = this.renderData.floatStride;
        for(let i = 0; i < vertexCount; i++) {
            vData[colorOffset] = this.colors[i % 4].r / 255;
            vData[colorOffset + 1] = this.colors[i % 4].g / 255;
            vData[colorOffset + 2] = this.colors[i % 4].b / 255;
            vData[colorOffset + 3] = this.colors[i % 4].a / 255;
            colorOffset += stride;
        }
    }
}

It works well on sprites and labels that use ttf fonts. Both in 3.5 and 3.6.
However, when a label uses a bitmap font, it looks like something resets the vertices colors.
I do not find a way to counter that.
Any help ?

I had to read a lot of source files to find a solution, but here it is :

import {
  _decorator,
  Color,
  Label,
} from "cc";
const { ccclass, property } = _decorator;

@ccclass("ColorBitmapFontLabel")
export class ColorBitmapFontLabel extends Label {
  @property([Color])
  private colors: Array<Color> = [Color.RED, Color.RED, Color.BLUE, Color.BLUE];

  private originalFillBuffer: Function;

  start() {
    this.originalFillBuffer = this._assembler.fillBuffers;

    this._assembler.fillBuffers = (comp: Label, renderer) => {
      this.originalFillBuffer(comp, renderer);
      this.setVerticesColors();
    };
  }

  setVerticesColors() {
    let colorOffset = 5;
    const vertexCount = this.renderData.vertexCount;
    if (vertexCount === 0) return;
    const vData = this.renderData.chunk.vb;
    const stride = this.renderData.floatStride;
    for (let i = 0; i < vertexCount; i++) {
      vData[colorOffset] = this.colors[i % 4].r / 255;
      vData[colorOffset + 1] = this.colors[i % 4].g / 255;
      vData[colorOffset + 2] = this.colors[i % 4].b / 255;
      vData[colorOffset + 3] = this.colors[i % 4].a / 255;
      colorOffset += stride;
    }
  }
}
3 Likes

It does work on web platform.
Since we modify 2d/UI-rendering process in v3.6, rendering data of Label are stored in RenderEntity and transported to native to excute fillBuffers like web.
You might try to overwrite setEntityColor in UIRenderer, recombine a color and assign it to entity.color. Then build in one of native platforms and validate it.

1 Like

My target is web only. But thanks for the info.