Procházet zdrojové kódy

display: Support specifying depth range on DisplayRegion

See https://discourse.panda3d.org/t/depthoffsetattrib-z-range-composition-order/27943/4
rdb před 4 roky
rodič
revize
280175f267

+ 32 - 0
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.

+ 4 - 2
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 &copy) :
   _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)
 {
 }
 

+ 6 - 0
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;
 

+ 63 - 12
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();
 }
 

+ 3 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -675,6 +675,9 @@ protected:
   bool _scissor_enabled;
   bool _scissor_attrib_active;
   epvector<LVecBase4i> _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;

+ 29 - 1
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)