test_depth_buffer.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. from panda3d import core
  2. import pytest
  3. @pytest.fixture(scope='module', params=[32, 24, 16])
  4. def depth_region(request, graphics_pipe):
  5. """Creates and returns a DisplayRegion with a depth buffer."""
  6. engine = core.GraphicsEngine()
  7. engine.set_threading_model("")
  8. # Vulkan needs no host window.
  9. if graphics_pipe.get_interface_name() != "Vulkan":
  10. host_fbprops = core.FrameBufferProperties()
  11. host_fbprops.force_hardware = True
  12. host = engine.make_output(
  13. graphics_pipe,
  14. 'host',
  15. 0,
  16. host_fbprops,
  17. core.WindowProperties.size(32, 32),
  18. core.GraphicsPipe.BF_refuse_window,
  19. )
  20. engine.open_windows()
  21. if host is None:
  22. pytest.skip("GraphicsPipe cannot make offscreen buffers")
  23. host_gsg = host.gsg
  24. else:
  25. host = None
  26. host_gsg = None
  27. fbprops = core.FrameBufferProperties()
  28. fbprops.force_hardware = True
  29. fbprops.depth_bits = request.param
  30. if fbprops.depth_bits >= 32:
  31. fbprops.float_depth = True
  32. buffer = engine.make_output(
  33. graphics_pipe,
  34. 'buffer',
  35. 0,
  36. fbprops,
  37. core.WindowProperties.size(32, 32),
  38. core.GraphicsPipe.BF_refuse_window,
  39. host_gsg,
  40. host
  41. )
  42. engine.open_windows()
  43. if buffer is None:
  44. pytest.skip("Cannot make depth buffer")
  45. if buffer.get_fb_properties().depth_bits != request.param:
  46. pytest.skip("Could not make buffer with desired bit count")
  47. yield buffer.make_display_region()
  48. if buffer is not None:
  49. engine.remove_window(buffer)
  50. def render_depth_pixel(region, distance, near, far, clear=None, write=True, state=None):
  51. """Renders a fragment at the specified distance using the specified render
  52. settings, and returns the resulting depth value."""
  53. # Set up the scene with a blank card rendering at specified distance.
  54. scene = core.NodePath("root")
  55. scene.set_attrib(core.DepthTestAttrib.make(core.RenderAttrib.M_always))
  56. scene.set_depth_write(write)
  57. if state:
  58. scene.set_state(scene.get_state().compose(state))
  59. camera = scene.attach_new_node(core.Camera("camera"))
  60. camera.node().get_lens(0).set_near_far(near, far)
  61. camera.node().set_cull_bounds(core.OmniBoundingVolume())
  62. if distance is not None:
  63. cm = core.CardMaker("card")
  64. cm.set_frame(-1, 1, -1, 1)
  65. card = scene.attach_new_node(cm.generate())
  66. card.set_pos(0, distance, 0)
  67. card.set_scale(60)
  68. region.active = True
  69. region.camera = camera
  70. if clear is not None:
  71. region.set_clear_depth_active(True)
  72. region.set_clear_depth(clear)
  73. depth_texture = core.Texture("depth")
  74. region.window.add_render_texture(depth_texture,
  75. core.GraphicsOutput.RTM_copy_ram,
  76. core.GraphicsOutput.RTP_depth)
  77. region.window.engine.render_frame()
  78. region.window.clear_render_textures()
  79. col = core.LColor()
  80. depth_texture.peek().lookup(col, 0.5, 0.5)
  81. return col[0]
  82. def test_depth_clear(depth_region):
  83. assert 1.0 == render_depth_pixel(depth_region, None, near=1, far=10, clear=1.0)
  84. assert 0.0 == render_depth_pixel(depth_region, None, near=1, far=10, clear=0.0)
  85. def test_depth_write(depth_region):
  86. assert 1.0 == render_depth_pixel(depth_region, 5.0, near=1, far=10, clear=1.0, write=False)
  87. assert 0.99 > render_depth_pixel(depth_region, 5.0, near=1, far=10, clear=1.0, write=True)
  88. def test_depth_far_inf(depth_region):
  89. inf = float("inf")
  90. assert 0.99 > render_depth_pixel(depth_region, 10.0, near=1, far=inf, clear=1.0)
  91. def test_depth_near_inf(depth_region):
  92. inf = float("inf")
  93. assert 0.01 < render_depth_pixel(depth_region, 10.0, near=inf, far=1, clear=0.0)
  94. def test_depth_clipping(depth_region):
  95. # Get the actual depth resulting from the clear value.
  96. clr = render_depth_pixel(depth_region, None, near=1, far=10, clear=0.5)
  97. # We try rendering something at various distances to make sure that the
  98. # resulting depth value matches our expectations.
  99. # Too close; read clear value.
  100. assert clr == render_depth_pixel(depth_region, 0.999, near=1, far=10, clear=0.5)
  101. # Too far; read clear value.
  102. assert clr == render_depth_pixel(depth_region, 10.01, near=1, far=10, clear=0.5)
  103. # Just close enough; read a value close to 0.0.
  104. assert 0.01 > render_depth_pixel(depth_region, 1.001, near=1, far=10, clear=0.5)
  105. # Just far enough; read 1.0.
  106. assert 0.99 < render_depth_pixel(depth_region, 9.999, near=1, far=10, clear=0.5)
  107. def test_inverted_depth_clipping(depth_region):
  108. # Get the actual depth resulting from the clear value.
  109. clr = render_depth_pixel(depth_region, None, near=1, far=10, clear=0.5)
  110. # Too close; read clear value.
  111. assert clr == render_depth_pixel(depth_region, 0.999, near=10, far=1, clear=0.5)
  112. # Too far; read clear value.
  113. assert clr == render_depth_pixel(depth_region, 10.01, near=10, far=1, clear=0.5)
  114. # Just close enough; read a value close to 1.0.
  115. assert 0.99 < render_depth_pixel(depth_region, 1.001, near=10, far=1, clear=0.5)
  116. # Just far enough; read a value close to 0.0.
  117. assert 0.01 > render_depth_pixel(depth_region, 9.999, near=10, far=1, clear=0.5)
  118. def test_depth_range(depth_region):
  119. try:
  120. depth_region.set_depth_range(0.25, 0.75)
  121. z = render_depth_pixel(depth_region, 1.00001, near=1, far=10, clear=0.0)
  122. assert z == pytest.approx(0.25, rel=0.01)
  123. z = render_depth_pixel(depth_region, 9.99999, near=1, far=10, clear=0.0)
  124. assert z == pytest.approx(0.75, rel=0.01)
  125. # Combines with DepthOffsetAttrib range.
  126. state = core.RenderState.make(core.DepthOffsetAttrib.make(0, 0.25, 0.75))
  127. z = render_depth_pixel(depth_region, 1.00001, near=1, far=10, clear=0.0, state=state)
  128. assert z == pytest.approx(0.375, rel=0.01)
  129. # Reverse the depth range.
  130. depth_region.set_depth_range(0.75, 0.25)
  131. z = render_depth_pixel(depth_region, 1.00001, near=1, far=10, clear=0.0)
  132. assert z == pytest.approx(0.75, rel=0.01)
  133. z = render_depth_pixel(depth_region, 9.99999, near=1, far=10, clear=0.0)
  134. assert z == pytest.approx(0.25, rel=0.01)
  135. finally:
  136. depth_region.set_depth_range(0, 1)
  137. def test_depth_bias(depth_region):
  138. # Without depth bias
  139. z_ref = render_depth_pixel(depth_region, 5, near=1, far=10)
  140. # With constant positive depth bias
  141. state = core.RenderState.make(core.DepthBiasAttrib.make(0, 1))
  142. z = render_depth_pixel(depth_region, 5, near=1, far=10, state=state)
  143. assert z > z_ref
  144. # With constant negative depth bias
  145. state = core.RenderState.make(core.DepthBiasAttrib.make(0, -1))
  146. z = render_depth_pixel(depth_region, 5, near=1, far=10, state=state)
  147. assert z < z_ref
  148. # With slope-scaled depth bias (our quad has no slope)
  149. state = core.RenderState.make(core.DepthBiasAttrib.make(10, 0))
  150. z = render_depth_pixel(depth_region, 5, near=1, far=10, state=state)
  151. assert z == pytest.approx(z_ref)
  152. # Same, but negative
  153. state = core.RenderState.make(core.DepthBiasAttrib.make(-10, 0))
  154. z = render_depth_pixel(depth_region, 5, near=1, far=10, state=state)
  155. assert z == pytest.approx(z_ref)
  156. def test_depth_offset(depth_region):
  157. # Without depth offset
  158. z_ref = render_depth_pixel(depth_region, 5, near=1, far=10)
  159. # With constant positive depth offset
  160. state = core.RenderState.make(core.DepthOffsetAttrib.make(1))
  161. z = render_depth_pixel(depth_region, 5, near=1, far=10, state=state)
  162. assert z < z_ref
  163. # With constant negative depth offset
  164. state = core.RenderState.make(core.DepthOffsetAttrib.make(-1))
  165. z = render_depth_pixel(depth_region, 5, near=1, far=10, state=state)
  166. assert z > z_ref