Good morning/evening jmonkeys,
I was recently pointed towards OpenGL compute shaders and trying to implement them i found its more work than i thought and my openGLKnowledge = null, so i started reading.
soon i read about Occlusion Queries and Conditional Rendering and found that would be a good exercise in adding some OpenGL functionality to jme.
I guess none of the changes is a breaking change (although the GL3 interface and the Renderer interface have methods added, guess nobody provides custom implementations for those though)
however it required a way to inject code right before a specific geometry is rendered and also right after it was rendered (to actually query for the correct draw calls instead of all drawcalls of a frame), thus I added 2 methods to Geometry:
notifyRenderStarted(RenderManager rm) and
(might rename them to preRender() and postRender() maybe)
otherwise i would have had to add methods to the control interface (the actual way to extend a spatials behaviour) and in addition to notifying the controls when the render stage is entered we would notify again when the specific geometry is rendered and a third time when rendering was finished and for nodes it woult not work at all.
but strictly speaking i dont want to extend the functionality of SPATIALS, only the functionality of GEOMETRIES so i found the Geometry class to be a good place
inside the Geometry class those methods are empty though so if you dont want to use it its a no-op.
However a new spatial was added: OcclusionQueryGeometry (extends Geometry), which overrides those functions, injects the query and in case a query result is available (from last frame) and it says 0 fragments were drawn, instead of rendering the original mesh, renders a box mesh with Unshaded.j3md material with the same extend as the original meshes bounds so in the next frame it is checked if any fragments of the box made it to the screen and if so, render the original mesh again
note: the box material has setColorWrite and setDepthWrite set to false, they still count in the query though so you can see if any fragment WOULD have made it to the screen if it was actually drawn
Also another spatial was added: StaticConditionalRenderNode (extends Node), which just as the OcclusionQueryGeometry uses a box mesh the same size of the nodes bounds to see if any part of the node is visible and skips rendering any geometries in that node in case no fragments would have been drawn. it only works with opaque objects that stay within the bounds of the node when it was initialized
The StaticConditionalRenderNode is quite tricky though:
Usually, all geometries that are not culled are added to a geometry list, which is then sorted and finally rendered. that means there is no guarantee that all geometries of a node are rendered in a row which is needed for easier conditional rendering.
thus all geometries added to the StaticConditionalRenderNode will have their CullHints set to ALWAYS (resulting in less geometries in the geometryList and shorter sorting times, but also meaning the sorting might not be perfect → overdraw, dependant on your scenegraph structure though).
and when the test box mesh finished rendering injects starting the conditional render, renders all geometries of that node and stops the conditional render, before the rendermanager continues rendering the geometries from the queue. if the result of drawing the test box in the last frame was 0 fragments, the conditional render and rendering all geometries is completly skipped
about the OcclusionQueryGeometry: For small and/or fast moving objects this might not be ideal because of the 1 frame delay (objects might “pop in”) and because there is not much benefit in drawing a box instead of another simple geometry, however for big and/or static geometries with many vertices this should give a performance improvement, especially if the original mesh uses heavyweight shaders (many uniforms, tessellation stage, many texture lookups, dynamic loops, high number of vertices, etc) for exmaple terrain chunks. it does NOT save drawcalls, but it replaces complex meshes / shaders with simple ones when the geometry is occluded
About the StaticConditionalRenderNode: as mentioned, it only works with static geometries because the test box mesh does not react to bounding volume changes. it is especially useful when you have several geometries close by another but also some bigger geometries around that occlude the vision. example: rooms with furnitures (could be batched but if you set the nodes bounds to fit the room you can still move around the geometries separately, still they would all be culled when you leave the room and look at it from within another room). another example is chunks with several geometries per chunk
is there any interest in adding this to the engine? i would clean it up and document it then, which i would skip ofc if i only used it for myself
if you made it down here thanks for your attention and many greetings from the shire,