mosra / magnum

Lightweight and modular C++11 graphics middleware for games and data visualization
https://magnum.graphics/
Other
4.76k stars 438 forks source link

gridWireframe3D is missing some horizontal lines on odd subdivisions #522

Closed diwant closed 2 years ago

diwant commented 3 years ago

The issue I see is that the wireframe grid seems to be missing some horizontal lines when the y subdivisions are odd.

I am using an orthographic camera and a Flat3D shader. This is on an ubuntu 20.04 configuration with compiled 2020.06 Corrade, Magnum, and Magnum Plugins. Images exported using AnyImageConverter.

Some subdivision counts (such as 11 y subdivisions) are missing more than one horizontal line. I've tried exporting the image as tga and png but I get the same render.

Here are the grids I see for each of the subdivisions: _mesh = Magnum::MeshTools::compile(Magnum::Primitives::grid3DWireframe({2, 2})); 2x2

_mesh = Magnum::MeshTools::compile(Magnum::Primitives::grid3DWireframe({2, 3})); 2x3

_mesh = Magnum::MeshTools::compile(Magnum::Primitives::grid3DWireframe({3, 2})); 3x2

_mesh = Magnum::MeshTools::compile(Magnum::Primitives::grid3DWireframe({3, 3})); 3x3

And here are a few square grids to show the difference between the odd and even values. Horizontal lines are the only ones missing and they are only missing for an odd number of y subdivisions.

_mesh = Magnum::MeshTools::compile(Magnum::Primitives::grid3DWireframe({4, 4})); 4x4

_mesh = Magnum::MeshTools::compile(Magnum::Primitives::grid3DWireframe({5, 5})); 5x5

_mesh = Magnum::MeshTools::compile(Magnum::Primitives::grid3DWireframe({9, 9})); 9x9

_mesh = Magnum::MeshTools::compile(Magnum::Primitives::grid3DWireframe({10, 10})); 10x10

_mesh = Magnum::MeshTools::compile(Magnum::Primitives::grid3DWireframe({11, 11})); 11x11

_mesh = Magnum::MeshTools::compile(Magnum::Primitives::grid3DWireframe({13, 13})); 13x13

mosra commented 3 years ago

Hi!

This seems like an issue with aliasing / image scaling rather than with the primitive itself. Such behavior doesn't even make sense algorithmically, there's just a simple 2D loop that generates the lines and there's no logic that could cause it to throw away 4th, 7th and 10th line. For a quick sanity test I just loaded the 2x3 wireframe grid primitive in the magnum-player utility and it seems alright:

magnum-player -I PrimitiveImporter -i "grid3DWireframe/subdivisions=2 3" /dev/null

image

The problem in your case is that when scaling down a higher-resolution image (rendering to a large framebuffer and then using it to texture something smaller, or having a WebGL canvas in a higher resolution than the area on the screen, or viewing remote desktop on a smaller resolution window etc., not exactly sure what's your case), some pixel rows/columns might get completely thrown away and some will get averaged, which tends to make a mess out of any line art. The only reliable solution is to ensure the framebuffer is rendered at the exact same resolution as it's later viewed at.


(Even though this turned out to not be a bug in the end, I feel like I should point out your issue reporting skills. Clear, concise, and with exactly the right amount of information to immediately act on. Good job, keep that up!)

diwant commented 3 years ago

Thank you for the quick response, and for the lovely compliment! I appreciate the kind words!

As to aliasing, that seems like it could be the issue, but I'm not able to understand where it happens. I'll continue looking into it, but here are a few more details if you are curious.

On my local mac all the lines show, no problem. I see this issue in my Dockerized Ubuntu.

I'm rendering to a Framebuffer, like so,

  color = new Magnum::GL::Texture2D;
  color->setStorage(1, Magnum::GL::TextureFormat::RGBA8, size);

  depthStencil = new Magnum::GL::Renderbuffer{};
  depthStencil->setStorage(Magnum::GL::RenderbufferFormat::Depth24Stencil8,
                           size);

  fb = new Magnum::GL::Framebuffer{{{}, size}};
  fb->attachTexture(Magnum::GL::Framebuffer::ColorAttachment{0},
                                  *color, 0);
  fb->attachRenderbuffer(Magnum::GL::Framebuffer::BufferAttachment::DepthStencil,
                                  *depthStencil);

  Magnum::GL::Renderer::enable(Magnum::GL::Renderer::Feature::Blending);
  Magnum::GL::Renderer::setBlendFunction(
      Magnum::GL::Renderer::BlendFunction::SourceAlpha, /* or SourceAlpha for non-premultiplied */
      Magnum::GL::Renderer::BlendFunction::OneMinusSourceAlpha);

Here size is a const Magnum::Vector2i for which I pass in Magnum::Vector2i{256, 256}

Later, I pick up the image from that framebuffer with this line,

Magnum::Image2D img = fb->read(fb->viewport(), {Magnum::PixelFormat::RGBA8Unorm});

I check this image against a pre-saved expected image in my TestSuite with this line:

CORRADE_COMPARE_WITH(img, Corrade::Utility::formatString("expected/{}.tga", testName), (Magnum::DebugTools::CompareImageToFile{1.5f, 0.01f}));

I use the --save-diagnostic flag from the Corrade TestSuite to save the new image to my set of expected images, and that image is what I've shared above for each grid.

In this method, the image saved is the same size as my framebuffer. I'm not sure why the aliasing is happening.

mosra commented 3 years ago

Hm, I still have no idea why this could happen. If I remember correctly, the driver should not just throw lines away like that, no matter how the rounding and pixel positioning ends up. What's the driver used? With some (such as SVGA3D) I could suspect something fishy goin on in the background.

Does this happen if you don't have blending enabled? How is the shader set up? This could happen also if you'd have Flag::AlphaMask enabled for example, lines that are on the boundary of two pixels could have opacity around 0.5 and their fragments could get discarded.

mosra commented 2 years ago

Closing as this issue is outside of the control of this library, being a driver bug most probably, and thus I can't really do much about fixing it.