Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix draw_rect #32657

Merged
merged 1 commit into from
Oct 26, 2019
Merged

Fix draw_rect #32657

merged 1 commit into from
Oct 26, 2019

Conversation

ptrojahn
Copy link
Contributor

@ptrojahn ptrojahn commented Oct 8, 2019

OpenGL uses the diamond exit rule to rasterize lines. If we don't shift
the points down and to the right by 0.5, the line can sometimes miss a
pixel when it shouldn't. The final fragment of a line isn't drawn. By
drawing the lines clockwise, we can avoid a missing pixel in the rectangle.
See section 3.4.1 in the OpenGL 1.5 specification.
Fixes #32279

@ptrojahn ptrojahn requested a review from reduz as a code owner October 8, 2019 19:36
@clayjohn
Copy link
Member

clayjohn commented Oct 8, 2019

Changes look good. Can you check that the spec is the same for 2.1 and 3.3?

Copy link
Member

@Calinou Calinou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be good to write a comment to explain why 0.5 is being added to the values 🙂

p_rect.position + Point2(0, offset),
p_rect.position + Size2(0, p_rect.size.height - offset),
p_rect.position + Point2(p_rect.size.width, offset),
p_rect.position + Size2(p_rect.size.width, p_rect.size.height - offset),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order used to be "add Point2 then add Size2, but now it's no longer consistent, you have Point2/Size2 here, but Size2/Point2 for the next two calls.
It's the same typedef so it doesn't matter so much, but the inconsistency looks weird.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Size2 for all probably makes more sense semantically.

@akien-mga akien-mga added this to the 3.2 milestone Oct 9, 2019
@ptrojahn
Copy link
Contributor Author

ptrojahn commented Oct 9, 2019

The idea of diamond exit is explained here: https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-rasterizer-stage-rules. (This is for directx, but it should be the same in OpenGL.)To make sure that the first pixel of a line is drawn, it should start at the x in the center(0.5, 0.5) of the pixel area. If we start to draw at the top left(0,0), the line might never cut the diamond pattern of the first pixel, leaving a gap at the corner. This works fine on my nvidia GPU, but on my integrated intel haswell gpu it doesn't(At least the missing pixel is now consistently at the top right). I don't think it's possible to create cross platform pixel perfect lines with OpenGL. The algorithm is in the specification for OpenGL 2.1 and 3.3, but vendors are allowed to use slightly different algorithms. It might be interesting if someone with a AMD GPU or more modern intel GPU could test this.

@hbina
Copy link
Contributor

hbina commented Oct 10, 2019

More resources here, explained pretty succinctly. http://graphics-software-engineer.blogspot.com/2012/04/rasterization-rules.html

@akien-mga
Copy link
Member

I think it'd be good to write a comment to explain why 0.5 is being added to the values

Changes seem good to me, but I agree that adding a short comment in both GLES2 and GLES3 before the verts definition would be good to make things explicit.

OpenGL uses the diamond exit rule to rasterize lines. If we don't shift
the points down and to the right by 0.5, the line can sometimes miss a
pixel when it shouldn't. The final fragment of a line isn't drawn. By
drawing the lines clockwise, we can avoid a missing pixel in the rectangle.
See section 3.4.1 in the OpenGL 1.5 specification.
Fixes godotengine#32279
@ptrojahn
Copy link
Contributor Author

For future reference:
image

@akien-mga akien-mga merged commit 3eb8bd0 into godotengine:master Oct 26, 2019
@akien-mga
Copy link
Member

Thanks!

@AlexHolly
Copy link
Contributor

It this change causing this issue?

Fills my control fully - all good

func _draw():
	draw_rect(Rect2(Vector2(), Vector2(32,32)), Color.red, true)

But drawing only the outline has an offset

func _draw():
	draw_rect(Rect2(Vector2(), Vector2(32,32)), Color.red, false)

image

@ptrojahn
Copy link
Contributor Author

ptrojahn commented Nov 2, 2019

Can't reproduce with your code. Would be helpful if you could add a minimal reproduction project.

@AlexHolly
Copy link
Contributor

Can you try to scale the Control by 16,16 this is what I do in my case

@reduz
Copy link
Member

reduz commented Nov 7, 2019

I understand why this was added, but this is not the right way and it will likely need to be reverted. The reason is that lines can be used with a canvas scale and this will break them.

Researching a better solution is probably a better idea, or likely attempting to implement it in 4.0, which supplies the separate canvas matrices.

@reduz
Copy link
Member

reduz commented Nov 7, 2019

I suggest doing the pixel shifting at matrix level instead for lines.

akien-mga added a commit to akien-mga/godot that referenced this pull request Nov 7, 2019
As discussed in godotengine#32657, this can't be done here as lines can be used
with a canvas scale, and this breaks them.
A suggestion is to do the pixel shifting at matrix level instead.

Fixes godotengine#33393.
Fixes godotengine#33421.
@akien-mga
Copy link
Member

Pushing a partial revert of the line shifting in #33427, as @reduz mentioned that the clockwise draw in CanvasItem is otherwise fine.

akien-mga added a commit that referenced this pull request Nov 7, 2019
Partial revert of #32657, undoing line shifting by 0.5
victordomiciano pushed a commit to JavaryGames/godot that referenced this pull request Dec 3, 2019
As discussed in godotengine#32657, this can't be done here as lines can be used
with a canvas scale, and this breaks them.
A suggestion is to do the pixel shifting at matrix level instead.

Fixes godotengine#33393.
Fixes godotengine#33421.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missing pixel when using draw_rect
7 participants