diff --git a/doc/APIreference/APItypes.rst b/doc/APIreference/APItypes.rst index 706a6e28..a8450195 100644 --- a/doc/APIreference/APItypes.rst +++ b/doc/APIreference/APItypes.rst @@ -502,6 +502,16 @@ These are the possible framebuffers. They are used as an argument to the functio .. mujoco-include:: mjtFramebuffer +mjtDepthMap +~~~~~~~~~~~ + +These are the depth mapping options. They are used as a value for the ``readPixelDepth`` attribute of the +:ref:`mjrContext` struct, to control how the depth returned by :ref:`mjr_readPixels` is mapped from +``znear`` to ``zfar``. + +.. mujoco-include:: mjtDepthMap + + .. _mjtFontScale: mjtFontScale diff --git a/doc/changelog.rst b/doc/changelog.rst index c1371156..51f43a04 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -131,15 +131,19 @@ General 22. Added multi-threaded constraint solving via :ref:`mj_island` and :ref:`mjThreadPool` to :ref:`testspeed` exposed via npoolthread flag. The `22 humanoids `__ model shows a 3x speedup compared to the single threaded simulation. +23. Implemented reversed Z rendering for better depth precision. An enum :ref:`mjtDepthMap` was added with values + :ref:`mjDEPTH_ZERONEAR` and :ref:`mjDEPTH_ZEROFAR`, which can be used to set the new ``readDepthMap`` attribute in + :ref:`mjrContext`` to control how the depth returned by :ref:`mjr_readPixels` is mapped from ``znear`` to ``zfar``. + `Contribution `__ by `Levi Burner `__. Python bindings ^^^^^^^^^^^^^^^ -23. Fixed `#870 `__ where calling ``update_scene`` with an invalid +24. Fixed `#870 `__ where calling ``update_scene`` with an invalid camera name used the default camera. -24. Added ``user_scn`` to the :ref:`passive viewer` handle, which allows users to add custom - visualization geoms (`#1023 `__). -25. Added optional boolean keyword arguments ``show_left_ui`` and ``show_right_ui`` to the functions ``viewer.launch`` +25. Added ``user_scn`` to the :ref:`passive viewer` handle, which allows users to add custom + visualization geoms (`#1023 `__). +26. Added optional boolean keyword arguments ``show_left_ui`` and ``show_right_ui`` to the functions ``viewer.launch`` and ``viewer.launch_passive``, which allow users to launch a viewer with UI panels hidden. Simulate @@ -149,21 +153,21 @@ Simulate :align: right :width: 240px -26. Added **state history** mechanism to :ref:`simulate` and the managed +27. Added **state history** mechanism to :ref:`simulate` and the managed :ref:`Python viewer`. State history can be viewed by scrubbing the History slider and (more precisely) with the left and right arrow keys. See screen capture: -27. The ``LOADING...`` label is now shown correctly. +28. The ``LOADING...`` label is now shown correctly. `Contribution `__ by `Levi Burner `__. Bug fixes ^^^^^^^^^ -28. Fixed a bug that was causing :ref:`geom margin` to be ignored during the construction of +29. Fixed a bug that was causing :ref:`geom margin` to be ignored during the construction of midphase collision trees. -29. Fixed a bug that was generating incorrect values in ``efc_diagApprox`` for weld equality constraints. +30. Fixed a bug that was generating incorrect values in ``efc_diagApprox`` for weld equality constraints. Version 2.3.7 (July 20, 2023) diff --git a/doc/includes/references.h b/doc/includes/references.h index 1ad876c8..3f98f313 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -1443,6 +1443,10 @@ typedef enum mjtFramebuffer_ { // OpenGL framebuffer option mjFB_WINDOW = 0, // default/window buffer mjFB_OFFSCREEN // offscreen buffer } mjtFramebuffer; +typedef enum mjtDepthMap_ { // depth mapping for `mjr_readPixels` + mjDEPTH_ZERONEAR = 0, // standard depth map; 0: znear, 1: zfar + mjDEPTH_ZEROFAR = 1 // reversed depth map; 1: znear, 0: zfar +} mjtDepthMap; typedef enum mjtFontScale_ { // font scale, used at context creation mjFONTSCALE_50 = 50, // 50% scale, suitable for low-res rendering mjFONTSCALE_100 = 100, // normal scale, suitable in the absence of DPI scaling @@ -1546,6 +1550,9 @@ struct mjrContext_ { // custom OpenGL context // pixel output format int readPixelFormat; // default color pixel format for mjr_readPixels + + // depth output format + int readDepthMap; // depth mapping: mjDEPTH_ZERONEAR or mjDEPTH_ZEROFAR }; typedef struct mjrContext_ mjrContext; typedef enum mjtTaskStatus_ { // status values for mjTask diff --git a/include/mujoco/mjrender.h b/include/mujoco/mjrender.h index 4b417c15..4a4089d7 100644 --- a/include/mujoco/mjrender.h +++ b/include/mujoco/mjrender.h @@ -43,6 +43,10 @@ typedef enum mjtFramebuffer_ { // OpenGL framebuffer option mjFB_OFFSCREEN // offscreen buffer } mjtFramebuffer; +typedef enum mjtDepthMap_ { // depth mapping for `mjr_readPixels` + mjDEPTH_ZERONEAR = 0, // standard depth map; 0: znear, 1: zfar + mjDEPTH_ZEROFAR = 1 // reversed depth map; 1: znear, 0: zfar +} mjtDepthMap; typedef enum mjtFontScale_ { // font scale, used at context creation mjFONTSCALE_50 = 50, // 50% scale, suitable for low-res rendering @@ -155,6 +159,9 @@ struct mjrContext_ { // custom OpenGL context // pixel output format int readPixelFormat; // default color pixel format for mjr_readPixels + + // depth output format + int readDepthMap; // depth mapping: mjDEPTH_ZERONEAR or mjDEPTH_ZEROFAR }; typedef struct mjrContext_ mjrContext; diff --git a/introspect/enums.py b/introspect/enums.py index 5b0fa748..bb039f65 100644 --- a/introspect/enums.py +++ b/introspect/enums.py @@ -650,6 +650,15 @@ ENUMS: Mapping[str, EnumDecl] = dict([ ('mjFB_OFFSCREEN', 1), ]), )), + ('mjtDepthMap', + EnumDecl( + name='mjtDepthMap', + declname='enum mjtDepthMap_', + values=dict([ + ('mjDEPTH_ZERONEAR', 0), + ('mjDEPTH_ZEROFAR', 1), + ]), + )), ('mjtFontScale', EnumDecl( name='mjtFontScale', diff --git a/introspect/structs.py b/introspect/structs.py index e5cce8bd..639600cb 100644 --- a/introspect/structs.py +++ b/introspect/structs.py @@ -7905,6 +7905,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([ type=ValueType(name='int'), doc='default color pixel format for mjr_readPixels', ), + StructFieldDecl( + name='readDepthMap', + type=ValueType(name='int'), + doc='depth mapping: mjDEPTH_ZERONEAR or mjDEPTH_ZEROFAR', + ), ), )), ('mjuiState', diff --git a/python/mujoco/render.cc b/python/mujoco/render.cc index 622bbb7a..b2922efc 100644 --- a/python/mujoco/render.cc +++ b/python/mujoco/render.cc @@ -215,6 +215,7 @@ PYBIND11_MODULE(_render, pymodule) { X(windowDoublebuffer); X(currentBuffer); X(readPixelFormat); + X(readDepthMap); #undef X #define X(var) \ diff --git a/python/mujoco/renderer.py b/python/mujoco/renderer.py index e96cd89e..f34c8243 100644 --- a/python/mujoco/renderer.py +++ b/python/mujoco/renderer.py @@ -86,6 +86,7 @@ the clause: _render.mjr_setBuffer( _enums.mjtFramebuffer.mjFB_OFFSCREEN.value, self._mjr_context ) + self._mjr_context.readDepthMap = _enums.mjtDepthMap.mjDEPTH_ZEROFAR # Default render flags. self._depth_rendering = False @@ -138,7 +139,9 @@ the clause: """ original_flags = self._scene.flags.copy() - if self._segmentation_rendering: + # Using segmented rendering for depth makes the calculated depth more + # accurate at far distances. + if self._depth_rendering or self._segmentation_rendering: self._scene.flags[_enums.mjtRndFlag.mjRND_SEGMENT] = True self._scene.flags[_enums.mjtRndFlag.mjRND_IDCOLOR] = True @@ -173,11 +176,34 @@ the clause: near = self._model.vis.map.znear * extent far = self._model.vis.map.zfar * extent - # Convert from [0 1] to depth in units of length, see links below: - # http://stackoverflow.com/a/6657284/1461210 - # https://www.khronos.org/opengl/wiki/Depth_Buffer_Precision - out = near / (1 - out * (1 - near / far)) + # Calculate OpenGL perspective matrix values in float32 precision + # so they are close to what glFrustum returns + # https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/glFrustum.xml + zfar = np.float32(far) + znear = np.float32(near) + c_coef = -(zfar + znear) / (zfar - znear) + d_coef = -(np.float32(2) * zfar * znear) / (zfar - znear) + # In reverse Z mode the perspective matrix is transformed by the following + c_coef = np.float32(-0.5) * c_coef - np.float32(0.5) + d_coef = np.float32(-0.5) * d_coef + + # We need 64 bits to convert Z from ndc to metric depth without noticeable + # losses in precision + out_64 = out.astype(np.float64) + + # Undo OpenGL projection + # Note: We do not need to take action to convert from window coordinates + # to normalized device coordinates because in reversed Z mode the mapping + # is identity + out_64 = d_coef / (out_64 + c_coef) + + # Cast result back to float32 for backwards compatibility + # This has a small accuracy cost + out[:] = out_64.astype(np.float32) + + # Reset scene flags. + np.copyto(self._scene.flags, original_flags) elif self._segmentation_rendering: _render.mjr_readPixels(out, None, self._rect, self._mjr_context) diff --git a/src/render/glad/glad.c b/src/render/glad/glad.c index 9b5e4c6d..fa1642c4 100644 --- a/src/render/glad/glad.c +++ b/src/render/glad/glad.c @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// Based on the OpenGL loader generated by glad 0.1.35 on Mon Mar 21 12:42:33 2022. +// Based on the OpenGL loader generated by glad 0.1.34 on Thu Jun 22 14:40:03 2023. // // The original generated code is distributed under CC0. // https://creativecommons.org/publicdomain/zero/1.0/ @@ -22,6 +22,8 @@ // APIs: gl=1.5 // Profile: compatibility // Extensions: +// GL_ARB_clip_control, +// GL_ARB_depth_buffer_float, // GL_ARB_framebuffer_object, // GL_ARB_seamless_cube_map, // GL_ARB_vertex_buffer_object, @@ -32,9 +34,9 @@ // Reproducible: False // // Commandline: -// --profile="compatibility" --api="gl=1.5" --generator="c" --spec="gl" --extensions="GL_ARB_framebuffer_object,GL_ARB_seamless_cube_map,GL_ARB_vertex_buffer_object,GL_KHR_debug" +// --profile="compatibility" --api="gl=1.5" --generator="c" --spec="gl" --extensions="GL_ARB_clip_control,GL_ARB_depth_buffer_float,GL_ARB_framebuffer_object,GL_ARB_seamless_cube_map,GL_ARB_vertex_buffer_object,GL_KHR_debug" // Online: -// https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D1.5&extensions=GL_ARB_framebuffer_object&extensions=GL_ARB_seamless_cube_map&extensions=GL_ARB_vertex_buffer_object&extensions=GL_KHR_debug +// https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D1.5&extensions=GL_ARB_clip_control&extensions=GL_ARB_depth_buffer_float&extensions=GL_ARB_framebuffer_object&extensions=GL_ARB_seamless_cube_map&extensions=GL_ARB_vertex_buffer_object&extensions=GL_KHR_debug #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push @@ -829,10 +831,13 @@ PFNGLWINDOWPOS3IPROC mjGlad_glWindowPos3i = NULL; PFNGLWINDOWPOS3IVPROC mjGlad_glWindowPos3iv = NULL; PFNGLWINDOWPOS3SPROC mjGlad_glWindowPos3s = NULL; PFNGLWINDOWPOS3SVPROC mjGlad_glWindowPos3sv = NULL; +int mjGLAD_GL_ARB_clip_control = 0; +int mjGLAD_GL_ARB_depth_buffer_float = 0; int mjGLAD_GL_ARB_framebuffer_object = 0; int mjGLAD_GL_ARB_seamless_cube_map = 0; int mjGLAD_GL_ARB_vertex_buffer_object = 0; int mjGLAD_GL_KHR_debug = 0; +PFNGLCLIPCONTROLPROC mjGlad_glClipControl = NULL; PFNGLISRENDERBUFFERPROC mjGlad_glIsRenderbuffer = NULL; PFNGLBINDRENDERBUFFERPROC mjGlad_glBindRenderbuffer = NULL; PFNGLDELETERENDERBUFFERSPROC mjGlad_glDeleteRenderbuffers = NULL; @@ -1355,6 +1360,10 @@ static void mjGlad_load_GL_VERSION_1_5(GLADloadproc load) { mjGlad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load("glGetBufferParameteriv"); mjGlad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)load("glGetBufferPointerv"); } +static void mjGlad_load_GL_ARB_clip_control(GLADloadproc load) { + if(!mjGLAD_GL_ARB_clip_control) return; + mjGlad_glClipControl = (PFNGLCLIPCONTROLPROC)load("glClipControl"); +} static void mjGlad_load_GL_ARB_framebuffer_object(GLADloadproc load) { if(!mjGLAD_GL_ARB_framebuffer_object) return; mjGlad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)load("glIsRenderbuffer"); @@ -1419,6 +1428,8 @@ static void mjGlad_load_GL_KHR_debug(GLADloadproc load) { } static int mjGlad_find_extensionsGL(void) { if (!mjGlad_get_exts()) return 0; + mjGLAD_GL_ARB_clip_control = mjGlad_has_ext("GL_ARB_clip_control"); + mjGLAD_GL_ARB_depth_buffer_float = mjGlad_has_ext("GL_ARB_depth_buffer_float"); mjGLAD_GL_ARB_framebuffer_object = mjGlad_has_ext("GL_ARB_framebuffer_object"); mjGLAD_GL_ARB_seamless_cube_map = mjGlad_has_ext("GL_ARB_seamless_cube_map"); mjGLAD_GL_ARB_vertex_buffer_object = mjGlad_has_ext("GL_ARB_vertex_buffer_object"); @@ -1491,6 +1502,7 @@ int mjGladLoadGLUnsafe(void) { mjGlad_load_GL_VERSION_1_5(mjGlad_get_proc); if (!mjGlad_find_extensionsGL()) return 0; + mjGlad_load_GL_ARB_clip_control(mjGlad_get_proc); mjGlad_load_GL_ARB_framebuffer_object(mjGlad_get_proc); mjGlad_load_GL_ARB_vertex_buffer_object(mjGlad_get_proc); mjGlad_load_GL_KHR_debug(mjGlad_get_proc); diff --git a/src/render/glad/glad.h b/src/render/glad/glad.h index 4fc2c6c8..1b1fd449 100644 --- a/src/render/glad/glad.h +++ b/src/render/glad/glad.h @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// Based on the OpenGL loader generated by glad 0.1.35 on Mon Mar 21 12:42:33 2022. +// Based on the OpenGL loader generated by glad 0.1.34 on Thu Jun 22 14:40:03 2023. // // The original generated code is distributed under CC0. // https://creativecommons.org/publicdomain/zero/1.0/ @@ -22,6 +22,8 @@ // APIs: gl=1.5 // Profile: compatibility // Extensions: +// GL_ARB_clip_control, +// GL_ARB_depth_buffer_float, // GL_ARB_framebuffer_object, // GL_ARB_seamless_cube_map, // GL_ARB_vertex_buffer_object, @@ -32,9 +34,9 @@ // Reproducible: False // // Commandline: -// --profile="compatibility" --api="gl=1.5" --generator="c" --spec="gl" --extensions="GL_ARB_framebuffer_object,GL_ARB_seamless_cube_map,GL_ARB_vertex_buffer_object,GL_KHR_debug" +// --profile="compatibility" --api="gl=1.5" --generator="c" --spec="gl" --extensions="GL_ARB_clip_control,GL_ARB_depth_buffer_float,GL_ARB_framebuffer_object,GL_ARB_seamless_cube_map,GL_ARB_vertex_buffer_object,GL_KHR_debug" // Online: -// https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D1.5&extensions=GL_ARB_framebuffer_object&extensions=GL_ARB_seamless_cube_map&extensions=GL_ARB_vertex_buffer_object&extensions=GL_KHR_debug +// https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D1.5&extensions=GL_ARB_clip_control&extensions=GL_ARB_depth_buffer_float&extensions=GL_ARB_framebuffer_object&extensions=GL_ARB_seamless_cube_map&extensions=GL_ARB_vertex_buffer_object&extensions=GL_KHR_debug #ifndef MUJOCO_SRC_RENDER_GLAD_GLAD_H_ #define MUJOCO_SRC_RENDER_GLAD_GLAD_H_ @@ -2304,6 +2306,15 @@ typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, GLAPI PFNGLGETBUFFERPOINTERVPROC mjGlad_glGetBufferPointerv; #define glGetBufferPointerv mjGlad_glGetBufferPointerv #endif +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_NEGATIVE_ONE_TO_ONE 0x935E +#define GL_ZERO_TO_ONE 0x935F +#define GL_CLIP_ORIGIN 0x935C +#define GL_CLIP_DEPTH_MODE 0x935D +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD #define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 #define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 #define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 @@ -2487,6 +2498,17 @@ GLAPI PFNGLGETBUFFERPOINTERVPROC mjGlad_glGetBufferPointerv; #define GL_STACK_OVERFLOW_KHR 0x0503 #define GL_STACK_UNDERFLOW_KHR 0x0504 #define GL_DISPLAY_LIST 0x82E7 +#ifndef GL_ARB_clip_control +#define GL_ARB_clip_control 1 +GLAPI int mjGLAD_GL_ARB_clip_control; +typedef void (APIENTRYP PFNGLCLIPCONTROLPROC)(GLenum origin, GLenum depth); +GLAPI PFNGLCLIPCONTROLPROC mjGlad_glClipControl; +#define glClipControl mjGlad_glClipControl +#endif +#ifndef GL_ARB_depth_buffer_float +#define GL_ARB_depth_buffer_float 1 +GLAPI int mjGLAD_GL_ARB_depth_buffer_float; +#endif #ifndef GL_ARB_framebuffer_object #define GL_ARB_framebuffer_object 1 GLAPI int mjGLAD_GL_ARB_framebuffer_object; diff --git a/src/render/render_context.c b/src/render/render_context.c index 18c71805..afb1377f 100644 --- a/src/render/render_context.c +++ b/src/render/render_context.c @@ -1062,7 +1062,7 @@ static void makeShadow(const mjModel* m, mjrContext* con) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_GEQUAL); glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); @@ -1126,11 +1126,14 @@ static void makeOff(mjrContext* con) { mju_error("Could not allocate offscreen depth and stencil buffer"); } glBindRenderbuffer(GL_RENDERBUFFER, con->offDepthStencil); + + GLenum depth_buffer_format = + mjGLAD_GL_ARB_depth_buffer_float ? GL_DEPTH32F_STENCIL8 : GL_DEPTH24_STENCIL8; if (con->offSamples) { - glRenderbufferStorageMultisample(GL_RENDERBUFFER, con->offSamples, GL_DEPTH24_STENCIL8, + glRenderbufferStorageMultisample(GL_RENDERBUFFER, con->offSamples, depth_buffer_format, con->offWidth, con->offHeight); } else { - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, con->offWidth, con->offHeight); + glRenderbufferStorage(GL_RENDERBUFFER, depth_buffer_format, con->offWidth, con->offHeight); } glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, con->offDepthStencil); @@ -1169,7 +1172,7 @@ static void makeOff(mjrContext* con) { mju_error("Could not allocate offscreen depth and stencil buffer_r"); } glBindRenderbuffer(GL_RENDERBUFFER, con->offDepthStencil_r); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, con->offWidth, con->offHeight); + glRenderbufferStorage(GL_RENDERBUFFER, depth_buffer_format, con->offWidth, con->offHeight); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, con->offDepthStencil_r); @@ -1535,7 +1538,6 @@ void mjr_makeContext_offSize(const mjModel* m, mjrContext* con, int fontscale, glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - // free previous context mjr_freeContext(con); // no model: offscreen and font only @@ -1607,6 +1609,9 @@ void mjr_makeContext_offSize(const mjModel* m, mjrContext* con, int fontscale, // set default color pixel format for mjr_readPixels con->readPixelFormat = GL_RGB; + + // set default depth mapping for mjr_readPixels + con->readDepthMap = mjDEPTH_ZERONEAR; } @@ -1830,10 +1835,10 @@ MJAPI void mjr_resizeOffscreen(int width, int height, mjrContext* con) { glBindRenderbuffer(GL_RENDERBUFFER, con->offDepthStencil); if (con->offSamples) { - glRenderbufferStorageMultisample(GL_RENDERBUFFER, con->offSamples, GL_DEPTH24_STENCIL8, + glRenderbufferStorageMultisample(GL_RENDERBUFFER, con->offSamples, GL_DEPTH32F_STENCIL8, con->offWidth, con->offHeight); } else { - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, con->offWidth, con->offHeight); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH32F_STENCIL8, con->offWidth, con->offHeight); } if (con->offSamples) { @@ -1841,6 +1846,6 @@ MJAPI void mjr_resizeOffscreen(int width, int height, mjrContext* con) { glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, con->offWidth, con->offHeight); glBindRenderbuffer(GL_RENDERBUFFER, con->offDepthStencil_r); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, con->offWidth, con->offHeight); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH32F_STENCIL8, con->offWidth, con->offHeight); } } diff --git a/src/render/render_gl2.c b/src/render/render_gl2.c index f655bc4e..75e91856 100644 --- a/src/render/render_gl2.c +++ b/src/render/render_gl2.c @@ -14,6 +14,7 @@ #include "render/render_gl2.h" +#include #include #include @@ -92,6 +93,23 @@ void mjr_restoreBuffer(const mjrContext* con) { +static inline void flipDepthIfRequired(float* depth, mjrRect viewport, const mjrContext* con) { + if (con->readDepthMap == mjDEPTH_ZERONEAR) { + int npixel = viewport.width * viewport.height; + for (int i = 0; i < npixel; i++) { + depth[i] = 1.0 - depth[i]; // Reverse the reversed Z buffer + } + } else if (!mjGLAD_GL_ARB_clip_control) { + mju_warning("ARB_clip_control unavailable while mjDEPTH_ZEROFAR requested, " + "depth accuracy will be limited"); + } else if (!mjGLAD_GL_ARB_depth_buffer_float) { + mju_warning("ARB_depth_buffer_float unavailable while mjDEPTH_ZEROFAR requested, " + "depth accuracy will be limited"); + } +} + + + // read pixels from current OpenGL framebuffer to client buffer // viewport is in OpenGL framebuffer; client buffer starts at (0,0) void mjr_readPixels(unsigned char* rgb, float* depth, @@ -115,6 +133,7 @@ void mjr_readPixels(unsigned char* rgb, float* depth, if (depth) { glReadPixels(viewport.left, viewport.bottom, viewport.width, viewport.height, GL_DEPTH_COMPONENT, GL_FLOAT, depth); + flipDepthIfRequired(depth, viewport, con); } } @@ -159,6 +178,7 @@ void mjr_readPixels(unsigned char* rgb, float* depth, if (depth) { glReadPixels(viewport.left, viewport.bottom, viewport.width, viewport.height, GL_DEPTH_COMPONENT, GL_FLOAT, depth); + flipDepthIfRequired(depth, viewport, con); } // restore currentBuffer @@ -432,7 +452,6 @@ void mjr_text(int font, const char* txt, const mjrContext* con, static int draw_overlay(int font, mjrRect viewport, int skip, int gridpos, float red, float green, float blue, const char* overlay, const mjrContext* con) { - int pos, ncthis, nc, nr, W, H, flg_big = (font == mjFONT_BIG); int PAD = 5, sz = mjMIN(mjMAXOVERLAY, (int)strlen(overlay)); char text[mjMAXOVERLAY]; diff --git a/src/render/render_gl3.c b/src/render/render_gl3.c index 17ff239f..abe2a8f5 100644 --- a/src/render/render_gl3.c +++ b/src/render/render_gl3.c @@ -580,6 +580,9 @@ static void initGL3(const mjvScene* scn, const mjrContext* con) { // common options glDisable(GL_BLEND); glEnable(GL_NORMALIZE); + if (mjGLAD_GL_ARB_clip_control) { + glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); + } glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); if (scn->flags[mjRND_CULL_FACE]) { @@ -588,11 +591,11 @@ static void initGL3(const mjvScene* scn, const mjrContext* con) { glDisable(GL_CULL_FACE); } glShadeModel(GL_SMOOTH); - glDepthFunc(GL_LEQUAL); + glDepthFunc(GL_GEQUAL); glDepthRange(0, 1); glAlphaFunc(GL_GEQUAL, 0.99f); glClearColor(0, 0, 0, 0); - glClearDepth(1); + glClearDepth(0); glClearStencil(0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); @@ -682,6 +685,15 @@ static void setView(int view, mjrRect viewport, const mjvScene* scn, const mjrCo // set projection glMatrixMode(GL_PROJECTION); glLoadIdentity(); + if (mjGLAD_GL_ARB_clip_control) { + // reverse Z rendering mapping [znear, zfar] -> [1, 0] (ndc) + glTranslatef(0.0f, 0.0f, 0.5f); + glScalef(1.0f, 1.0f, -0.5f); + } + else { + // reverse Z rendering mapping without shift [znear, zfar] -> [1, -1] (ndc) + glScalef(1.0f, 1.0f, -1.0f); + } glFrustum(cam.frustum_center - halfwidth, cam.frustum_center + halfwidth, cam.frustum_bottom, @@ -757,9 +769,15 @@ void mjr_render(mjrRect viewport, mjvScene* scn, const mjrContext* con) { float biasMatrix[16] = { 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.5f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f + 0.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.0f, 1.0f }; + if (!mjGLAD_GL_ARB_clip_control) { + // account for conversion from ndc to window coordinates + biasMatrix[2*4+2] = 0.5; + biasMatrix[3*4+2] = 0.5; + } + float tempMatrix[16], textureMatrix[16]; mjvGeom *thisgeom, tempgeom; mjvLight *thislight; @@ -884,7 +902,7 @@ void mjr_render(mjrRect viewport, mjvScene* scn, const mjrContext* con) { } // render with stereo - for (int view=(stereo?0:-1); view < (stereo?2:0); view++) { + for (int view = (stereo ? 0 : -1); view < (stereo ? 2 : 0); view++) { // change drawbuffer for QUADBUFFERED stereo if (stereo == mjSTEREO_QUADBUFFERED) { if (con->windowDoublebuffer) { @@ -1107,6 +1125,15 @@ void mjr_render(mjrRect viewport, mjvScene* scn, const mjrContext* con) { // set projection: from light viewpoint glMatrixMode(GL_PROJECTION); glLoadIdentity(); + if (mjGLAD_GL_ARB_clip_control) { + // reverse Z rendering mapping [znear, zfar] -> [1, 0] (ndc) + glTranslatef(0.0f, 0.0f, 0.5f); + glScalef(1.0f, 1.0f, -0.5f); + } + else { + // reverse Z rendering mapping without shift [znear, zfar] -> [1, -1] (ndc) + glScalef(1.0f, 1.0f, -1.0f); + } if (thislight->directional) { glOrtho(-con->shadowClip, con->shadowClip, -con->shadowClip, con->shadowClip, @@ -1127,7 +1154,8 @@ void mjr_render(mjrRect viewport, mjvScene* scn, const mjrContext* con) { glBindFramebuffer(GL_FRAMEBUFFER, con->shadowFBO); glDrawBuffer(GL_NONE); glClear(GL_DEPTH_BUFFER_BIT); - glViewport(1, 1, con->shadowSize-2, con->shadowSize-2); // avoid infinite shadows from edges + glViewport( + 1, 1, con->shadowSize-2, con->shadowSize-2); // avoid infinite shadows from edges glCullFace(GL_FRONT); glShadeModel(GL_FLAT); glDisable(GL_LIGHTING); diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index d190ce3a..9133f445 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -406,6 +406,10 @@ public enum mjtFramebuffer : int{ mjFB_WINDOW = 0, mjFB_OFFSCREEN = 1, } +public enum mjtDepthMap : int{ + mjDEPTH_ZERONEAR = 0, + mjDEPTH_ZEROFAR = 1, +} public enum mjtFontScale : int{ mjFONTSCALE_50 = 50, mjFONTSCALE_100 = 100, @@ -5596,6 +5600,7 @@ public unsafe struct mjrContext_ { public int windowDoublebuffer; public int currentBuffer; public int readPixelFormat; + public int readDepthMap; } [StructLayout(LayoutKind.Sequential)]