-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Voxels #10253
Conversation
Thanks for the pull request @IanLilleyT!
Reviewers, don't forget to make sure that:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@IanLilleyT Did a quick pass through the custom shader code. I think what you have looks sensible, though I did have some comments.
@@ -0,0 +1 @@ | |||
{"asset":{"version":"2.0"},"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0}],"meshes":[{"primitives":[{"mode":2147483648,"attributes":{"_a":0},"extensions":{"EXT_primitive_voxel":{"dimensions":[2,2,2]}}}]}],"extensionsUsed":["EXT_primitive_voxel","EXT_structural_metadata"],"extensionsRequired":["EXT_primitive_voxel","EXT_structural_metadata"],"extensions":{"EXT_structural_metadata":{"schemaUri":"../../../../schema.json","propertyAttributes":[{"class":"voxel","properties":{"a":{"attribute":"_a"}}}]}},"accessors":[{"bufferView":0,"type":"SCALAR","componentType":5126,"min":[0.0],"max":[1.0],"count":8}],"bufferViews":[{"buffer":0,"byteLength":32}],"buffers":[{"uri":"a.bin","byteLength":32}]} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for small samples like this, we tend to like to format the JSON, e.g. with this pretty printer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. I'm using https://rapidjson.org/classrapidjson_1_1_pretty_writer.html with 2 spaces for indentation and it looks very similar.
Apps/Sandcastle/gallery/Voxels.html
Outdated
@@ -0,0 +1,507 @@ | |||
<!DOCTYPE html> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this sandcastle is missing a thumbnail
getPrimitiveFunction: true, | ||
}); | ||
addProperty({ | ||
name: "debugDraw", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the debug draw option really kills performance on my laptop
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
me too 😢 - I used PolylineCollection
for thick lines but it's struggling to keep up with all the bounding boxes. I might have to render only the first N levels of the tree.
Source/Scene/VoxelPrimitive.js
Outdated
*/ | ||
VoxelPrimitive.DefaultCustomShader = new CustomShader({ | ||
// TODO what should happen when lightingModel undefined? | ||
// lightingModel: Cesium.LightingModel.UNLIT, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something tells me the best thing to do is to ignore the lighting model in general, always treat as unlit
(unless voxels have well defined normals for lighting?)
Source/Scene/VoxelPrimitive.js
Outdated
this._traversal = undefined; | ||
} | ||
|
||
// TODO: I assume the custom shader does not have to be destroyed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, at least for now. If you look at the constructor for CustomShader
it mentions that the caller is responsible for cleaning up resources.
That said, we might change to a reference-count model, see #10250 (comment) (which might result in a separate issue)
Source/Scene/VoxelPrimitive.js
Outdated
shaderBuilder.addStructField(voxelStructId, "bool", `${name}NormalValid`); | ||
} | ||
|
||
shaderBuilder.addStructField(voxelStructId, "vec3", "positionEC"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the Model equivalent also allows positionMC
and positionWC
(albeit the latter is not super useful unless we can give it more precision somehow...). Though voxels could be different.
Definitely check the https://github.com/CesiumGS/cesium/tree/main/Documentation/CustomShaderGuide. Maybe voxels need their own section for differences.
Source/Scene/VoxelPrimitive.js
Outdated
|
||
shaderBuilder.addUniform( | ||
"sampler2D", | ||
"u_megatextureTextures[METADATA_COUNT]", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I haven't come across it yet in this PR, but it would be good to document what exactly you mean by "megatexture" somewhere, I don't know the memory layout or type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Megatexture is handled in VoxelTraversal but I don't think it's documented very well. In short, it's a large texture that stores multiple tiles of voxel data and pages tiles in and out as the camera moves.
Source/Scene/VoxelPrimitive.js
Outdated
const componentType = componentTypes[i]; | ||
const glslType = getGlslType(type, componentType); | ||
shaderBuilder.addFunctionLines(clearAttributesFunctionId, [ | ||
`attributes.${name} = ${glslType}(0.0);`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this works because attributes are only ever float/vecN/matN
, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. In the short term voxels only works with floatN metadata. Once it uses all the metadata capabilities it will need to be more tightly integrated with CustomShaderPipelineStage / ModelExperimental systems
Specs/Data/Cesium3DTiles/Voxel/VoxelMultiAttribute3DTiles/README.md
Outdated
Show resolved
Hide resolved
Thanks @IanLilleyT, @lilleyse, @ptrgags, and @jjhembd! Great to get an initial implementation in! While we'll still need to finalize the format, the API, and the sandcastle examples, this is an exciting first step and a lot of progress has been made! |
Opening a draft PR for early feedback. There's still more code to write and I'll be filling in a lot more details as the
daymonth goes on.Primitive
A voxel primitive gets data from a voxel provider and renders it in the scene. It has a lot in common with other primitives in CesiumJS in that it is updated every frame based on camera position and submits draw commands. Multiple voxel primitives can get data from the same voxel provider, allowing for different ways of rendering the same underlying datasets.
Basic
A voxel primitive with no options. It will be located at the center of the earth. The default model matrix is identity, the default provider is a 1x1x1 voxel grid, and the default custom shader is white.
Full example with local sandcastle
Model Matrix
Use the
modelMatrix
option to put the voxel primitive above the earth's surface.Full example with local sandcastle
Custom Shader
Voxels can be shaded in many different ways. In this example it draws the color of the box's coordinate system. X = red, Y = green, Z = blue.
Full example with local sandcastle
Custom Provider
This example uses a custom voxel provider to create procedural data. The
color
metadata has an alpha component that makes it look like an octant of a sphere, even though the voxel shape is a box. Note how the edges are mushy, this is because the voxel grid is only 32x32x32 voxels. A larger voxel grid would create a crisper shape.Full example with local sandcastle
Provider
To do
glTF
To do
3D Tiles
To do
Procedural
To do
Traversal
To do
Shapes
Here's a video showing the different kinds of shapes supported by the voxel system. The data is one procedurally generated tile that is completely opaque (to make it easier to see the surface). If something looks like a bug, it probably is. There are several edge cases that still need to be fixed. Also please ignore the bad framerate. It's smoother in person.
Peek.2022-04-14.20-17.mp4
Currently it supports box, ellipsoid, and cylinder shapes - and each of them can be stretched and smooshed in a number of ways, sometimes becoming 2D surfaces. Out of all the shapes, box has the best performance because the shader doesn't have to do as many coordinate conversions, but it might not be the most natural choice for all kinds of data. For example, ocean temperature data is often gridded by latitude, longitude, and depth, so the most accurate fit for the data would be the ellipsoid shape. In some cases it may make sense to resample the data to fit another shape, but that's a decision left to the user and their data pipeline.
In the future there could be other kinds of shapes such as cone or torus. All that's needed is some sort of 3D coordinate system that can be gridded into voxels.
Each shape inherits from
VoxelShape.js
and is responsible for computing:Coordinate systems
There a lot of different coordinate systems and "spaces" throughout the code. Still working on making this terminology consistent everywhere:
(0,0,0)
is the bottom corner and(1,1,1)
is the top corner. This is where all the raymarching happens as it maps very closely to texture coordinates when sampling voxel data for the box shape and has decent floating point precision.(-1,-1,-1)
to(+1,+1,+1)
.(-1,-1,-1)
to(+1,+1,+1)
. For ellipsoid it would be(-pi, -halfPi, -inf)
to(+pi, +halfPi, +inf)
. For cylinder it would be(0,-1,-pi)
to(1,+1,+pi)
. UV space is converted to shape space at every step in the raymarch, and can be costly. For example, the ellipsoid shape needs to do an iterative ellipse/ellipsoid distance.(0,0,0)
to(1,1,1)
range based on the shape's bounds. For example, if an ellipsoid shape's longitude goes from10 degrees
to20 degrees
,10 degrees
will map to0.0
,15 degrees
will map to0.5
,20 degrees
will map to1.0
, etc.(0.5,0.5,0.5)
to(voxelDimX-0.5, voxelDimY-0.5, voxelDimZ-0.5)
to avoid accidentally reading neighboring data in the megatexture when linear texture sampling is on. This space also handles padding (more explanation needed for padding...).texture3d(megatexture, texcoord)
. The 2D megatexture encoding (for WebGL 1) is a bit more complicated because it needs to mimic the 3d linear sampling in software.Shape Bounds and Clip Bounds
shapeMinBounds
andshapeMaxBounds
controls where voxel data exists in the shape's coordinate system. For example, to create an ellipsoid shape that covers the top half of the globe, setshapeMinBounds.y
to0.0
andshapeMaxBounds.y
tohalfPi
.clipMinBounds
andclipMaxBounds
is similar but controls where voxel data is rendered.Here's a video showing both options:
clipping.mp4
Shader
Performance
Inspector
To-do:
VoxelPrimitive
Thanks to @ErixenCruz, @krupkad, @lilleyse, and many others for their contributions, ideas, and feedback.