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

The rendered result doesn't look nice #46

Closed
olliwang opened this issue Mar 4, 2014 · 29 comments
Closed

The rendered result doesn't look nice #46

olliwang opened this issue Mar 4, 2014 · 29 comments

Comments

@olliwang
Copy link
Contributor

olliwang commented Mar 4, 2014

I was trying to render circles on iPhone 5S. But the circle doesn't look smooth as a circle even in Retina display. I tried to change the mediump to highp but the result was not improved.

This picture shows the result I got, and I enlarged it 2x in Photoshop so it's more clear to see the result is not smooth.
nanovg_circle

The code I was using to render the circle is as follow.

const int kCenterX = (GetWidth() + 1) / 2;
const int kCenterY = (GetHeight() + 1) / 2;

nvgBeginPath(context);
nvgCircle(context, kCenterX, kCenterY, (GetWidth() - 1) / 2 - 2);
nvgFillColor(context, nvgRGBA(110, 201, 235, 180));
nvgFill(context);

nvgBeginPath(context);
nvgCircle(context, kCenterX, kCenterY, (GetWidth() - 1) / 2 - 1);
nvgStrokeColor(context, nvgRGBA(255, 255, 255, 255));
nvgStrokeWidth(context, 3);
nvgStroke(context);

nvgBeginPath(context);
nvgCircle(context, kCenterX, kCenterY, (GetWidth() - 1) / 2 - 7);
nvgFillColor(context, nvgRGBA(255, 255, 255, 255));
nvgFill(context);

The environment for setting OpenGL and nanovg context up is also very intuitive like this: https://github.com/ollix/moui/blob/master/moui/widgets/widget_view.cc

@olliwang olliwang closed this as completed Mar 6, 2014
@olliwang olliwang reopened this Mar 8, 2014
@olliwang
Copy link
Contributor Author

olliwang commented Mar 8, 2014

I was thinking this issue happened because I didn't create stencil render buffer but I was wrong. This issue still exists after stencil buffer created.

nanovg_circle

The drawing code is as follow.

nvgBeginPath(context);
nvgCircle(context, 16, 16, 15);
nvgFillColor(context, nvgRGBA(0, 0, 0, 255));
nvgFill(context);
nvgStrokeColor(context, nvgRGBA(255, 255, 255, 255));
nvgStrokeWidth(context, 2);
nvgStroke(context);

@dougbinks
Copy link
Contributor

To me it looks like your settings for the device pixel ration are wrong. This needs to be set in nvgBegingFrame:

void nvgBeginFrame(struct NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio);

devicePixelRatio should be set to frameBufferWidth / windowWidth. In your case I measure the image as being about 60 pixels wide, and your drawing a 15 radius (width 30) circle so it looks like you have a devicePixelRatio of 2.0 - which is what I believe the iPhone 5S has.

@olliwang
Copy link
Contributor Author

olliwang commented Mar 8, 2014

The devicePixelRatio is 2.0 in my setting. The 60 pixels wide should be correct, because 15 radius = width 30 (with ratio 1.0) = width 60 (with ratio 2.0). Am I wrong?

The 60-pixel width screenshot was taken from Retina display, but the width will be 30 pixels for non-Retina display, and both will look the same size on screen.

@dougbinks
Copy link
Contributor

If you have the devicePixelRatio set to 2.0 then I'm not sure what's causing the error in the tessellation of the beziers which make up the circle, and I don't have a high retina display to test with.

I think Mikko would need to check this - do you have a bit more of the code (including the nvgBeginFrame call)?

@olliwang
Copy link
Contributor Author

olliwang commented Mar 8, 2014

Yes, this is where I setup nanovg context: https://github.com/ollix/moui/blob/master/moui/widgets/widget_view.cc

And this is how I setup OpenGL environment on iOS.
https://github.com/ollix/moui/blob/master/moui/ui/ios/MOOpenGLView.mm

BTW, the result also doesn't look nice on my Mac and Android device, too. Both devices are set to ratio 1.0 (and it is).

@dougbinks
Copy link
Contributor

It's well worth qualifying what you mean by 'look nice'.

On 8 March 2014 15:58, Olli Wang notifications@github.com wrote:

Yes, this is where I setup nanovg context:
https://github.com/ollix/moui/blob/master/moui/widgets/widget_view.cc

And this is how I setup OpenGL environment on iOS.
https://github.com/ollix/moui/blob/master/moui/ui/ios/MOOpenGLView.mm

BTW, the result also doesn't look nice on my Mac and Android device, too.
Both devices are set to ratio 1.0 (and it is).

Reply to this email directly or view it on GitHubhttps://github.com//issues/46#issuecomment-37099473
.

@olliwang
Copy link
Contributor Author

olliwang commented Mar 8, 2014

After some experiments, I just found a super weird thing.

The window height is 568 dp in iPhone 5 (1136 pixels on Retina display). If I draw the shapes after calling

nvgTranslate(context_, 100, y);

When y is small, the drawing looks pretty smooth. However, when y is near the window height, the drawing looks awful. Take a look at this demonstration.
nanovg_circle2

In the experiment I found the threshold is around 500.

@dougbinks
Copy link
Contributor

I should have noticed this before, but your up-scaled 2x images are interpolated, so can't be used to make comparisons.

If you enlarge the images without interpolation (i.e. just zoom into them with gimp without doing any resizing), the two circles look extremely similar when put side by side. The background noise in the blue part of the image makes it hard to say more than that. Both outputs compare well in quality to a circular brush in Gimp.

@olliwang
Copy link
Contributor Author

olliwang commented Mar 9, 2014

I just taken a screenshot directly of the zoomed version without any resizing. Then added the red circle to point out where I noticed different in the screenshot picture. I've made the background color as clear gray and make the stroke a bit thinker (width 4). Both drawings are rendered by the same source code except the "Y" mentioned before. The circle on the right has a smaller Y and it looks more smooth on the screen.
screen shot 2014-03-09 at 16 03 43

Here's another demonstration with 2 stroke width. The circle on the right side also has a smaller Y and looks much more smoother on Retina display.
screen shot 2014-03-09 at 16 16 06

@memononen
Copy link
Owner

Hi, sorry for not chiming in earlier, I was on vacation last week.

I have seen those spikes in some odd cases too. I have a couple of ideas why they may appear, but not completely sure yet. When you render to different locations, is the offset in integer pixels? I.e. can you replicate the bad rendering by small a fractional offset, i.e. 0.5,0.5?

@olliwang
Copy link
Contributor Author

olliwang commented Mar 9, 2014

Thanks for reply. The exact threshold in my case is 475 (tested on a full screen view with 568 height (1136 pixels) in iPhone 5S). I was testing only integer, but after testing fractional offset, I found it doesn't matter at all. For Y = 474.1, 474.2 ... 474.9. The drawing looks nice (actually it looks nice as long as Y < 475). For Y >= 475, it starts looking bad, I've tested 475.0, 475.1, 475.2, 500, etc.

@memononen
Copy link
Owner

Interesting. How about if you change:
"precision mediump float;\n"
to
"precision highp float;\n"

in nanovg_gl2.h (all 3 occurances)

@olliwang
Copy link
Contributor Author

olliwang commented Mar 9, 2014

The rendered results posted above are already highp. I was trying to see if highp does better but I forgot to change it back.

@memononen
Copy link
Owner

Maybe there is something odd in the tesselation then.
In glnvg__renderStroke(), can you change:
glDrawArrays(GL_TRIANGLE_STRIP, 0, path->nstroke);
to
glDrawArrays(GL_LINES, 0, path->nstroke);

This is simple trick to see if the tesselation of the outline is the same in both cases. It is possible that the problem is not in the shader.

@olliwang
Copy link
Contributor Author

olliwang commented Mar 9, 2014

I can still see the difference. Check where the red arrows pointing.
screen shot 2014-03-09 at 20 43 52

@memononen
Copy link
Owner

Ok, so this rules out tesselation as cuprit as both shapes get same amount of segments. I have to get the bottom of this as my time permits. I'm currently working on better looking line joins at sharp angles.

@olliwang
Copy link
Contributor Author

olliwang commented Mar 9, 2014

Thanks for your awesome work. :)

@memononen
Copy link
Owner

I was not able to reproduce the offset problem, but I fixed a couple of things which should improve the rendering.

Firstly, the AA was too blurry on retina, now it is crispier. I also made the tesselation a bit more finer. Basically all I got was the ugly rendering in your pictures.

Let me know if this fixes it for you. I'd be interested to see how the offset vs non-offset circles look on your setup.

@olliwang
Copy link
Contributor Author

Thanks for your efforts. It seems there are some differences to previously rendered results. But I can still see bad rendering when Y >= 475.
screen shot 2014-03-10 at 12 08 50

@memononen
Copy link
Owner

Ok, so that thing is still there. A stupid idea, since you're rendering in retina, the values in pixel shader are actually double the values? Does the badness happen to vertices which are > 1024, or just to the one at the bottom? Based on that picture the top half of the picture is ok.

@olliwang
Copy link
Contributor Author

Yes, the pixels are double in Retina display. How could I test for vertices > 1024? The badness only happens when Y >= 475 in my case (no any other code changed except the Y).

@memononen
Copy link
Owner

If your circle is at 16,16, radius 15, and offset by 475, pixel ratio 2, then the bottom vertex is roughly at location (16+15+475)*2 px, that is 1012. Not quite 1024, but close :) I was wondering if more vertices look odd when you move the circle further down?

@olliwang
Copy link
Contributor Author

It seems the rendering is not consistent on different edges. I've tried to render the same shape by the Pixelmator app and it looks consistent. I think the inconsistence is what makes the eyes think the circle is not smooth.
screen shot 2014-03-10 at 15 38 17

@memononen
Copy link
Owner

I asked around in twitter and it is possible that for some reason the shader gets compiled on mediump. That in turn could explain the problems around 1024.

Can you try change the shaders as per this gist:
https://gist.github.com/memononen/9460921

@olliwang
Copy link
Contributor Author

It seems the rendering is much better but the difference now happens on the top. The red parts are where I can notice not so smooth when looking at the Retina display in my eyes. It's perfectly displayed on the Retina display when Y = 100.
screen shot 2014-03-11 at 16 50 55

@memononen
Copy link
Owner

How about if you add "precision highp float" to the fragment shader too?

@olliwang
Copy link
Contributor Author

That's it! Now it looks perfect everywhere. Thank you so much. 👍
screen shot 2014-03-11 at 18 24 20

@memononen
Copy link
Owner

Phew, that was a long journey! It's very scary that everything needs to be highp for things to work well. I have a feeling that we have to be careful where to put those highp specifiers to make things fast, and we'll probably need hack this for each platform.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants
@olliwang @dougbinks @memononen and others