• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Shader or method for pixelating and dithering image

Hi all!

I'm looking to make the terrain in my faux 3D stage look as as pixelated and artifact-y as the video posted above. (The character sprites are drawn on the GUI layer, separate from the rest).

Below is a screenshot of my stage for reference.



But I can't think of a way to achieve this. I tried downscaling it and then scaling it back up but the results did not achieve the effect. Is there a shader that could do this? Any other ideas?
 

NightFrost

Member
A shader would achieve that effect. A simple one to make would be one that took the top left pixel's color from a group of pixels - 4x4 pixel groups for example - and filled that group with the color. In other words, the shader would round the read position to those that are evenly divisible by four by doing (x div 4) * 4 or in other words floor(x / 4) * 4, and the same for y. This is a brute method that obscures lot of small detail, but that's how the example also seems to operate.
 

Yal

šŸ§ *penguin noises*
GMC Elder
The easiest thing to do would be to just draw the background stuff onto a smaller surface, then draw the surface scaled to your gui size. Make sure you turn off texture interpolation for both.
Looks like OP tried that exact thing and failed:
I tried downscaling it and then scaling it back up but the results did not achieve the effect.
(then again, it might've been problems with execution rather than the method being wrong)

The idea here is like this:
  • Make a surface
  • Change the rendering target to the surface
  • Draw the 3D world (only the flat plane)
  • Change back to the normal rendering target
  • Do some crustification operations on the surface so it gets lower quality
  • Draw the surface as a flat background (this might necessitate switching back to ortho/2D perspective)
  • Draw all other 2D stuff on top by doing the same 3D perspective drawing you did originally
Ironically, it's going to take a lot more CPU power to make the world look worse, but that's the price you pay for realism :p
 
A shader would achieve that effect. A simple one to make would be one that took the top left pixel's color from a group of pixels - 4x4 pixel groups for example - and filled that group with the color. In other words, the shader would round the read position to those that are evenly divisible by four by doing (x div 4) * 4 or in other words floor(x / 4) * 4, and the same for y. This is a brute method that obscures lot of small detail, but that's how the example also seems to operate.
Are you thinking of this shader being applied to all the terrain objects individually or to the whole screen at once? (Minus the objects)
 
As long as you use the same projection, the sprites should take up the same proportion of the surface.
I guess I dont understand what you are getting at. Any surface smaller than the view size will crop any object outside the surface's width and height, despite said object still being in view.
 
Do you mind clarifying how you're drawing things? The way that Sonic hack added the 3D effect was by reducing the rendering resolution for that particular portion of the background and then upscaling the output. This should be the easiest way to do it, but you said you tried it and it didn't work. What did it look like?
 
I guess I dont understand what you are getting at. Any surface smaller than the view size will crop any object outside the surface's width and height, despite said object still being in view.
The size of the surface doesn't determine what area of the world it shows. The projection determines that. So if you have different sized surfaces, but you use the same projection (and view) on all of them, they will all show the same area of the world.
 
Do you mind clarifying how you're drawing things? The way that Sonic hack added the 3D effect was by reducing the rendering resolution for that particular portion of the background and then upscaling the output. This should be the easiest way to do it, but you said you tried it and it didn't work. What did it look like?
2020-02-04_21-38-35.gif

In this gif, the left half of the track has been reduced and then upscaled, the right half is unchanged (split the track down the middle of the image). I don't really see the pixelated artifacts I'm looking for, and there's barley a difference.

I did another test with a rotating title screen island (from SMW2).
2020-02-04_21-45-35.gif
You notice that down scaling it and then upscaling it just made the pixels larger and fatter (notice when they get close to the screen).

I achieved this here was reducing the size of the sprite asset in half (or 1/4) and then increasing the image_x/yscale in-engine to compensate. Now, if this execution is 100% wrong, then please help understand why, because as I see it right now, the other methods offered here are just doing the same thing.
 
View attachment 28588I achieved this here was reducing the size of the sprite asset in half (or 1/4) and then increasing the image_x/yscale in-engine to compensate. Now, if this execution is 100% wrong, then please help understand why, because as I see it right now, the other methods offered here are just doing the same thing.
What is the exact code you're using for the down/up scaling? Both of those examples are definitely not rendering at a lower resolution, which is what you need to be doing to get the intended effect. It's hard to give specific code without knowing what your rendering code is, but here's some psuedocode on how I would approach it:

Code:
// I'm assuming the native resolution is 320x224; same as the Genesis/Mega Drive.
// If that's the case, I need to quarter the render resolution of the background.
// For your game, I think all this would include are the tiles on the floor.
// Sprites like Sonic, obstacles, and rings should be left out of this, as the UFOs are in your original linked video.

// Required surface checking code
if (!surface_exists(backgroundSurf)) {
  surface_create(resWidth/2, resHeight/2);
}

surface_set_target(backgroundSurf);
  // Whatever is in this script has to account for the fact that the resolution is now 160x112.
  drawSpecialStageFloor();
surface_reset_target();

// Upscales the surface to native res
draw_surface_stretched(backgroundSurf, 0, 0, resWidth, resHeight);

// Draws anything that doesn't need to be scaled.
drawSpecialStageSprites();
 
What is the exact code you're using for the down/up scaling? Both of those examples are definitely not rendering at a lower resolution, which is what you need to be doing to get the intended effect. It's hard to give specific code without knowing what your rendering code is, but here's some psuedocode on how I would approach it:

Code:
// I'm assuming the native resolution is 320x224; same as the Genesis/Mega Drive.
// If that's the case, I need to quarter the render resolution of the background.
// For your game, I think all this would include are the tiles on the floor.
// Sprites like Sonic, obstacles, and rings should be left out of this, as the UFOs are in your original linked video.

// Required surface checking code
if (!surface_exists(backgroundSurf)) {
  surface_create(resWidth/2, resHeight/2);
}

surface_set_target(backgroundSurf);
  // Whatever is in this script has to account for the fact that the resolution is now 160x112.
  drawSpecialStageFloor();
surface_reset_target();

// Upscales the surface to native res
draw_surface_stretched(backgroundSurf, 0, 0, resWidth, resHeight);

// Draws anything that doesn't need to be scaled.
drawSpecialStageSprites();
I'm trying this out. I can't get the tracks/floor to draw correctly (or at all!).
Here's the code, I've purposely removed any scaling and made it 1:1 to help eliminate any variables as to why the terrain is not working

Code:
    var cx = camera_get_view_x(view_camera[0]);
    var cy = camera_get_view_y(view_camera[0]);
    var resWidth = 320;
    var resHeight = 224;


// Required surface checking code
if (!surface_exists(backgroundSurf))
{
  backgroundSurf = surface_create(resWidth, resHeight);
}


surface_set_target(backgroundSurf);
  // Whatever is in this section has to account for the fact that the resolution is now 160x112.
 
  //Set 3D perspective
  d3d_set_projection_ext(x-cx, y-cy, z, xto-cx, yto-cy, zto, 0, 0, 1, fov, global.ViewWidth  / global.ViewHeight, 1, 32000);

  //Draw each track piece
with parSS_Track
  {
    draw_sprite(sprite_index,image_index,(x-cx),(y-cy))
  }
 

surface_reset_target();

// Upscales the surface to native res
draw_surface_stretched(backgroundSurf, 0, 0, resWidth, resHeight);

2020-02-05_06-14-59.gif
here's what I see in game.

and oddly, here's what happens if I go to fullscreen and then back down to windowed. Looks like a draw_clear_alpha type of issue but doesn't explain the above

2020-02-05_06-15-10.gif
 

Attachments

Last edited:
The size of the surface doesn't determine what area of the world it shows. The projection determines that. So if you have different sized surfaces, but you use the same projection (and view) on all of them, they will all show the same area of the world.
The surface size doesn't determine the scale of the sprites either, and the project is a separate thing entirely, which isn't changing, if I'm following you.
I still don't quite understand what I'm missing here? Any tests I've done, as seen above, are coming up short.
 
Last edited:

2DKnights

Member
What is your games native resolution? Using those 16bit style sprites if you lower the resolution you should get the effect you want. Try something lower like 320x240(4:3) or 320x180(16:9)

Alternatively activate a line doubling shader before drawing your 3d and deactivate afterward
 
Top