Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
name="description"
content="Rendering Volume Cloud with Texture3D and Custom GLSL. Transplanted from Three.js"
/>
<meta name="cesium-sandcastle-labels" content="Development" />
<meta name="cesium-sandcastle-labels" content="Tutorials,Showcases" />
<title>Cesium Demo</title>
<script type="text/javascript" src="../Sandcastle-header.js"></script>
<script
Expand Down
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#### Additions :tada:

- Expand the CustomShader Sample to support real-time modification of CustomShader. [#12702](https://github.com/CesiumGS/cesium/pull/12702)
- Add wrapR property to Sampler and Texture3D, to support the newly added third dimension wrap.[#12701](https://github.com/CesiumGS/cesium/pull/12701)

## 1.131 - 2025-07-01

Expand Down
13 changes: 13 additions & 0 deletions packages/engine/Source/Renderer/Sampler.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ function Sampler(options) {
options = options ?? Frozen.EMPTY_OBJECT;

const {
wrapR = TextureWrap.CLAMP_TO_EDGE,
wrapS = TextureWrap.CLAMP_TO_EDGE,
wrapT = TextureWrap.CLAMP_TO_EDGE,
minificationFilter = TextureMinificationFilter.LINEAR,
Expand All @@ -21,6 +22,10 @@ function Sampler(options) {
} = options;

//>>includeStart('debug', pragmas.debug);
if (!TextureWrap.validate(wrapR)) {
throw new DeveloperError("Invalid sampler.wrapR.");
}

if (!TextureWrap.validate(wrapS)) {
throw new DeveloperError("Invalid sampler.wrapS.");
}
Expand All @@ -44,6 +49,7 @@ function Sampler(options) {
);
//>>includeEnd('debug');

this._wrapR = wrapR;
this._wrapS = wrapS;
this._wrapT = wrapT;
this._minificationFilter = minificationFilter;
Expand All @@ -52,6 +58,11 @@ function Sampler(options) {
}

Object.defineProperties(Sampler.prototype, {
wrapR: {
get: function () {
return this._wrapR;
},
},
wrapS: {
get: function () {
return this._wrapS;
Expand Down Expand Up @@ -84,6 +95,7 @@ Sampler.equals = function (left, right) {
left === right ||
(defined(left) &&
defined(right) &&
left._wrapR === right._wrapR &&
left._wrapS === right._wrapS &&
left._wrapT === right._wrapT &&
left._minificationFilter === right._minificationFilter &&
Expand All @@ -94,6 +106,7 @@ Sampler.equals = function (left, right) {

Sampler.NEAREST = Object.freeze(
new Sampler({
wrapR: TextureWrap.CLAMP_TO_EDGE,
wrapS: TextureWrap.CLAMP_TO_EDGE,
wrapT: TextureWrap.CLAMP_TO_EDGE,
minificationFilter: TextureMinificationFilter.NEAREST,
Expand Down
24 changes: 17 additions & 7 deletions packages/engine/Source/Renderer/Texture3D.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,30 @@ import Sampler from "./Sampler.js";
import TextureMagnificationFilter from "./TextureMagnificationFilter.js";
import TextureMinificationFilter from "./TextureMinificationFilter.js";

/**
* @typedef {object} Texture3D.Source
* @property {number} width The width (in pixels) of the 3D texture source data.
* @property {number} height The height (in pixels) of the 3D texture source data.
* @property {number} depth The depth (in pixels) of the 3D texture source data.
* @property {TypedArray|DataView} arrayBufferView The source data for a 3D texture. The type of each element needs to match the pixelDatatype.
Copy link
Contributor

Choose a reason for hiding this comment

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

The docs for texImage3D and texSubImage3D allow for these additional sources: ImageBitmap, ImageData, HTMLImageElement, HTMLCanvasElement, and HTMLVideoElement. Can we support those here? If so, we could generalize this type definition to Texture.Source and use it in Texture.js too!

Copy link
Contributor Author

@Hiwen Hiwen Jul 31, 2025

Choose a reason for hiding this comment

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

Using ImageBitmap, ImageData, HTMLImageElement, HTMLCanvasElement, and HTMLVideoElement as the source is a really good idea. However, I haven't actually used this yet, and I'm not sure about the specific effects. I think it would be better to open a new issue to support this.

* @property {TypedArray|DataView} [mipLevels] An array of mip level data. Each element in the array should be a TypedArray or DataView that matches the pixelDatatype.
*/

/**
* @typedef {object} Texture3D.ConstructorOptions
*
* @property {Context} context
* @property {object} [source] The source for texel values to be loaded into the texture3D.
* @property {Texture3D.Source} [source] The source for texel values to be loaded into the 3D texture.
* @property {PixelFormat} [pixelFormat=PixelFormat.RGBA] The format of each pixel, i.e., the number of components it has and what they represent.
* @property {PixelDatatype} [pixelDatatype=PixelDatatype.UNSIGNED_BYTE] The data type of each pixel.
* @property {boolean} [flipY=true] If true, the source values will be read as if the y-axis is inverted (y=0 at the top).
* @property {boolean} [skipColorSpaceConversion=false] If true, color space conversions will be skipped when reading the texel values.
* @property {Sampler} [sampler] Information about how to sample the texture3D.
* @property {number} [width] The pixel width of the texture3D. If not supplied, must be available from the source.
* @property {number} [height] The pixel height of the texture3D. If not supplied, must be available from the source.
* @property {number} [depth] The pixel depth of the texture3D. If not supplied, must be available from the source.
* @property {Sampler} [sampler] Information about how to sample the 3D texture.
* @property {number} [width] The width (in pixels) of the 3D texture. If not supplied, must be available from the source.
* @property {number} [height] The height (in pixels) of the 3D texture. If not supplied, must be available from the source.
* @property {number} [depth] The depth (in pixels) of the 3D texture. If not supplied, must be available from the source.
* @property {boolean} [preMultiplyAlpha] If true, the alpha channel will be multiplied into the other channels.
* @property {string} [id] A unique identifier for the texture3D. If this is not given, then a GUID will be created.
* @property {string} [id] A unique identifier for the 3D texture. If this is not given, then a GUID will be created.
*
* @private
*/
Expand Down Expand Up @@ -253,7 +262,7 @@ function Texture3D(options) {
* Load texel data from a buffer into a texture3D.
*
* @param {Texture3D} texture3D The texture3D to which texel values will be loaded.
* @param {object} source The source for texel values to be loaded into the texture3D.
* @param {Texture3D.Source} source The source for texel values to be loaded into the texture3D.
*
* @private
*/
Expand Down Expand Up @@ -503,6 +512,7 @@ function setupSampler(texture3D, sampler) {
gl.bindTexture(target, texture3D._texture);
gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, minificationFilter);
gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, magnificationFilter);
gl.texParameteri(target, gl.TEXTURE_WRAP_R, sampler.wrapR);
gl.texParameteri(target, gl.TEXTURE_WRAP_S, sampler.wrapS);
gl.texParameteri(target, gl.TEXTURE_WRAP_T, sampler.wrapT);
if (defined(texture3D._textureFilterAnisotropic)) {
Expand Down
9 changes: 9 additions & 0 deletions packages/engine/Specs/Renderer/SamplerSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe(

it("has expected default values", function () {
const sampler = new Sampler();
expect(sampler.wrapR).toEqual(TextureWrap.CLAMP_TO_EDGE);
expect(sampler.wrapS).toEqual(TextureWrap.CLAMP_TO_EDGE);
expect(sampler.wrapT).toEqual(TextureWrap.CLAMP_TO_EDGE);
expect(sampler.minificationFilter).toEqual(
Expand All @@ -32,6 +33,14 @@ describe(
expect(sampler.maximumAnisotropy).toEqual(1.0);
});

it("throws when creating a sampler with invalid wrapR", function () {
expect(function () {
return new Sampler({
wrapR: "invalid wrap",
});
}).toThrowDeveloperError();
});

it("throws when creating a sampler with invalid wrapS", function () {
expect(function () {
return new Sampler({
Expand Down