Skip to content

Conversation

mickae1
Copy link
Contributor

@mickae1 mickae1 commented Aug 20, 2025

to test :
test.zip

you can find here the sandcastle

Copy link

Thank you for the pull request, @mickae1!

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

@mickae1
Copy link
Contributor Author

mickae1 commented Aug 21, 2025

@lukemckinstry @ggetz do you think this pull request can be accepted for the next cesium release ? My teams need it.

Thank you,

@mzschwartz5
Copy link
Contributor

Hi @mickae1! Thanks for opening a PR and contributing this feature. I have a couple questions to kick off my review:

  1. Is there a GH issue or forum post that discusses this feature? If so, can you link it so we maintain the context of this feature for posterity? If not, it might be a good idea to open an issue or write a forum post to get feedback / direction.
  2. One thing I'd want to discuss in such an issue is the best way to implement this feature that fits in with the Cesium's design. Right now, you're using a compiler macro to ignore Cesium's built-in geometry stage function when a flag is set. I think a more natural way to do this would be to pass in and override with your new geometry stage here. That way, the engine is never potentially in a situation where there's no geometry stage function (if, say, you forget to supply one). It's guaranteed to have at least the default.

But beyond the actual implementation, I'm curious what it is you're trying to achieve with this new feature - maybe we already support what you want to do some other way! (Again, why it's often helpful to start with or link a forum post).

I'll assign myself as a reviewer so I can get notified when you reply!

@mzschwartz5 mzschwartz5 self-assigned this Aug 25, 2025
@@ -166,6 +166,7 @@ import ModelImagery from "./ModelImagery.js";
* @privateParam {boolean} [options.showCreditsOnScreen=false] Whether to display the credits of this model on screen.
* @privateParam {SplitDirection} [options.splitDirection=SplitDirection.NONE] The {@link SplitDirection} split to apply to this model.
* @privateParam {boolean} [options.projectTo2D=false] Whether to accurately project the model's positions in 2D. If this is true, the model will be projected accurately to 2D, but it will use more memory to do so. If this is false, the model will use less memory and will still render in 2D / CV mode, but its positions may be inaccurate. This disables minimumPixelSize and prevents future modification to the model matrix. This also cannot be set after the model has loaded.
* @privateParam {boolean} [options.customGeometryStage=false] Whether to override the default geometry stage, to let the user define it's own geometry stage.
Copy link
Contributor

Choose a reason for hiding this comment

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

Small nit- it's -> its.

But, per my overall comment, I think it might make more sense to do something like pass in the geometry stage function snippet itself rather than a flag.

@mzschwartz5
Copy link
Contributor

Oh, also - if you haven't already, can you add yourself to CONTRIBUTING.md and add your feature to CHANGES.md?

@mickae1
Copy link
Contributor Author

mickae1 commented Aug 25, 2025

Hi @mickae1! Thanks for opening a PR and contributing this feature. I have a couple questions to kick off my review:

  1. Is there a GH issue or forum post that discusses this feature? If so, can you link it so we maintain the context of this feature for posterity? If not, it might be a good idea to open an issue or write a forum post to get feedback / direction.
  2. One thing I'd want to discuss in such an issue is the best way to implement this feature that fits in with the Cesium's design. Right now, you're using a compiler macro to ignore Cesium's built-in geometry stage function when a flag is set. I think a more natural way to do this would be to pass in and override with your new geometry stage here. That way, the engine is never potentially in a situation where there's no geometry stage function (if, say, you forget to supply one). It's guaranteed to have at least the default.

But beyond the actual implementation, I'm curious what it is you're trying to achieve with this new feature - maybe we already support what you want to do some other way! (Again, why it's often helpful to start with or link a forum post).

I'll assign myself as a reviewer so I can get notified when you reply!

you can find here the issue :

#12815

@mickae1
Copy link
Contributor Author

mickae1 commented Aug 26, 2025

for my model loaded from glb, i want to do the projection change in vertex shader when I'm in 2D/2.5D . ( i don't want any cpu computing the 2D projection )

the code works, the projection works.

you can find here the sandcastle

please tell me what I need to do to validate this pull request

@mickae1 mickae1 changed the title add the option customGeometryStage to let the user customize the geometry stage function in the vertex shader add the option customGeometryStageVS to let the user customize the geometry stage function in the vertex shader Aug 26, 2025
@mickae1
Copy link
Contributor Author

mickae1 commented Aug 27, 2025

I've made modifications, to start a new approach, more generic to every one, please give advice .

the updated sandcastle

@mickae1 mickae1 changed the title add the option customGeometryStageVS to let the user customize the geometry stage function in the vertex shader support 2.5D/2D projection by default when the option projectTo2D is not set Aug 27, 2025
…not set

add the options.customGeometryStageVS Whether to override the default VS geometry stage, to let the user define its own VS geometry stage.
return vec3(altitude, plate_x, plate_y);
}

vec3 cartesianToCartographic(vec3 cartesian) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm still processing the math here, but wondering if something here might be helpful (or if we can even just use these functions directly). It looks conceptually similar.

If we can use the above, I wonder if isSphere is actually a fast path, given the expense of trig functions on the GPU. Might be worth doing some performance measurements there to see. Maybe it's faster to just treat everything as an ellipsoid and use that non-trig iterative method.


#if defined(USE_2D_POSITIONS) || defined(USE_2D_INSTANCING)
vec3 position2D = attributes.position2D;
vec3 positionEC = (u_modelView2D * vec4(position2D, 1.0)).xyz;
computedPosition = czm_projection * vec4(positionEC, 1.0);
#else
computedPosition = czm_projection * vec4(v_positionEC, 1.0);
if(czm_morphTime != 1.){
Copy link
Contributor

Choose a reason for hiding this comment

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

Small nit- given that, the vast majority of the time, we operate in 3D mode, it would be slightly faster to invert this check so that it short-circuits more often.

Copy link
Contributor

Choose a reason for hiding this comment

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

(Looking back at this, I see that this is within the USE_2D_POSITIONS macro, so is it already always 2D mode? If we remove the CPU-side projection implementation, per my overall-review comment, then we would remove this macro too, I think, and we'd need the czm_morphTime check you added.)

vec3 cartographic = cartesianToCartographic(positionMC);
vec3 position = projectToGeographic(cartographic);

vec3 positionEC = (czm_view * vec4(position, 1.0)).xyz;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we still need to write out to v_positionEC in this branch of the if-statement?

Copy link
Contributor Author

@mickae1 mickae1 Aug 29, 2025

Choose a reason for hiding this comment

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

i don't know, If it is used in other pipeline, or used in fragment shader.

Copy link
Contributor

Choose a reason for hiding this comment

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

The prefix v_ typically means it's a vertex output attribute - so it would get used in the fragment shader. In this case, you can see that here.


#if defined(USE_2D_POSITIONS) || defined(USE_2D_INSTANCING)
vec3 position2D = attributes.position2D;
vec3 positionEC = (u_modelView2D * vec4(position2D, 1.0)).xyz;
computedPosition = czm_projection * vec4(positionEC, 1.0);
#else
computedPosition = czm_projection * vec4(v_positionEC, 1.0);
if(czm_morphTime != 1.){
vec3 cartographic = cartesianToCartographic(positionMC);
Copy link
Contributor

Choose a reason for hiding this comment

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

(This probably reveals some misunderstanding I have but) - why does this operate on the model space position instead of world or eye space?

@mzschwartz5
Copy link
Contributor

Overall comment - I have yet to test the changes, will do soon. Just did an overview for the moment. I think this could be a really nice contribution - if it works as promised without any regressions, we should go a step further and remove the CPU implementation of the projection. This would be the default, and we could skip the added options to the geometry pipeline stage to override the implementation.

@mickae1
Copy link
Contributor Author

mickae1 commented Sep 2, 2025

@mzschwartz5 what i'm doing in the vertex shader, is exactly what is done in the cpu :

GeometryPipeline.projectTo2D = function (

with :

 const lonLat = ellipsoid.cartesianToCartographic(
      value,
      scratchProjectTo2DCartographic,
    );
    //>>includeStart('debug', pragmas.debug);
    if (!defined(lonLat)) {
      throw new DeveloperError(
        `Could not project point (${value.x}, ${value.y}, ${value.z}) to 2D.`,
      );
    }
    //>>includeEnd('debug');

    const projectedLonLat = projection.project(
      lonLat,
      scratchProjectTo2DCartesian3,
    );

I found out why, it need to be vec3(z,x,y ) to be working : ( and not vec3(x,y,z) ) :

because it's what you are doing in other shader :

p = czm_translateRelativeToEye(position2DHigh.zxy, position2DLow.zxy);

but not here, why ?

vec3 positionEC = (u_modelView2D * vec4(position2D, 1.0)).xyz;

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

Successfully merging this pull request may close these issues.

2 participants