Skip to content

Support Texture3D and add Volume Cloud SandBox Sample #12611

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
May 21, 2025

Conversation

Image for: Conversation
Copy link
Contributor

Hiwen commented May 9, 2025

Description

I extended the support for 3D Texture and ported the volumetric cloud example from Three.js to the sandbox. 3D Texture is widely used in volume rendering. With the support for 3D Texture, it will provide a basic support for this type of work.

Issue number and link

Image for: Issue number and link

#12550

Testing plan

Image for: Testing plan

I added a sandbox example of volumetric clouds and improved the unit tests.
How to view sandbox examples:
Run npm run release
Run npm start
Navigate to http://localhost:8080/Apps/Sandcastle/index.html?src=development%2FVolumeCloud.html

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
Copy link

github-actions bot commented May 9, 2025

Thank you for the pull request, @Hiwen!

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

Copy link
Contributor

ggetz commented May 9, 2025

Looks awesome @Hiwen!

I noticed this pull request is marked as a draft. Is it ready for review?

Copy link
Contributor Author

Hiwen commented May 9, 2025

Looks awesome @Hiwen!

I noticed this pull request is marked as a draft. Is it ready for review?

It's almost ready. After I transplanted the volumetric clouds from Three.js, I noticed some differences in color, and I want to improve it further. But so far, no breakthrough has been found. Maybe we can improve it together.

Hiwen marked this pull request as ready for review May 9, 2025 14:57
Copy link
Contributor Author

Hiwen commented May 10, 2025

@ggetz I solved the Color Problem, and added params control UI. It's ready for review.

Copy link
Contributor

ggetz commented May 12, 2025

Looks great @Hiwen!

@jjhembd Could you please review this PR?

Copy link
Contributor

jjhembd left a comment

Choose a reason for hiding this comment

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

Thanks @Hiwen, it's very exciting to start supporting 3D textures!

The Sandcastle works well for me. I took a first pass through the code and left some comments. Please let me know if my feedback makes sense.


it("PixelDatatype.FLOAT correspond to WebGLConstants.RGBA32F", function () {
if (!context.webgl2) {
console.warn("Skipping tests for WebGL1.");
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think these warnings are needed (also in Texture3DSpec). See BufferSpec, MultiSampleFramebufferSpec, etc. We can silently skip the test if WebGL2 is not supported.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That makes sense. It's done.

* @param {number} t - The interpolation factor in the closed interval `[0, 1]`.
* @return {number} The interpolated value.
*/
function lerp(x, y, t) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we simply use Cesium.Math.lerp instead? That way the person reading the code can get to the important part sooner.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's done.

"use strict";
//Sandcastle_Begin

//ImprovedNoise from Three.js
Copy link
Contributor

Choose a reason for hiding this comment

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

How much of this example is from Three.js? We want to make sure to credit them for the idea, and for whatever else comes from their repo. Perhaps we can add a link to the Three.js example in a comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's necessary! And it's done.

@@ -34,4 +46,97 @@ describe("Core/PixelFormat", function () {
);
expect(flipped).toBe(dataBuffer);
});

it("PixelDatatype.FLOAT correspond to WebGLConstants.RGBA32F", function () {
Copy link
Contributor

Choose a reason for hiding this comment

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

This spec is checking more than just RGBA32F. I think it makes sense to check them all in one block. Maybe we can edit the description to make it more clear. Something like:

"Returns the correct internal formats for PixelDatatype.FLOAT"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's much better! And it's done.

console.warn("Skipping tests for WebGL1.");
return;
}
const interFormatR8 = PixelFormat.toInternalFormat(
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a nitpick: the Coding Guide recommends to avoid abbreviations. Also this one is 32-bit. Would internalFormatR32 be a better name here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I fixed this.

console.warn("texture3D.flipY is not supported.");
}

// gl.texStorage3D( textureTarget, 1, 33321, width, height, depth );
Copy link
Contributor

Choose a reason for hiding this comment

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

I see that texStorage3D is preferred. We didn't use texStorage2D because we wanted everything to work in WebGL1. But Texture3D assumes WebGL2 is supported. Can we switch to texStorage3D here?

Copy link
Contributor Author

Hiwen May 14, 2025

Choose a reason for hiding this comment

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

I noticed that Three.js writes data through gl.texStorage3D. I didn't know why at that time, but now I understand!
Switch to texStorage3D is an excellent idea, and I did it.

mipWidth = nextMipSize(mipWidth);
mipHeight = nextMipSize(mipHeight);
mipDepth = nextMipSize(mipDepth);
gl.texImage2D(
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be texImage3D? Or switch to texStorage3D (see previous comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. I fixed it.

sizeInBytes: {
get: function () {
if (this._hasMipmap) {
return Math.floor((this._sizeInBytes * 4) / 3);
Copy link
Contributor

Choose a reason for hiding this comment

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

For 3D, the formula should be this._sizeInBytes * 8 / 7.
It's a geometric series, where the ratio is the relative size of the next mipmap: 1/4 for 2D, 1/8 for 3D.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice catch! I didn't notice this problem before.

}

// WebGL 2 depth texture3D only support nearest filtering. See section 3.8.13 OpenGL ES 3 spec
if (context.webgl2) {
Copy link
Contributor

Choose a reason for hiding this comment

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

context.webgl2 is always true for a 3D texture, so we don't need to check it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's right.

);
}

if (this._width > 1 && !CesiumMath.isPowerOfTwo(this._width)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since this class assumes WebGL2, power-of-two sizes are not required. I think we can skip all 3 of these checks.

Copy link
Contributor Author

Hiwen May 14, 2025

Choose a reason for hiding this comment

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

Yes, power-of-two sizes are not mandatory to use WebGL2.
But I think we can keep a warning to warn developers that power-of-two values have better performance.
At the same time, avoid the situation where generateMipmap may truncate some mip levels.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do power-of-two sizes have better performance? I haven't been able to find a reference for this. Machines that support WebGL2 should be able to handle these sizes at the hardware level.

If we have evidence that there will be a performance impact, then

  • We should add a warning for 2D textures too--Texture.js currently doesn't warn anything as long as the context is WebGL2.
  • It would be better to use oneTimeWarning to alert the user once, without filling up the console with too many duplicate messages.
Copy link
Contributor Author

Hiwen commented May 14, 2025

@jjhembd Thank you so much for your meticulous review! The suggestions you put forward are both professional and practical, and I have benefited a great deal from them. To be honest, I didn't realize there were so many areas that needed improvement before. These valuable feedbacks are of great value to me. Once I have arranged my time, I will definitely implement and make the modifications item by item. Thank you sincerely again for your guidance and assistance!

Copy link
Contributor Author

Hiwen commented May 14, 2025

@jjhembd Thanks for reviewing and I fixed all the issues you raised. I hope you can review it again when you have time. This is really helpful to me.

Hiwen requested a review from jjhembd May 14, 2025 14:02
Copy link
Contributor

jjhembd left a comment

Choose a reason for hiding this comment

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

Thanks @Hiwen for these updates! I think this is really close. I pushed an update with some minor formatting tweaks.

I just have one remaining question about power of two sizes.

);
}

if (this._width > 1 && !CesiumMath.isPowerOfTwo(this._width)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do power-of-two sizes have better performance? I haven't been able to find a reference for this. Machines that support WebGL2 should be able to handle these sizes at the hardware level.

If we have evidence that there will be a performance impact, then

  • We should add a warning for 2D textures too--Texture.js currently doesn't warn anything as long as the context is WebGL2.
  • It would be better to use oneTimeWarning to alert the user once, without filling up the console with too many duplicate messages.
Copy link
Contributor Author

Hiwen commented May 20, 2025

@jjhembd Thanks for review.
About power-of-two sizes, I couldn't find this requirement in the official documentation, but AI suggests keeping the dimensions as powers of 2 to avoid some levels being clipped downwards.

Maybe I'm overthinking it. Currently, there is no direct evidence indicating that a non-power-of-two value will have any negative impact on the rendering effect. Let's remove these few unclear warnings.

But I did a test. In GLSL, I used textureSize to read the texture sizes of different Mipmap levels, output them to the screen through out_FragColor, and then used a screenshot software(Snipaste) to read the color of the pixels to indirectly read the texture sizes.
The test results are as follows:
For 128 x 128 x 128 3D Texture:
level size
0 128 x 128 x 128
1 64 x 64 x 64
3 32 x 32 x 32
4 16 x 16 x 16
5 8 x 8 x 8
6 4 x 4 x 4
7 2 x 2 x 2
8 1 x 1 x 1
9 1 x 1 x 1

And for 100 x 100 x 100 3D Texture
level size
0 100 x 100 x 100
1 50 x 50 x 50
2 25 x 25 x 25
3 12 x 12 x 12

4 6 x 6 x 6
5 3 x 3 x 3
6 1 x 1 x 1

7 1 x 1 x 1
Judging from the second test results, from level 2 to level 3, the texture size changed from 25 to 12. This is not a reduction by exactly half in the strict sense. So, when generating Mipmaps, it is better to keep the length, width, and depth of the 3D Texture as powers of 2. Only in this way can we ensure that all levels are exactly halved, avoiding an error of 0.5 pixels.

However, I'm still not sure what negative impacts this will have on rendering. Maybe there will be a slight jolt when switching levels?

Hiwen requested a review from jjhembd May 21, 2025 13:40
Copy link
Contributor

jjhembd left a comment

Choose a reason for hiding this comment

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

Thanks @Hiwen for the explanation! I see what you mean about truncation of mipmaps. I think in this case, it makes sense to leave the implementation details up to the browser or the user's operating system.

The authors of the WebGL2 spec apparently thought arbitrary dimensions wouldn't be a problem. As mentioned on a related thread for a different spec, if a GPU manufacturer was concerned about performance, they probably would have objected to the spec change. But they all agreed with the change, so any problems must have been already taken care of on the hardware or driver.

Thanks again for all your work on this! It's a big step forward for the code. I'm looking forward to further improvements we can implement, building on this new Texture3D class.

jjhembd added this pull request to the merge queue May 21, 2025
Merged via the queue into CesiumGS:main with commit 2640bd8 May 21, 2025
4 checks passed
Copy link
Contributor Author

Hiwen commented May 21, 2025

Thanks again @jjhembd. This PR was a wonderful journey for me.

Hiwen deleted the Texture3D branch May 22, 2025 04:41
Copy link
Contributor

mickae1 commented Jun 3, 2025

thx for the work, can you put the sandcastle into :

https://sandcastle.cesium.com/

i don't see volume cloud example

Copy link
Contributor

javagl commented Jun 3, 2025

It is in the "Development" section, which is not shown by default (not even when "All" is selected). You can select it manually, via
https://sandcastle.cesium.com/index.html?src=development%2FVolumeCloud.html

(It should/could probably be moved to a different, more visible section in the next release. That cloud looks pretty cool...)

Copy link
Contributor Author

Hiwen commented Jun 3, 2025

It is in the "Development" section, which is not shown by default (not even when "All" is selected). You can select it manually, via https://sandcastle.cesium.com/index.html?src=development%2FVolumeCloud.html

(It should/could probably be moved to a different, more visible section in the next release. That cloud looks pretty cool...)

I will do it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
5 participants