raysan5/raylib

[rshapes] `DrawRectangleLines()` lines flickering when zooming out due to `RL_LINES` vs `RL_QUADS`

arceryz opened this issue · 3 comments

Please, before submitting a new issue verify and check:

  • I tested it on latest raylib version from master branch
  • I checked there is no similar issue already reported
  • I checked the documentation on the wiki
  • My code has no errors or misuse of raylib

Issue description

When working with DrawRectangleLines, the behavior as expected is to have the rectangle drawn with RL_LINES, i.e pure OpenGL lines with no artefacts at small distances like DrawLine. However, DrawRectangleLines (and possible other fake *Lines drawing methods) are rendered with Quads when SUPPORT_QUADS_DRAW_MODE is true. While I understand that this may be for avoiding draw order issues (as stated in the source comment), intuitively one would expect that *Lines methods are drawn with pure OpenGL lines to avoid artefacts.

So I would recommend not mixing RL_LINES and Quads when possible and to be clear which or the other any function in rshapes.c is using.

I encountered this when creating an infinite grid when zooming in and out on a tool, using DrawRectangleLines would have the rectangle flickering at high levels of zoom, because the quads become sub-pixel sized. It is not a big issue, because I can just implement a custom DrawRectangleLines that draws 4 lines instead (as expected). But I think it may be better to have all the *Lines function (except the Ex versions with lineWidth) to be rendered always with RL_LINES and not quads.

It is in particular this piece of code in rshapes.c:

// Draw rectangle outline
// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues
void DrawRectangleLines(int posX, int posY, int width, int height, Color color)
{
#if defined(SUPPORT_QUADS_DRAW_MODE)
    DrawRectangle(posX, posY, width, 1, color);
    DrawRectangle(posX + width - 1, posY + 1, 1, height - 2, color);
    DrawRectangle(posX, posY + height - 1, width, 1, color);
    DrawRectangle(posX, posY + 1, 1, height - 2, color);
#else
    rlBegin(RL_LINES);
        rlColor4ub(color.r, color.g, color.b, color.a);
        rlVertex2f(posX + 1, posY + 1);
        rlVertex2f(posX + width, posY + 1);
        ...

See the gif for a demonstration of the issue and how it may work counterintuitively for Raylib users. (Expectation: rectangle remains a rectangle at big distance).

Environment

Windows, vanilla Raylib 5.0 C

Issue Screenshot

The left square is the expected behavior (4 lines) and the right square is DrawRectangleLines.
The camera is zoomed out progressively. With greater zoom levels, DrawRectangleLines becomes basically pointless.
Bug

Code Example

It is python code because I wrote quickly, but it is nothing special and would look the same in C.

from pyray import *

init_window(800, 800, "Bug")

camera: Camera2D = Camera2D(Vector2(0, 0), Vector2(0, 0), 0, 1)

while not window_should_close():
    begin_drawing()
    begin_mode_2d(camera)
    clear_background(BLACK)

    draw_line(10, 10, 210, 10, WHITE)
    draw_line(210, 10, 210, 210, WHITE)
    draw_line(210, 210, 10, 210, WHITE)
    draw_line(10, 210, 10, 10, WHITE)

    draw_rectangle_lines(300, 10, 200, 200, WHITE)

    camera.zoom -= get_frame_time() * 0.1
    end_mode_2d()
    end_drawing()

@arceryz Good catch! And thanks for reporting! I expected OpenGL driver to manage that properly on its side... but effectively lines rendering is completely different than triangles... It probably can't be addressed from raylib side but just be properly documented.

@raysan5 Would it be an option to have all the pure *Lines drawing methods (expect the *Ex variants) just implemented with RL_LINES, and the *LinesEx methods, which have thickness, be implemented using triangles/quads? I think this would make most sense, since of course lines with thickness can't avoid this issue so we can leave *LinesEx untouched. This way you can just say that the *Lines drawing methods are safe at faraway distances.

@arceryz DrawRectangleLines() has been reviewed with the proposed fix BUT notice that this change can derive in other unexpected issues: When drawing with GL_LINES first pixel position calculation could be offseted by one pixel depending on the GPU/drivers, I'm afraid it's an impossible issue to be fixed.

Also note that DrawRectangleRoundedLines() now should be reviewed for consistency with other Draw*Lines() functions.