raysan5 / raylib

A simple and easy-to-use library to enjoy videogames programming
http://www.raylib.com
zlib License
22.12k stars 2.23k forks source link

[shapes] Missing pixels on `DrawCircle()` with small radius and window `FLAG_MSAA_4X_HINT` active #3247

Closed evanrinehart closed 1 year ago

evanrinehart commented 1 year ago
#include <raylib.h>

int main(int argc, char* argv[]){

    SetConfigFlags(FLAG_MSAA_4X_HINT);
    InitWindow(800, 600, "TEST CASE");

    while(!WindowShouldClose()){
        BeginDrawing();
        ClearBackground(WHITE);
        DrawCircle(400.5, 300.5, 8, BLACK);
        DrawCircle(528.0, 172.0, 8, BLACK);
        EndDrawing();
    }

    CloseWindow();

    return 0;
}

image

This is on latest Ubuntu with NVIDIA GeForce GTX 1080 Ti. It seems like the constituents of the disc don't line up exactly and MSAA_4X has found those spots not covered.

evanrinehart commented 1 year ago

I narrowed it down to the rshapes.c routine DrawCircleSector. Specifically the SUPPORT_QUADS_DRAW_MODE branch of that code. When I force the triangles version of DrawCircleSector to draw my discs, the visual errors go away.

Something is going wrong when trying to draw circles with quads

raysan5 commented 1 year ago

@evanrinehart I'm aware of this issue. I don't know why it happens...

krupitskas commented 1 year ago

Can it be OpenGL bug? However I've tried to compile raylib with 4.3 opengl, no difference.

I've briefly took a look: When we are drawing to MSAA target samples look like this: Sample 0:

image

Sample 1:

image

Other samples are having same issues, thats why resolve does look like this:

image

However I've checked shader and looks like issue with sampling of provided texture in fragment shader

    vec4 texelColor = texture(texture0, fragTexCoord);   
    finalColor = texelColor*colDiffuse*fragColor;        

I see that texture0 is R8G8_Unorm with RRRG swizzling, so I've tried to replicate same sampling in the shader

vec4 texelColor = texture(texture0, fragTexCoord).rrrg;   

And its gone...

image

With bigger circles it happens too

image
raysan5 commented 1 year ago

@krupitskas thanks for the investigation and the detailed explanation. I'm not sure about the reasons for this issue but my guess is that could be related to the rasterization of the individual indexed quads that become 2 triangles of the circle each. It just approximates the mathematical defined borders to pixels and on some cases it choses not to add a pixel.

The solution to this issue is drawing using plain triangles or a triangle-strip but it's not possible to get it with quads...

It's a guess but I don't really understand the issue, probably I don't really understand what MSAAx4 really does internally (beside the theorical definition).

raysan5 commented 1 year ago

I'm afraid this issue is out-of-scope of raylib. It seems it's more related to the driver implementation of OpenGL.

evanrinehart commented 1 year ago

I got the exact effect on my NVIDIA linux box and macbook air. I think a different way of drawing a circle with quads might help.

Currently I'm disabling SUPPORT_QUADS_DRAW_MODE in config.h which is enabled by default. It's a shame the default behavior is bugged.

I might be able to look at an alternative circle-with-quads drawing strategy at some point. I'll have to remember this closed issue.

evanrinehart commented 1 year ago

Well after a few minutes of messing around I noticed that disabling the 1x1 pixel texture that goes with each slice of DrawCircleSector, the erroneous results seem to go away. I'll have to do more testing.

In the mean time, what is the purpose of the dummy texture used in all the QUAD mode rshapes routines?

raysan5 commented 1 year ago

I might be able to look at an alternative circle-with-quads drawing strategy at some point.

I tried this for a couple of hours and I couldn't find a solution.

what is the purpose of the dummy texture used in all the QUAD mode rshapes routines?

because raylib uses the same internal shader for shapes drawing and textures drawing, and that shader requires a texture. Also note that by default the font texture is used for shapes drawing, not the 1x1 pixel white texture.

raysan5 commented 1 year ago

I'm reopening it to ask the X experts about it, maybe someone could provide some more info about the issue.

mausimus commented 1 year ago

I think it's texel bleeding, the only material difference between triangle and quad approaches are tex coords (constant w/ triangles, varied w/ quads). I think there's already some padding being added around the shape area on the font texture but it looks not enough for all MSAA scenarios (an MSAA sample of a small shape could resolve to more than 1 texel away). Setting texShapesRec to width 1 and height 1 (or 0 for that matter) fixes it for example.

raysan5 commented 1 year ago

@mausimus you are right! thank you very much for finding it! Indeed there is a 1px white padding around the texture piece used for drawing but it seems it's not enough for MSAAx4! Reviewing it right now!

raysan5 commented 1 year ago

Not completed yet, just noticed that as smaller is the circle, the bigger should be the padding... very weird issue...

EDIT: It works, it was a bug on my fixing code. Just noted that padding should be bigger as smaller become the quads.

evanrinehart commented 1 year ago

OH! The font texture. Of course. I'm glad you guys figured it out. My circles look great after pulling master and rebuilding.

mausimus commented 1 year ago

You're welcome @raysan5 @evanrinehart ! Yes the problematic scenario is when a triangle doesn't cover the center of a pixel but covers one or more MSAA samples within it - the pixel shader is run only once using center of the pixel (which being outside of shape extrapolates UV to out of bounds) and its color output is used by all MSAA samples.

verioneuvien commented 5 months ago

This bug reappears in the DrawRectangleRounded function:

R: Radius (converted to roundness for DrawRectangleRounded's parameter, which in turn is converted back to radius) S: Segments (for DrawRectangleRounded's parameter) image

Image generated with the below code

#include <raylib.h>

int main()
{
    SetConfigFlags(FLAG_MSAA_4X_HINT);
    InitWindow(800, 400, "Rounded Rectangle Pixel Awkward\n");

    int segmentss[4] = { 1, 30, 45, 90 };
    int radiuss[4] = { 5, 10, 20, 40 };

    while (!WindowShouldClose())
    {
        BeginDrawing();
        ClearBackground(WHITE);

        for (int s = 0; s < 4; s++)
        {
            for (int r = 0; r < 4; r++)
            {
                // Calculate position and stuff
                Rectangle rec = { s * 200.0f + 20.0f, r * 100.0f + 10.0f, 160.0f, 80.0f };
                float roundness = radiuss[r] * 2.0f / rec.height; // WHY??

                // Main thing
                DrawRectangleRounded(rec, roundness, segmentss[s], BLACK);

                // Draw text on it
                const char *text = TextFormat("R:%d,S:%d", radiuss[r], segmentss[s]);
                float textWidth = MeasureText(text, 20);
                DrawText(text, rec.x + (rec.width - textWidth) / 2, rec.y + (rec.height - 20) / 2, 20, WHITE);
            }
        }

        EndDrawing();
    }

    CloseWindow();
}

System: Arch Linux x86_64 Raylib version: just pulled from master

Raylib logs:

INFO: Initializing raylib 5.1-dev INFO: Platform backend: DESKTOP (GLFW) INFO: Supported raylib modules: INFO: > rcore:..... loaded (mandatory) INFO: > rlgl:...... loaded (mandatory) INFO: > rshapes:... loaded (optional) INFO: > rtextures:. loaded (optional) INFO: > rtext:..... loaded (optional) INFO: > rmodels:... loaded (optional) INFO: > raudio:.... loaded (optional) INFO: DISPLAY: Trying to enable MSAA x4 INFO: DISPLAY: Device initialized successfully INFO: > Display size: 1920 x 1080 INFO: > Screen size: 800 x 400 INFO: > Render size: 800 x 400 INFO: > Viewport offsets: 0, 0 WARNING: GLFW: Error: 65548 Description: Wayland: The platform does not support setting the window position INFO: GLAD: OpenGL extensions loaded successfully INFO: GL: Supported extensions count: 230 INFO: GL: OpenGL device information: INFO: > Vendor: Intel INFO: > Renderer: Mesa Intel(R) Xe Graphics (TGL GT2) INFO: > Version: 4.6 (Core Profile) Mesa 24.0.5-arch1.1 INFO: > GLSL: 4.60 INFO: GL: VAO extension detected, VAO functions loaded successfully INFO: GL: NPOT textures extension detected, full NPOT textures supported INFO: GL: DXT compressed textures supported INFO: GL: ETC2/EAC compressed textures supported INFO: GLFW platform: Wayland INFO: PLATFORM: DESKTOP (GLFW): Initialized successfully INFO: TEXTURE: [ID 1] Texture loaded successfully (1x1 | R8G8B8A8 | 1 mipmaps) INFO: TEXTURE: [ID 1] Default texture loaded successfully INFO: SHADER: [ID 1] Vertex shader compiled successfully INFO: SHADER: [ID 2] Fragment shader compiled successfully INFO: SHADER: [ID 3] Program shader loaded successfully INFO: SHADER: [ID 3] Default shader loaded successfully INFO: RLGL: Render batch vertex buffers loaded successfully in RAM (CPU) INFO: RLGL: Render batch vertex buffers loaded successfully in VRAM (GPU) INFO: RLGL: Default OpenGL state initialized successfully INFO: TEXTURE: [ID 2] Texture loaded successfully (128x128 | GRAY_ALPHA | 1 mipmaps) INFO: FONT: Default font loaded successfully (224 glyphs)

Cotterzz commented 5 months ago

I've been looking at this and I am wondering if a different method of drawing curves might give improved rendering? It's an odd way around the problem, but moving the lines to the edge instead of the centre might look better: For illustration: screenrec007 This is with MSAA turned on, though I'm on windows, no idea if this is the way to repeat the bug. Made it small and black for comparison. I'm using a custom mesh object: screenrec001 Example code here: https://github.com/Cotterzz/Raylib/blob/master/examples/alt_corner/rounded_corner_alternative.c

Also, let me know if this is more use drawn as a series of quads.

Cotterzz commented 5 months ago

This was an unexpected find, but it looks like rendering corners this way might be more performant: https://www.humus.name/index.php?page=News&ID=228 He's doing something very similar with whole circles. It's 15 years old, so I don't know if it still applies, but this was the only reference I could find to a similar method.

raysan5 commented 5 months ago

@Cotterzz This is an interesting approach, I didn't know about it! It could be an alternative but the code complexity required seems quite high compared to current solution.

Cotterzz commented 5 months ago

Yes, and it's a very specific use case for 2D only, so I can't really say if it's worth implementing. My code is probably very overcomplicated as it was my first attempt at the algorithm, I'm not great with C, and I should probably have used recursion - so if you did want to use it, I'm sure it can be reduced.