|
[A5] Screen scrolling causes tile bitmap pixel misplacement |
Eric Johnson
Member #14,841
January 2013
|
Hi there. I've noticed that windowed applications, when incorporating any sort of camera system with vertical scrolling, generates pixel gaps between bitmap tiles. Here's an image: {"name":"nlosyh.jpg","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/f\/ef0ebffbd1a685f8ea20e16c27b20319.png","w":482,"h":509,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/f\/ef0ebffbd1a685f8ea20e16c27b20319"} Notice the al_clear_to_color() call bleeding between the tiles. This does not occur until the camera begins scrolling vertically (horizontal scrolling has no affect). Here's my crude example code: 1#include <allegro5/allegro.h>
2#include <allegro5/allegro_image.h>
3
4int main(void) {
5
6 bool render = false;
7 bool running = true;
8
9 // Camera vars
10 float camera_x = 0.0;
11 float camera_y = 0.0;
12
13 float smile_x = 0.0;
14 float smile_y = 0.0;
15
16 // Excuse the lack of error checking
17
18 al_init();
19
20 al_set_new_display_flags(ALLEGRO_WINDOWED);
21
22 ALLEGRO_TIMER *timer = al_create_timer(1.0 / 60.0);
23 ALLEGRO_DISPLAY *display = al_create_display(480, 480);
24 ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue();
25
26 // Give the window a fancy display title
27 al_set_window_title(display, "Example");
28
29 al_init_image_addon();
30
31 al_register_event_source(event_queue, al_get_timer_event_source(timer));
32 al_register_event_source(event_queue, al_get_display_event_source(display));
33
34 // Load our beautiful bitmaps
35 ALLEGRO_BITMAP *smile = al_load_bitmap("smile.png");
36 ALLEGRO_BITMAP *grass = al_load_bitmap("grass.png");
37
38 al_start_timer(timer);
39
40 // Stretch display
41 ALLEGRO_TRANSFORM t;
42 al_identity_transform(&t);
43 al_scale_transform(&t, 5, 5); // Works OK if scale value is > 5
44 al_use_transform(&t);
45
46 // Primary game loop
47 while (running) {
48
49 ALLEGRO_EVENT event;
50
51 al_wait_for_event(event_queue, &event);
52
53 switch (event.type) {
54
55 case ALLEGRO_EVENT_DISPLAY_CLOSE:
56
57 // Shut the program down
58 running = false;
59 break;
60
61 case ALLEGRO_EVENT_TIMER:
62
63 render = true;
64
65 // Crappy camera calculations
66 camera_x = smile_x;
67 camera_y = smile_y;
68
69 // Move player down screen
70 smile_y += 0.5;
71 break;
72 }
73
74 // Drawing phase
75 if (render && al_is_event_queue_empty(event_queue)) {
76
77 render = false;
78
79 if (smile && grass) {
80
81 al_hold_bitmap_drawing(true);
82
83 for (int y = 0; y < 100; ++y) {
84
85 for (int x = 0; x < 100; ++x) {
86
87 // Draw grass tiles
88 al_draw_bitmap(grass, x * 8 - camera_x, y * 9 - camera_y, 0);
89 }
90 }
91
92 al_hold_bitmap_drawing(false);
93
94 // Draw our smile guy (think of him as player)
95 al_draw_bitmap(smile, smile_x - camera_x, smile_y - camera_y, 0);
96 }
97
98 al_flip_display();
99 al_clear_to_color(al_map_rgb(0, 0, 255));
100 }
101 }
102
103 // Garbage collection
104 al_destroy_timer(timer);
105 al_destroy_display(display);
106 al_destroy_event_queue(event_queue);
107
108 return 0;
109}
I am running x86_64 Ubuntu 13.10 with Intel Corporation 7 Series/C210 Series Chipset Family MEI Controller #1 (rev 04) graphics card. Do you see anything wrong with the way I'm structuring the example code? What could be causing this? I've attached the example source and bitmap files to this post. Your feedback is most appreciated.
|
Arthur Kalliokoski
Second in Command
February 2005
|
Unable to reproduce here, no matter how I set driver vsync etc. Slackware 14.1 with Nvidia driver 330.20 and GeForce GTX 650 card. {"name":"608217","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/7\/3738f2640796239829ae936f503ccd8f.png","w":480,"h":480,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/7\/3738f2640796239829ae936f503ccd8f"} They all watch too much MSNBC... they get ideas. |
Kris Asick
Member #1,424
July 2001
|
Sheegoth said: I am running x86_64 Ubuntu 13.10 with Intel Corporation 7 Series/C210 Series Chipset Family MEI Controller #1 (rev 04) graphics card. Intel Graphics Chipsets = Severely Lame What's probably happening is your graphics chipset can't properly handle floating-point positions, since you're only moving everything in half-block increments. That said though, you really should round all your coordinates for drawing stuff down to the next whole number in order to avoid aliasing and to keep fine details in the graphics from flickering. This may also solve the issue you're having. --- Kris Asick (Gemini) |
Eric Johnson
Member #14,841
January 2013
|
Arthur Kalliokoski said: Unable to reproduce here, no matter how I set driver vsync etc. Slackware 14.1 with Nvidia driver 330.20 and GeForce GTX 650 card. Thank you for giving it a go, Arthur. It is good to know that it is an issue with just my machine. Kris Asick said: Intel Graphics Chipsets = Severely Lame What's probably happening is your graphics chipset can't properly handle floating-point positions, since you're only moving everything in half-block increments. That said though, you really should round all your coordinates for drawing stuff down to the next whole number in order to avoid aliasing and to keep fine details in the graphics from flickering. This may also solve the issue you're having. About the floating-point positions, I knew this; I just wanted to ensure that IS the issue (which you've confirmed). I've known that whole value movement (1px instead of 0.5px, for example) works just fine, but a speed of 1 is too fast (for my liking) after transforming the display. I recall not having this issue several months ago. This all began after messing with some things in Ubuntu. What do you think: would a factory re-install of Linux potentially solve the issue?
|
Matthew Leverton
Supreme Loser
January 1999
|
On my XUbuntu laptop using the i915 driver (Intel 3000?) I get the same gaps. |
Kris Asick
Member #1,424
July 2001
|
Sheegoth said: What do you think: would a factory re-install of Linux potentially solve the issue? It's most likely related to your graphics drivers so the simplest thing to do would be to roll back to earlier drivers and see if the problem goes away, or install the latest drivers and see if that helps. I seriously doubt the problem would be related to anything else. Plus, I didn't say you had to move everything by whole-pixel increments, just round down the coordinates used for drawing when you draw them. You can still store and move positions at the floating-point level. With my own game objects, because I use a frame interpolation system that can run a game at a fixed framerate yet render at whatever framerate the end user can achieve, all of my game objects track the coordinates they are currently in, the coordinates they were in on the last fixed frame, and the coordinates they should render to. --- Kris Asick (Gemini) |
Eric Johnson
Member #14,841
January 2013
|
Kris Asick said: Plus, I didn't say you had to move everything by whole-pixel increments, just round down the coordinates used for drawing when you draw them. You can still store and move positions at the floating-point level. With my own game objects, because I use a frame interpolation system that can run a game at a fixed framerate yet render at whatever framerate the end user can achieve, all of my game objects track the coordinates they are currently in, the coordinates they were in on the last fixed frame, and the coordinates they should render to. Hmm, what an interesting concept. Would you mind posting pseudo code? What immediately came to my mind was to change the camera values to ints, and then to pass the player's floats as arguments (thus accumulating whole-values after 0.5+ goes for two cycles). This corrected the gaps, but, to no surprise, resulted in jittery player movement.
|
jmasterx
Member #11,410
October 2009
|
1 void Camera::transformView()
2 {
3 updateCamera();
4 al_identity_transform(&m_transform);
5 al_translate_transform(&m_transform,floor(-getLeft()),floor(-getTop()));
6 al_use_transform(&m_transform);
7 }
8
9 void Camera::focus( Entity* target )
10 {
11 m_target = target;
12 updateCamera();
13 }
14
15 void Camera::onResize(int w, int h)
16 {
17 float x = getX();
18 float y = getY();
19 getBox().setBox(0,0,w,h);
20 setPosition(x,y);
21 }
22
23 void Camera::updateCamera()
24 {
25 if(m_target)
26 {
27 setPosition(
28 m_target->getRenderLeft() + (m_target->getWidth() / 2),
29 m_target->getRenderTop() + (m_target->getHeight() / 2));
30 }
31 }
32}
33
34.....
35
36 int Entity::getRenderLeft() const
37 {
38 return floor(getLeft() + 0.5f);
39 }
40
41 int Entity::getRenderTop() const
42 {
43 return floor(getTop() + 0.5f);
44 }
Agui GUI API -> https://github.com/jmasterx/Agui |
Kris Asick
Member #1,424
July 2001
|
Sheegoth said: Hmm, what an interesting concept. Would you mind posting pseudo code? Not sure of a good way to write this in pseudo-code, but if you're experiencing jitter it's typically related to not rounding things in the right order, rounding values together that should be rounded separately, or figuring out positions based on rounded values instead of actual values or vice versa. The best way to scan this is to cut all your movement rates down by a factor of 100 or so, write the values on-screen for each step of the calculations and watch them as they slowly change. This is what I do when one of my math equations does funny stuff like this and watching the numbers change is a good way to gauge what's causing things like jitter to happen. Also, since the Allegro drawing functions take floats, not ints, you probably shouldn't convert to them at any point and just stick with using floor(). --- Kris Asick (Gemini) |
|