diff --git a/panda/src/display/displayRegion.I b/panda/src/display/displayRegion.I index 71b3a069cf..0377a90073 100644 --- a/panda/src/display/displayRegion.I +++ b/panda/src/display/displayRegion.I @@ -156,6 +156,28 @@ set_dimensions(const LVecBase4 &dimensions) { set_dimensions(0, dimensions); } +/** + * Changes the range of the depth buffer this DisplayRegion writes to. + * The parameters range from 0 to 1. It is legal for the near value to be + * larger than the far value. + */ +INLINE void DisplayRegion:: +set_depth_range(PN_stdfloat near, PN_stdfloat far) { + CDWriter cdata(_cycler, true); + cdata->_depth_range.set(near, far); +} + +/** + * + */ +INLINE void DisplayRegion:: +get_depth_range(PN_stdfloat &near, PN_stdfloat &far) const { + CDReader cdata(_cycler); + const LVecBase2 &range = cdata->_depth_range; + near = range[0]; + far = range[1]; +} + /** * Returns the GraphicsOutput that this DisplayRegion is ultimately associated * with, or NULL if no window is associated. @@ -693,6 +715,16 @@ get_top(int i) const { return _cdata->_regions[i]._dimensions[3]; } +/** + * + */ +INLINE void DisplayRegionPipelineReader:: +get_depth_range(PN_stdfloat &near, PN_stdfloat &far) const { + const LVecBase2 &range = _cdata->_depth_range; + near = range[0]; + far = range[1]; +} + /** * Returns the GraphicsOutput that this DisplayRegion is ultimately associated * with, or NULL if no window is associated. diff --git a/panda/src/display/displayRegion.cxx b/panda/src/display/displayRegion.cxx index cddfd2b4a5..b29d490d03 100644 --- a/panda/src/display/displayRegion.cxx +++ b/panda/src/display/displayRegion.cxx @@ -738,7 +738,8 @@ CData() : _stereo_channel(Lens::SC_mono), _tex_view_offset(0), _target_tex_page(-1), - _scissor_enabled(true) + _scissor_enabled(true), + _depth_range(0, 1) { _regions.push_back(Region()); } @@ -757,7 +758,8 @@ CData(const DisplayRegion::CData ©) : _stereo_channel(copy._stereo_channel), _tex_view_offset(copy._tex_view_offset), _target_tex_page(copy._target_tex_page), - _scissor_enabled(copy._scissor_enabled) + _scissor_enabled(copy._scissor_enabled), + _depth_range(copy._depth_range) { } diff --git a/panda/src/display/displayRegion.h b/panda/src/display/displayRegion.h index 0aa9d2b998..f5d25639a3 100644 --- a/panda/src/display/displayRegion.h +++ b/panda/src/display/displayRegion.h @@ -82,6 +82,9 @@ PUBLISHED: virtual void set_dimensions(int i, const LVecBase4 &dimensions); MAKE_PROPERTY(dimensions, get_dimensions, set_dimensions); + INLINE void set_depth_range(PN_stdfloat near, PN_stdfloat far); + INLINE void get_depth_range(PN_stdfloat &near, PN_stdfloat &far) const; + INLINE GraphicsOutput *get_window() const; GraphicsPipe *get_pipe() const; virtual bool is_stereo() const; @@ -233,6 +236,7 @@ private: } Regions _regions; + LVecBase2 _depth_range; // near, far int _lens_index; // index into which lens of a camera is associated with this display region. 0 is default @@ -333,6 +337,8 @@ public: INLINE PN_stdfloat get_bottom(int i = 0) const; INLINE PN_stdfloat get_top(int i = 0) const; + INLINE void get_depth_range(PN_stdfloat &near, PN_stdfloat &far) const; + INLINE GraphicsOutput *get_window() const; GraphicsPipe *get_pipe() const; diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 74d9f208b3..426597b83e 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -528,6 +528,7 @@ CLP(GraphicsStateGuardian)(GraphicsEngine *engine, GraphicsPipe *pipe) : _scissor_enabled = false; _scissor_attrib_active = false; + _has_attrib_depth_range = false; _white_texture = 0; @@ -3337,6 +3338,7 @@ reset() { _glDepthRangedNV(-1.0, 1.0); _use_depth_zero_to_one = true; _use_remapped_depth_range = true; + _has_attrib_depth_range = false; if (GLCAT.is_debug()) { GLCAT.debug() @@ -3352,6 +3354,17 @@ reset() { } #endif + if (_has_attrib_depth_range) { +#ifdef OPENGLES + glDepthRangef(0.0f, 1.0f); +#else + glDepthRange(0.0, 1.0); +#endif + _depth_range_near = 0; + _depth_range_far = 1; + _has_attrib_depth_range = false; + } + // Set up all the enableddisabled flags to GL's known initial values: // everything off. _multisample_mode = 0; @@ -3926,6 +3939,33 @@ prepare_display_region(DisplayRegionPipelineReader *dr) { } } + PN_stdfloat near; + PN_stdfloat far; + dr->get_depth_range(near, far); +#ifdef GSG_VERBOSE + if (GLCAT.is_spam()) { + GLCAT.spam() + << "glDepthRange(" << near << ", " << far << ")" << endl; + } +#endif + +#ifdef OPENGLES + // OpenGL ES uses a single-precision call. + glDepthRangef((GLclampf)near, (GLclampf)far); +#else + // Mainline OpenGL uses a double-precision call. + if (!_use_remapped_depth_range) { + glDepthRange((GLclampd)near, (GLclampd)far); + } else { + // If we have a remapped depth range, we should adjust the values to range + // from -1 to 1. We need to use an NV extension to pass unclamped values. + _glDepthRangedNV(near * 2.0 - 1.0, far * 2.0 - 1.0); + } +#endif // OPENGLES + _has_attrib_depth_range = false; + _depth_range_near = near; + _depth_range_far = far; + report_my_gl_errors(); } @@ -8151,24 +8191,35 @@ do_issue_depth_offset() { PN_stdfloat min_value = target_depth_offset->get_min_value(); PN_stdfloat max_value = target_depth_offset->get_max_value(); + if (min_value != (PN_stdfloat)0.0 || + max_value != (PN_stdfloat)1.0 || + _has_attrib_depth_range) { + min_value = _depth_range_far * min_value + _depth_range_near * (1 - min_value); + max_value = _depth_range_far * max_value + _depth_range_near * (1 - max_value); + #ifdef GSG_VERBOSE - GLCAT.spam() - << "glDepthRange(" << min_value << ", " << max_value << ")" << endl; + if (GLCAT.is_spam()) { + GLCAT.spam() + << "glDepthRange(" << min_value << ", " << max_value << ")" << endl; + } #endif #ifdef OPENGLES - // OpenGL ES uses a single-precision call. - glDepthRangef((GLclampf)min_value, (GLclampf)max_value); + // OpenGL ES uses a single-precision call. + glDepthRangef((GLclampf)min_value, (GLclampf)max_value); #else - // Mainline OpenGL uses a double-precision call. - if (!_use_remapped_depth_range) { - glDepthRange((GLclampd)min_value, (GLclampd)max_value); - } else { - // If we have a remapped depth range, we should adjust the values to range - // from -1 to 1. We need to use an NV extension to pass unclamped values. - _glDepthRangedNV(min_value * 2.0 - 1.0, max_value * 2.0 - 1.0); - } + // Mainline OpenGL uses a double-precision call. + if (!_use_remapped_depth_range) { + glDepthRange((GLclampd)min_value, (GLclampd)max_value); + } else { + // If we have a remapped depth range, we should adjust the values to range + // from -1 to 1. We need to use an NV extension to pass unclamped values. + _glDepthRangedNV(min_value * 2.0 - 1.0, max_value * 2.0 - 1.0); + } #endif // OPENGLES + _has_attrib_depth_range = true; + } + report_my_gl_errors(); } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 83f2721fa3..656ee40e5b 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -675,6 +675,9 @@ protected: bool _scissor_enabled; bool _scissor_attrib_active; epvector _scissor_array; + PN_stdfloat _depth_range_near; + PN_stdfloat _depth_range_far; + bool _has_attrib_depth_range; #ifndef OPENGLES_1 BitMask32 _enabled_vertex_attrib_arrays; diff --git a/tests/display/test_depth_buffer.py b/tests/display/test_depth_buffer.py index c655758703..b1352fac14 100644 --- a/tests/display/test_depth_buffer.py +++ b/tests/display/test_depth_buffer.py @@ -56,7 +56,7 @@ def depth_region(request, graphics_pipe): engine.remove_window(buffer) -def render_depth_pixel(region, distance, near, far, clear=None, write=True): +def render_depth_pixel(region, distance, near, far, clear=None, write=True, state=None): """Renders a fragment at the specified distance using the specified render settings, and returns the resulting depth value.""" @@ -65,6 +65,9 @@ def render_depth_pixel(region, distance, near, far, clear=None, write=True): scene.set_attrib(core.DepthTestAttrib.make(core.RenderAttrib.M_always)) scene.set_depth_write(write) + if state: + scene.set_state(scene.get_state().compose(state)) + camera = scene.attach_new_node(core.Camera("camera")) camera.node().get_lens(0).set_near_far(near, far) camera.node().set_cull_bounds(core.OmniBoundingVolume()) @@ -151,3 +154,28 @@ def test_inverted_depth_clipping(depth_region): # Just far enough; read a value close to 0.0. assert 0.01 > render_depth_pixel(depth_region, 9.999, near=10, far=1, clear=0.5) + + +def test_depth_range(depth_region): + try: + depth_region.set_depth_range(0.25, 0.75) + z = render_depth_pixel(depth_region, 1.00001, near=1, far=10) + assert z == pytest.approx(0.25, rel=0.01) + + z = render_depth_pixel(depth_region, 10, near=1, far=10) + assert z == pytest.approx(0.75, rel=0.01) + + # Combines with DepthOffsetAttrib range. + state = core.RenderState.make(core.DepthOffsetAttrib.make(0, 0.25, 0.75)) + z = render_depth_pixel(depth_region, 1.00001, near=1, far=10, state=state) + assert z == pytest.approx(0.375, rel=0.01) + + # Reverse the depth range. + depth_region.set_depth_range(0.75, 0.25) + z = render_depth_pixel(depth_region, 1.00001, near=1, far=10) + assert z == pytest.approx(0.75, rel=0.01) + + z = render_depth_pixel(depth_region, 10, near=1, far=10) + assert z == pytest.approx(0.25, rel=0.01) + finally: + depth_region.set_depth_range(0, 1)