- Home /
How to (efficiently) make an image pixelated?
I'm trying to write a "Retro Filter" image effect.
Basically I have two steps for now.
Make the image pixelated
Reduce the amount of different colors in the image
For step 2 I'm using a simple shader I wrote that shrinks down the amount of possible colors But step 1 doesn't work. You know how a picture gets very pixelated and "blocky" when you scale it down and up again in an image editing program (Photoshop, Gimp, Paint, etc.)? That's exactly what I was looking for. So, I decided to scale the Image down and up again in OnRenderImage. The code for that looks like this:
RenderTexture scaleBuffer = RenderTexture.GetTemporary(source.width / DownscaleFactor, source.height / DownscaleFactor);
accumTexture.MarkRestoreExpected();
Graphics.Blit(accumTexture, scaleBuffer);
Graphics.Blit(scaleBuffer, accumTexture);
RenderTexture.ReleaseTemporary(scaleBuffer);
I assumed that would exactly do what I want. But instead of feeling blocky, it looks very washed out. Here you can see it with DownscaleFactor set to 8: Result with factor 8
"Ok, that looks a bit washed out but it surely will get better if I scale it down even more and then back up again! At least that's what i was thinking. But this is what happens when I set DownscaleFactor to 32: Result with factor 32
I have no Idea why this is happening. I tried turning AA off everywhere I could, including setting the AA level of the RenderTexture objects to 1.
I'd be ever so grateful if someone could tell me why this is happening, what I can do about it or what else would work to get the picture all blocky. I'm sure a shader might work but the shader I wrote for reducing the colors was my first shader ever and I'm just about to learn the basics. I have no idea how to go about writing a "pixeling" shader and the down- and upscale approach felt very intuitive to me.
Thank you very much for your help!
Answer by Bunny83 · Nov 09, 2013 at 02:10 AM
Your problem is the filtermode of your texture. You should set it to "point".
Correct, I just started up Unity to check my answer and it's indeed the Point filter mode.
@Bunny83 This was indeed the problem, thank you very much! Although I'm not new to program$$anonymous$$g, I'm very new to graphics program$$anonymous$$g and I just didn't realize that the filtering does not only occur when e.g. walking towards a texture or away from it but also if you manually blit it from one buffer into another. Now it makes sense and I almost feel stupid. But you have no idea how happy you made me! :)
Here's a comparison shot of the filter in case you're interested in the result: Comparison
$$anonymous$$ight I ask you if you think that this is an efficient approach to pixelate the picture from a computational point? And thanks again!
Well, beside setting the screen resolution to your target resolution (which isn't possible anymore for very small resolutions), I don't think you can get it any faster ;)
Since you render your whole scene to a small rendertarget you actually increase the performance since all expensive fragment shaders only run for a small amount of "fragments". If you downscale by 8 that means 8x8 = 64 so only 1/64 of the screen area is actually rendered. The blitting operation is quite cheap (especially with point filtering since you only sample one texel per screen pixel ins$$anonymous$$d of 4 or 8 like you would with billinear or trilinear filtering).
So you're actually $$anonymous$$imizing the actual scene-rendering fillrate by using a small render target and the most efficient blitting filter ;)
@Bunny83 Thank you very much for further explaining things to me! Things are quiet a bit clearer now. Although I studied computer science and am working as a programmer for a living, I never laid my hands on games before and I find myself having a much harder time finding comprehensive information about certain topics than I had with other fields of program$$anonymous$$g. Sometimes I feel that most of the things I find are either described so superficially that I don't gain anything from it or so specific and in-depth that I almost already need to understand the topic very well. $$anonymous$$aybe you know what I mean.
In any case: I want to thank you very much for helping me out and making things clearer although those questions are probably stupid beginner problems ;) One of the greatest things when working in this industry is the amazing user base (stackoverflow e.g.) and you are one of those pieces of the puzzle that make it so great :) Thanks again!
Answer by Owen-Reynolds · Nov 09, 2013 at 05:52 AM
Don't think I've ever done this, but could you have the shader round the texture coords to the nearest 1/100th? Something like: IN.uv.x = (frac(IN.uv.x/100)*100;
(same for y, unless frac is a vector operation.)
Then get fancy and have the 100 be a uniform var, if that works.
Thanks for this idea! Now that I read it, it fells so simple and obvious it almost makes me want to bang my head against my desk. How could I not see this? I was way overthinking it. Even though I got it working now, I'll also try out your idea out of curiosity and to get some more practice with shaders. Also, I'll upvote you as soon as I have enough karma to do so. Sadly, I can't at the moment even though your idea's great.
Answer by Cherno · Nov 09, 2013 at 02:01 AM
I think it's not about the AA, but about the Anisotropic Filtering that is on by default and can't be disabled as far as I know, at least not for normal model textures.
Edit: I confused AF with Bilninear Filtering, my bad.
Anisotropic Filtering is just a special kind of mip-mapping which only applies when viewing a surface at a low angle. Since he's blotting the textures the angle will always be 0. So AF has no effect here.
Correct, I mixed it up with Bilinear Filtering, thanks for the note :)
Your answer
Follow this Question
Related Questions
Save a texture after a pass in shader 0 Answers
dynamic hole in layer / texture / camera 0 Answers
Blit into Texture2DArray slice not working in WebGL 0 Answers