Skip to content

BufferPrimitive: Add 'alpha' support#13384

Merged
donmccurdy merged 13 commits into
mainfrom
donmccurdy/feat/bufferprim-alpha
May 15, 2026
Merged

BufferPrimitive: Add 'alpha' support#13384
donmccurdy merged 13 commits into
mainfrom
donmccurdy/feat/bufferprim-alpha

Conversation

@donmccurdy
Copy link
Copy Markdown
Member

@donmccurdy donmccurdy commented Apr 8, 2026

Description

Changes:

  • Add blendOption parameter in BufferPrimitiveCollection constructors
  • Support material.color.alpha in BufferPrimitiveCollection subclasses
  • Support material.outlineColor.alpha in BufferPointCollection

Tasks:

  • Expose collection.blendOption per-collection option
  • Support pointMaterial.color.alpha
  • Support pointMaterial.outlineColor.alpha
  • Support polylineMaterial.color.alpha
  • Support polygonMaterial.color.alpha
  • Unit tests

Issue number and link

Testing plan

Unit tests added.

Sandcastle:

import * as Cesium from "cesium";

const viewer = new Cesium.Viewer("cesiumContainer");

const collection = new Cesium.BufferPolylineCollection({
  primitiveCountMax: 1024,
  vertexCountMax: 1024,
});

viewer.scene.primitives.add(collection);

const degreesArray = [-105.0, 40.0, -100.0, 38.0, -105.0, 35.0, -100.0, 32.0];

const positions = Cesium.Cartesian3.packArray(
  Cesium.Cartesian3.fromDegreesArray(degreesArray),
  new Float64Array((degreesArray.length * 3) / 2),
);

const polyline = new Cesium.BufferPolyline();
const material = new Cesium.BufferPolylineMaterial({color: Cesium.Color.RED, width: 10});
material.color.alpha = 0.25;

collection.add({ positions, material }, polyline);

viewer.zoomTo(collection);

Author checklist

  • I have submitted a Contributor License Agreement
  • I have added my name to CONTRIBUTORS.md
  • I have updated CHANGES.md with a short summary of my change
  • I have added or updated unit tests to ensure consistent code coverage
  • I have updated the inline documentation, and included code examples where relevant
  • I have performed a self-review of my code

AI acknowledgment

  • I used AI to generate content in this PR
  • If yes, I have reviewed the AI-generated content before submitting

If yes, I used the following Tools(s) and/or Service(s):

If yes, I used the following Model(s):

PR Dependency Tree

This tree was auto-generated by Charcoal

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

Thank you for the pull request, @donmccurdy!

✅ We can confirm we have a CLA on file for you.

@donmccurdy donmccurdy marked this pull request as ready for review May 14, 2026 16:07
typedArray: collection._positionView,
context,
usage: BufferUsage.DYNAMIC_DRAW,
usage: BufferUsage.STATIC_DRAW,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated - I've set all buffer attributes to STATIC_DRAW, just to keep things consistent for now. In the future I'd like to make this an option the user can control, if we can measure a difference in performance, which I haven't managed to do (at least on my machine) yet.

* @readonly
* @ignore
*/
this._blendOption = options.blendOption ?? BlendOption.TRANSLUCENT;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose TRANSLUCENT as the default to reduce aliasing — if aliasing were less of an issue, and/or if the default canvas resolution in CesiumJS matched the display (#13336) then OPAQUE would probably make more sense as the default.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BillboardCollection and PointPrimitiveCollection default to OPAQUE_AND_TRANSLUCENT. That might be the better option long term though it does require splitting the rendering into two commands, an opaque command and a translucent command.

Copy link
Copy Markdown
Member

@danielzhong danielzhong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great PR! Few questions:

  1. Translucent mode should submit the the pass to Pass.TRANSLUCENT right? I see renderBufferPointCollection.js and polylines, polygons still using:
pass: Pass.OPAQUE,

Is there any sample test cases for me to test it as well?

  1. When blendOption is TRANSLUCENT, the render state doesn't set depthMask: false. This means transparent primitives still write to the depth buffer, I'm not sure but do you think will this affect anything?
  2. PointPrimitiveCollection exposes blendOption as a plain public property, allowing users to read and update it at runtime. For now, BufferPrimitiveCollection only stores _blendOption as a private field. Not sure if we need to have getter or setter for this (maybe no need?).

@donmccurdy
Copy link
Copy Markdown
Member Author

donmccurdy commented May 15, 2026

Translucent mode should submit the the pass to Pass.TRANSLUCENT right?

Good catch, thanks! Yes, probably we should derive the pass from the blend option. Done.

Is there any sample test cases for me to test it as well?

The sandcastle snippet above is an easy test, or any of our existing vector tilesets can be tested by using styles like this:

tileset.style = new Cesium.Cesium3DTileStyle({
  color: "color('white', 0.25)",
  pointSize: 4,
  pointOutlineWidth: 2,
  pointOutlineColor: "color('purple', 0.25)",
});

I'm not aware that it's possible to change the blend option on a tileset currently.

When blendOption is TRANSLUCENT, the render state doesn't set depthMask: false. This means transparent primitives still write to the depth buffer, I'm not sure but do you think will this affect anything?

Good question. It definitely could affect things, but I don't think I'd want to derive the depth write setting directly from the blend option. I think we could expose depth buffer settings on the collection later, though, if that becomes necessary.

PointPrimitiveCollection exposes blendOption as a plain public property, allowing users to read and update it at runtime.

I'd be OK with letting users change this setting later, but no immediate use case for it, so I'm inclined to wait and keep things simple for now.


/cc @lilleyse just FYI, in case any of this feels like it may be inconsistent with CesiumJS user expectations?

@donmccurdy donmccurdy added this pull request to the merge queue May 15, 2026
Merged via the queue into main with commit 1d3f9d5 May 15, 2026
13 of 15 checks passed
@donmccurdy donmccurdy deleted the donmccurdy/feat/bufferprim-alpha branch May 15, 2026 18:22
@lilleyse
Copy link
Copy Markdown
Contributor

When blendOption is TRANSLUCENT, the render state doesn't set depthMask: false. This means transparent primitives still write to the depth buffer, I'm not sure but do you think will this affect anything?

Good question. It definitely could affect things, but I don't think I'd want to derive the depth write setting directly from the blend option. I think we could expose depth buffer settings on the collection later, though, if that becomes necessary.

FWIW, PointPrimitiveCollection derives depth write from the blend option:

if (
this._blendOption === BlendOption.OPAQUE ||
this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT
) {
this._rsOpaque = RenderState.fromCache({
depthTest: {
enabled: true,
func: WebGLConstants.LEQUAL,
},
depthMask: true,
});
} else {
this._rsOpaque = undefined;
}
if (
this._blendOption === BlendOption.TRANSLUCENT ||
this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT
) {
this._rsTranslucent = RenderState.fromCache({
depthTest: {
enabled: true,
func: WebGLConstants.LEQUAL,
},
depthMask: false,
blending: BlendingState.ALPHA_BLEND,
});
} else {
this._rsTranslucent = undefined;
}
}

@donmccurdy
Copy link
Copy Markdown
Member Author

Thanks @lilleyse! My only hesitation is that I'd recalled alpha blending helping to mitigate aliasing recently, for otherwise opaque geometry, related to #13336. I should probably make a reproduction to confirm that use case, but if so, then disabling depthMask when alpha blending is enabled would make it difficult to use that approach for opaque geometry.

Not feeling firmly decided on this either way. Related to the "Create a Sandcastle comparing BufferPoint/Polyline/Polygon classes to existing CesiumJS APIs..." task in #13444.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants