-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Support for model outlining #4314
Conversation
… 3D Models.html back to the original
Wow, the demo looks awesome @jasonbeverage! Thanks so much for submitting this. Do we have a contributor license agreement (CLA) from you? If not, can you please send one over so we can review this? You can find instructions in CONTRIBUTING.md |
Yup you should have one on file, I've had other PR's merged. Thanks! On Mon, Sep 12, 2016 at 4:11 PM Hannah notifications@github.com wrote:
|
Great, thanks @jasonbeverage! You have some jsHint errors. And I think we'll probably want some unit tests for this. See the pull request guidelines in our contributing documentation. We're a little busy around here this week and we have some people on vacation, so we might not be able to review this right away. I think this would be a really useful feature though, and I'd love to see it merged soon =) |
Just wanted to say that this is awesome. I was talking offline with @bagnell and he thought the same technique could be extended to other primitives (having a generic way to highlight all primitives is definitely something we've wanted for a long time). Our selection icon is good for HTML, but having a highlight that scales with the object is better. While I don't expect it to be part of this PR, having a new issue to track adding similar capabilities to geometry/labels/billboards/points would be great. While I'm not qualified to review and merge this, from a functionality standpoint my one comment would be that we map the width parameter to use screen space units so that it's easier for users to understand. Thanks @jasonbeverage. |
Glad you like it @mramato :) It seems like it would be easy to extend to other graphics like labels and billboards, if we did that perhaps it would make sense to stick the highlight properties on the entity object itself instead of on the model, billboard, etc. I agree on the screen space mapping instead of the weird clip space setting I have now, let me see what I can do. |
I've pushed some changes that should fix the jsHint issues and also changed the highlightSize to be in pixels instead of clipspace. I have a question about how the uniforms work in Cesium though. I added a new function called getUniformNameForSemantic that tries to get a uniform from GLTF based on the semantic. For the projection matrix I had no fallback to czm_projection if I couldn't find the projection matrix, but when I tried to get the VIEWPORT semantic I was getting no uniform name back (I assume b/c the models don't use VIEWPORT in their shaders) so I fell back to czm_viewport. If the uniform wasn't found I figured I needed to define it in the shader b/c it wasn't used, but when I tried to append it in createHighlightVertexShaderSource using something like this: However, doing that I get get a czm_viewport already defined shader compiler error even though czm_viewport actually isn't defined in the model's shader. I can apparently use czm_viewport just fine without defining it in my shader, is the model's shader eventually merged with some uber shader down the line? Thanks! |
Yup, before the shader is finalized it checks for any of the cesium built-in uniforms like |
Oh great, so that means that I actually don't need to get the semantic name for projection and viewport, I can just use czm_projection and czm_viewport freely right? That would simplify the shader generation code. |
Yeah that should be doable. |
Great, I just pushed a change doing that and it worked fine. Thanks! |
@@ -51,6 +51,9 @@ define([ | |||
* @param {Property} [options.receiveShadows=true] Deprecated, use options.shadows instead. A boolean Property specifying whether the model receives shadows from shadow casters in the scene. | |||
* @param {Property} [options.shadows=ShadowMode.ENABLED] An enum Property specifying whether the model casts or receives shadows from each light source. | |||
* @param {Property} [options.heightReference=HeightReference.NONE] A Property specifying what the height is relative to. | |||
* @param {Property} [options.highlight=false] Whether to highlight the model using an outline | |||
* @param {Property} [options.highlightColor=Color(1.0, 0.0, 0.0, 1.0)] The highlight color for the outline. |
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.
Can we just default this to the default constructed color?
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.
Done.
* | ||
* @type {Cartesian4} | ||
* | ||
* @default Color(1.0, 0.0, 0.0, 1.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.
Same comment.
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.
Done.
/** | ||
* The highlight color | ||
* | ||
* @type {Cartesian4} |
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 is Color
.
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.
Done.
*/ | ||
this.highlightSize = 0.002; | ||
|
||
|
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.
Minor, but extra whitespace.
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.
Done.
uri : url, | ||
minimumPixelSize : 128, | ||
maximumScale : 20000, | ||
highlight: viewModel.highlight, |
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.
Default this to true
so the example is obvious as soon as it loads.
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.
Done
uniformMap.u_highlightColor = createHighlightColorFunction(model); | ||
uniformMap.u_highlightSize = createHighlightSizeFunction(model); | ||
|
||
var highlightCommand = new DrawCommand({ |
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.
Can we create these commands and related resources on-demand the first time highlight
is true
?
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.
I tried to mimic what was done with the pickCommands, so kept the highlight command and resource creation close to where the pick commands are generated. If you don't mind I'll defer this to you to move around so I don't break anything :)
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.
@lilleyse let me know if you have any questions on this one; I prefer creating these on-demand since they may not be used in many apps, and users should only pay for what they use when it's reasonable for us to implement.
pass : isTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE | ||
}); | ||
|
||
|
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.
Extra whitespace.
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.
Done
pickCommand2D : pickCommand2D, | ||
highlightCommand: highlightCommand, | ||
highlightCommand2D: highlightCommand2D | ||
|
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.
Whitespace.
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.
done
Matrix4.clone(command.modelMatrix, highlightCommand.modelMatrix); | ||
BoundingSphere.clone(command.boundingVolume, highlightCommand.boundingVolume); | ||
|
||
|
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.
Whitespace.
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.
done.
owner : owner, | ||
pass : isTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE | ||
}); | ||
|
||
// Setup the stencil command for the highlight | ||
var highlightRS = clone(rs); |
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.
Shouldn't the blend state for this depend on the alpha value of hightlightColor
? We might also be able to get always with just always using alpha blending for this pass.
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.
Good catch, fixed and updated the Sandcastle demo to have an opacity slider, it looks nice with a partially transparent outline.
@jasonbeverage cool stuff, I'm sure users will appreciate this and will get a kick out of animating the highlight size and color/translucency! Comments:
Let me know if you have any questions on my code comments. They were all minor except the lazy creation of renderer resources, which will also be easy, but is important so users don't pay for things they don't use. Also, we planned to implement this with an upcoming post-processing framework; the screen-space technique will likely be much faster, especially given the typical CPU bottlenecks in Cesium apps, but I don't know how the quality will compare; I suspect it will be good. No concern now, just a heads up that we might significantly change this later...and you are more than welcome to help! |
Thanks for the feedback Patrick, I'll update the PR here soon and try to address your comments. Can you point me to some example unit tests that be applicable in this situation that I could try to draw inspiration from? |
For unit tests, we often rendering into a 1x1 viewport and check the color; however, even this is not super reliable across browsers, drivers, etc. so we have been doing more tests that check what commands were created (in this case, for example, you could validate the stencil render state). For simple property set/get, see https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Specs/Scene/ModelSpec.js#L214-L233 For command testing, see https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Specs/Scene/PrimitiveSpec.js#L447-L467 BTW, we will likely change this to use "derived commands" so that the renderer can automatically generate highlight commands for any type of primitive instead of require each primitive to have custom code to create these commands. It totally isn't required for this PR, but if it is something you are interested in, @bagnell and I could advise. |
This is updated now. There were too many special cases with the stencil-clearing approach so I decided to instead go with using the unique stencil value per model approach. 256 models can be outlined without any artifacts, but once it goes over there would be some minor stencil artifacts if two models with the same stencil id happen to overlap. I think this is a rare situation and a reasonable trade-off. Another plus is there are at most 2 render passes, whereas the other approach often required three if translucency was involved. This PR should work fine when OIT is supported. When it's not, the sorting causes stencil commands to be rendered in arbitrary order. A possible solution here is to create a derived command that combines multiple commands and always renders them together. All the model commands would be rendered sequentially, followed by all the stencil commands. Does this approach sound ok? So while this PR is not complete yet it's worth a look. |
Another idea for the non-OIT situation: have a separate render pass right after translucency. The stencil buffer will be completely ready. The downside is translucent silhouettes will not be rendered in the correct sorted order. |
Given that this is a short/medium-term solution for silhouettes that will be replaced by a screen-space approach, I would not do this architecture change. |
Agreed, can you please concisely document in, e.g., if more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts. The is more a note to us than end users, but it is probably better in the reference doc than as a comment. |
Do we clear stencil after the Also, how will this play when we are soon able to render models / 3D Tiles tilesets in the |
*/ | ||
stencilBuffer : { | ||
get : function() { | ||
return this._stencilBits >= 8; |
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.
I've never seen less than 8 (or anything other than 8), but are you sure this is the right test?
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.
When the context is created without stencil : true
, gl.getParameter(gl.STENCIL_BITS);
returns 0 so this test should be fine.
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.
I get that it is fine in practice (we think), but are you sure that > 0
isn't more correct according to the spec?
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.
According to the spec 8 bits is the minimum: https://www.khronos.org/registry/webgl/specs/latest/1.0/#2.2
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.
OK, thanks for checking.
@@ -589,7 +589,7 @@ define([ | |||
// Section 6.8 of the WebGL spec requires the reference and masks to be the same for | |||
// front- and back-face tests. This call prevents invalid operation errors when calling | |||
// stencilFuncSeparate on Firefox. Perhaps they should delay validation to avoid requiring this. | |||
gl.stencilFunc(stencilTest.frontFunction, stencilTest.reference, stencilTest.mask); | |||
gl.stencilFunc(frontFunction, reference, mask); |
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.
Good eye.
return (Math.floor(currAlpha) !== Math.floor(prevAlpha)) || (Math.ceil(currAlpha) !== Math.ceil(prevAlpha)); | ||
} | ||
|
||
// TODO : alternatively increment silhouette length as part of frame state and edit the model render states on the fly |
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.
What's the plan here?
If we are able to use derived commands to easily add silhouettes to other primitives, we could move this; otherwise, I think it is OK here.
@lilleyse can you move the tasklist, #4314 (comment), to the top comment (so it is easier to find) and update it? |
Yes, it does. |
Just to be sure, do you mean the GLOBE pass? Either way it will be problematic... Edit: Though if the silhouettes are opaque then it is doable to clear the stencil after the GLOBE pass. Translucent silhouettes won't be easily possible. |
Yes, GLOBE.
Couldn't the GLOBE pass have OPAQUE and TRANSLUCENT passes with proper stencil clearing? Or is OIT the issue? |
Ok yeah, if the globe pass has opaque and translucent passes in it this should all be fine. |
I should have this ready later tonight after testing on my other machine. |
e89915d
to
077e225
Compare
The tests are in. I'm still figuring out how to improve the silhouettes in 2D. |
@lilleyse let merge this. If silhouettes in 2D/CV get widely used, we can improve them. Likewise, this can also use derived commands when ready. |
Thanks again for kicking this off, @jasonbeverage. BIG contribution! |
@shunter do you, by chance, want to do the CZML updates here? |
Hi all,
I've done a bit of work adding support to Cesium to provide a halo effect for outlining models when they are selected (or any other reason you want to highlight them).
I've included a new sandcastle example that you can try here:
https://cesiumdemos.netlify.com/apps/sandcastle/?src=development/3D%20Models%20Outline.html&label=Development
Most of the modifications were to Model.js. It provides a two pass rendering approach using the stencil buffer, where the model is drawn normally first, filling the stencil buffer, then the model is drawn again using a shader that extrudes the normals and only renders where the original model didn't render.
This is my first time digging around the guts of the model rendering code in Cesium, so I'd appreciate any feedback on the approach I took.
Thanks!
Jason
@lilleyse TODO:
RenderState
inClearCommand
. This approach sort of breaks down for models that contain both opaque and transparent commands since the stencil buffer will get trashed in-between the opaque and translucent passes. All commands could be treated as translucent, but if OIT isn't supported the commands will be sorted and the clear commands won't. Essentially what's required for this all to work is render all model commands at the same time, opaque and translucent, immediately followed by the clear command - but a paradigm like list doesn't really exist in the rendering engine. Alternatively, if only the extruded back faces are rendered then the stencil writes can be done in the silhouette color pass and no clear command is needed. At this point though, it's useless to use stenciling at all and the artifacts are worse as @jasonbeverage noted. One further solution is to support only 255 silhouettes at a time - but this is limiting and has some edge cases in terms of constantly updating render states. Decided to go with this last approach and accept the minimal and somewhat unlikely artifactsv_normal
and assumes the normal is in view space, both of which are true for Cesium's sample models but not for all models.color
with translucency.CHANGES.md
.