I am trying to render a mix of 2D/3D where the 2D and the 3D will be sorted in z-order.
I am achieving this with the following render script:
function update(self)
render.set_depth_mask(true)
render.set_viewport(0, 0, render.get_window_width(), render.get_window_height())
render.clear({[render.BUFFER_COLOR_BIT] = self.clear_color, [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0})
render.set_projection(vmath.matrix4_orthographic(
-p.render_w * p.cam.scale + p.cam.pos.x,
p.render_w * p.cam.scale + p.cam.pos.x,
-p.render_h * p.cam.scale + p.cam.pos.y,
p.render_h * p.cam.scale + p.cam.pos.y,
-40000, 10000))
render.enable_state(render.STATE_BLEND)
render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA)
render.disable_state(render.STATE_STENCIL_TEST)
-- 3D
render.enable_state(render.STATE_CULL_FACE)
render.enable_state(render.STATE_DEPTH_TEST)
render.set_depth_mask(true)
render.draw(self.model_pred)
-- 2D
render.set_depth_mask(false)
render.enable_state(render.STATE_DEPTH_TEST)
render.disable_state(render.STATE_CULL_FACE)
render.draw(self.tile_pred)
render.draw(self.particle_pred)
-- ------
render.draw_debug3d()
-- GUI
render.set_depth_mask(false)
render.disable_state(render.STATE_DEPTH_TEST)
render.set_projection(vmath.matrix4_orthographic(0, render.get_window_width(), 0, render.get_window_height(), -1, 1))
render.enable_state(render.STATE_STENCIL_TEST)
render.draw(self.gui_pred)
render.draw(self.text_pred)
render.disable_state(render.STATE_STENCIL_TEST)
render.draw_debug2d()
end
This works perfectly fine. The models render first and writes to the depth buffer and then 2D renders and read the buffer as well.
On desktop that is…
I noticed that on mobile this doesn’t work at all. All 2D is rendered above the 3D. Why is that? Is it because of the high projection depth (10000 , -40000)?
Hmm. Do the shaders for 2d and 3d have different precision for the vertex positions?
No, seems like medium on all. (Using the built in sprite.vp and model.vp)
With more investigation it seems like this is an apple/iOS issue.
Testing the same build on android (Amazon Fire even) shows it exactly the way it should be.
iPhone 7 had the above described problem.
Now trying on iPhone 5c it got similar problems (3D showing. Some 2D elements showing but not the furthest away)
Any difference if you set vertex positions to highp?
From the OpenGL ES Shading Language reference:
highp - 16-bit, floating point range: -2^62 to 2^62, integer range: -2^16 to 2^16
mediump - 10 bit, floating point range: -2^14 to 2^14, integer range: -2^10 to 2^10
lowp - 8 bit, floating point range: -2 to 2, integer range: -2^8 to 2^8
(I believe these are minimums. Any implementation is free to offer higher precision.)
2 Likes
Tried to set them all to highp
No difference.
Feels like it could be precision of depth buffer or something similar but I’m not sure if I can even modify that?
1 Like
It’s odd that this only seems to be an issue on the iPhone/Pad devices, however a fast search you can see other people experience the same hw specific problem, for example https://github.com/OSMBuildings/OSMBuildings/issues/49. It could in your specific case be related to the tile based rendering (unlike the link, where the high vs low precision was the problem).
Check this OpenGL FAQ for z-buffer related issues. It has a lot of information that’s relevant to your case.
I think what you will end up needing to do is to decrease the near-far range. 50000 seems very high.
However, multipass as described “12.080 There is no way that a standard-sized depth buffer will have enough precision for my astronomically large scene. What are my options?” is another possible solutions, so is using render.set_polygon_offset in the render script can also help if you can divide things into different passes (predicates).
2 Likes
Thanks Andreas.
Yes I will setup a new testproject and see what solution might be the best. Great links!
1 Like
I can confirm that ‘far’ and ‘near’ values are the problem for iPhone and the depthbuffer will only write within the range of 16-bits signed ( -32768 - 32768 ).
This is actually well enough for us and I will make some more hardware tests to see if there are any devices with even harder limitations.
This is the solution for me now but if I see that precision becomes bad even within this range, I will probably have to find other solutions.
Thanks for all help.
5 Likes
Ok, good you verified that. The iPhone depth buffer depth is 24-bit (and optionally 8bit stencil).
Even if one selects 16bit, that is ignored and a 24bit buffer is used (this is usually how OpenGL works on 24bit stencil only hardware).
The one thing that could limit this is if you use lowp instead of highp, but you state above you already tried that.
You could send me your app or or steps to reproduce and I’ll have a look at it.
I’ve added you to a little project (PROJECT ID
71576)
In that you can touch/swipe on the left side to move the 2D sprite in z axis. To top of screen to be furthest away in z-range (defined in move.script)
And to bottom of screen to be close in z (start z defined in move.script as well ).
Same thing for the 3D object, swiping on the right side of screen.
In the state the project is now, the depth test is working.
If you in render script would set matrix4_orthographic far param to above 32750 it will work on desktop but not on iphone.
Cheers
To end this post without questions unanswered:
Apple uses 24bits depth Z-buffer on all it’s mobile devices. Problem was that I only tried raising to highp on the model material (that is writing and reading on the buffer) but using mediump on the Sprites (that is only reading)
It made all sprites render on top of all models.
Solution is to either use highp on all shaders using the buffer or (of course) keep the depth values lower.
7 Likes