17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-10 07:31:30 +01:00

fix(gl46): read back the editor cursor depth and correct the depth pass bias

GL46 read the depth under the editor cursor in the depth pass of the scene, drawn with glPolygonOffset(-1, -1), and took it as it was: the ground came out about a pixel row farther than it lies (metres at a grazing angle). Same correction as 7078edf for vulkan: a 3x3 block read back, the slope and the unit of the bias taken back. The depth is also worked out in the view of the frame it was asked in, and the point kept in world space, so a moved cursor or camera does not shift it. See #238.
This commit is contained in:
miloszunio
2026-11-01 13:12:01 +01:00
committed by GitHub
parent 60f34fd6bf
commit 3a4b878304

View File

@@ -407,7 +407,7 @@ bool opengl46_renderer::Init(GLFWwindow *Window)
if (Global.iMultisampling)
{
m_depth_pointer_rb = std::make_unique<gl::renderbuffer>();
m_depth_pointer_rb->alloc(depth_pointer_format, 1, 1);
m_depth_pointer_rb->alloc(depth_pointer_format, 3, 3); // the pixel under the cursor and the ones around it
m_depth_pointer_fb = std::make_unique<gl::framebuffer>();
m_depth_pointer_fb->attach(*m_depth_pointer_rb, GL_DEPTH_ATTACHMENT);
@@ -5496,53 +5496,100 @@ void opengl46_renderer::Pick_Node_Callback(std::function<void(scene::basic_node
m_node_pick_requests.push_back(callback);
}
// the position of the cursor of the editor in the scene: the depth around it read back from an earlier frame, worked out in
// the view that frame was drawn in (the cursor and the camera may have moved since), and kept in world space
glm::dvec3 opengl46_renderer::Update_Mouse_Position()
{
if (!m_depth_pointer_pbo->is_busy())
{
// determine point to examine
glm::vec2 mousepos = Global.cursor_pos;
mousepos.y = Global.window_size.y - mousepos.y; // cursor coordinates are flipped compared to opengl
// the depths asked for before, around where the cursor was then
auto const count{m_cursorblocksize.x * m_cursorblocksize.y};
std::array<float, 9> depths{};
if ((count > 0) && (count <= static_cast<int>(depths.size())) && m_depth_pointer_pbo->read_data(m_cursorblocksize.x, m_cursorblocksize.y, depths.data(), 4))
{
auto const centre{m_cursorposition - m_cursorblockcorner};
auto const depth_at = [&](glm::ivec2 const Pixel) {
if ((Pixel.x < 0) || (Pixel.y < 0) || (Pixel.x >= m_cursorblocksize.x) || (Pixel.y >= m_cursorblocksize.y))
{
return 0.f;
}
return depths[static_cast<std::size_t>(Pixel.y * m_cursorblocksize.x + Pixel.x)];
};
float pointdepth{depth_at(centre)};
if (m_cursorbiased && (pointdepth > 0.f))
{
// read in the depth pass of the scene (Render_Scene_Depth()), which draws everything a little farther than it lies
// (glPolygonOffset(-1, -1)), so the colour pass passes on the same surface. the slope of the depth grows towards the
// horizon, and the ground seen at a grazing angle came out a pixel's worth of ground farther than it lies, metres off
// some way from the camera. the slope is taken back as in the vulkan renderer: the pixels next to it on the same triangle
// are moved by the same bias, so their difference is the slope; of the two sides the smaller, which is less likely
// to lie on another surface, and the larger of the two axes, the one implementations use for m. then the unit, r
auto const slope = [&](glm::ivec2 const Step) {
auto result{-1.f};
for (auto const side : {-1, 1})
{
auto const other{depth_at(centre + Step * side)};
if (other > 0.f)
{
auto const difference{std::abs(other - pointdepth)};
result = (result < 0.f ? difference : std::min(result, difference));
}
}
return std::max(result, 0.f);
};
pointdepth += std::max(slope({1, 0}), slope({0, 1})) + std::ldexp(1.f, std::ilogb(pointdepth) - 23);
}
m_mousehit = pointdepth > 0.0f;
if (m_mousehit)
{
auto const offset{glm::unProjectZO(glm::vec3(m_cursorposition, pointdepth), m_cursorview, m_cursorprojection, m_cursorviewport)};
m_worldmouseposition = m_cursorcamera + glm::dvec3{offset};
m_mouseknown = true;
}
}
glm::ivec2 bufferpos;
bufferpos = glm::ivec2(mousepos * glm::vec2(Global.gfx_framebuffer_width, Global.gfx_framebuffer_height) / glm::vec2(Global.window_size));
bufferpos = glm::clamp(bufferpos, glm::ivec2(0, 0), glm::ivec2(Global.gfx_framebuffer_width - 1, Global.gfx_framebuffer_height - 1));
float pointdepth = std::numeric_limits<float>::max();
m_depth_pointer_pbo->read_data(1, 1, &pointdepth, 4);
// ask for the depths around where the cursor is now
glm::vec2 mousepos = Global.cursor_pos;
mousepos.y = Global.window_size.y - mousepos.y; // cursor coordinates are flipped compared to opengl
glm::ivec2 const extent{Global.gfx_framebuffer_width, Global.gfx_framebuffer_height};
auto bufferpos{glm::ivec2(mousepos * glm::vec2(extent) / glm::max(glm::vec2(Global.window_size), glm::vec2(1.f)))};
bufferpos = glm::clamp(bufferpos, glm::ivec2(0, 0), extent - 1);
// the pixels around the one under the cursor too, for the slope of the depth there
auto const blocksize{glm::clamp(extent, glm::ivec2(1), glm::ivec2(3))};
auto const blockcorner{glm::clamp(bufferpos - 1, glm::ivec2(0), extent - blocksize)};
if (!Global.iMultisampling)
{
m_depth_pointer_pbo->request_read(bufferpos.x, bufferpos.y, 1, 1, 4, GL_DEPTH_COMPONENT, GL_FLOAT);
m_depth_pointer_pbo->request_read(blockcorner.x, blockcorner.y, blocksize.x, blocksize.y, 4, GL_DEPTH_COMPONENT, GL_FLOAT);
}
else if (Global.gfx_skippipeline)
{
gl::framebuffer::blit(nullptr, m_depth_pointer_fb.get(), bufferpos.x, bufferpos.y, 1, 1, GL_DEPTH_BUFFER_BIT, 0);
gl::framebuffer::blit(nullptr, m_depth_pointer_fb.get(), blockcorner.x, blockcorner.y, blocksize.x, blocksize.y, GL_DEPTH_BUFFER_BIT, 0);
m_depth_pointer_fb->bind();
m_depth_pointer_pbo->request_read(0, 0, 1, 1, 4, GL_DEPTH_COMPONENT, GL_FLOAT);
m_depth_pointer_pbo->request_read(0, 0, blocksize.x, blocksize.y, 4, GL_DEPTH_COMPONENT, GL_FLOAT);
m_depth_pointer_fb->unbind();
}
else
{
gl::framebuffer::blit(m_viewports.front()->msaa_fb.get(), m_depth_pointer_fb.get(), bufferpos.x, bufferpos.y, 1, 1, GL_DEPTH_BUFFER_BIT, 0);
gl::framebuffer::blit(m_viewports.front()->msaa_fb.get(), m_depth_pointer_fb.get(), blockcorner.x, blockcorner.y, blocksize.x, blocksize.y, GL_DEPTH_BUFFER_BIT, 0);
m_depth_pointer_fb->bind();
m_depth_pointer_pbo->request_read(0, 0, 1, 1, 4, GL_DEPTH_COMPONENT, GL_FLOAT);
m_depth_pointer_pbo->request_read(0, 0, blocksize.x, blocksize.y, 4, GL_DEPTH_COMPONENT, GL_FLOAT);
m_viewports.front()->msaa_fb->bind();
}
if (pointdepth != std::numeric_limits<float>::max())
{
m_mousehit = pointdepth > 0.0f;
if (pointdepth > 0.0f)
m_worldmousecoordinates = glm::unProjectZO(glm::vec3(bufferpos, pointdepth), glm::mat4(glm::mat3(m_colorpass.pass_camera.modelview())), m_colorpass.pass_camera.projection(),
glm::vec4(0, 0, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height));
}
// and the view it's drawn in, for when the depths come back
m_cursorposition = bufferpos;
m_cursorblockcorner = blockcorner;
m_cursorblocksize = blocksize;
m_cursorbiased = m_scenedepthpass;
m_cursorview = glm::mat4(glm::mat3(m_colorpass.pass_camera.modelview()));
m_cursorprojection = m_colorpass.pass_camera.projection();
m_cursorcamera = m_colorpass.pass_camera.position();
m_cursorviewport = glm::vec4(0, 0, extent.x, extent.y);
}
return m_colorpass.pass_camera.position() + glm::dvec3{m_worldmousecoordinates};
return m_colorpass.pass_camera.position() + Mouse_Position();
}
void opengl46_renderer::Update(double const Deltatime)