- Home /
How to write a pixelation vertex/fragment or surface shader?
Hey everybody!
Finally I've got some spare time again. I tried to get started with shaders about two months ago and managed to create a simple retro effect shader as an imageeffect shader (http://i.imgur.com/VZ1Hy6k.png). But now I'd like to have this effect as a shader that's put onto objects and creates a world where some objects are "retro" and some are not.
But I don't quiet see how I would do the pixelation in this case. I don't need the shader written out for me, just an idea how one could achieve this effect in a vertex/fragment or surface shader (preferably both but I assume it will be quiet similar). My problem is that I don't see how I can get the information about the other final pixels. Simply put I have to make some pixels look like other pixels to get a pixelation effect. To make pixel A look like pixel B, I have to know how pixel B looks. In the case of an imageeffect, the whole "normal" rendering process of the object is already done and I have the final picture (that will be displayed on the screen) as a 2D texture to work on. This meant that I already knew how all the pixels looked like (before pixelating, that is) and also made it very easy to access the other pixels by texture coordinates. But as far as my understanding goes, I don't have this when using the shader on an object in the scene instead of anafter imageeffect and I'm puzzled what's the way to go about it.
I found this but that only shows how to pixelate an area of the screen and not an actual object and I don't think it can somehow be adapted to work the way I need it to.
Any idea how I could approach this problem is greatly appreciated!
Thanks in advance!
Answer by HappyMoo · Jan 04, 2014 at 01:43 AM
Hi,
I only dabbled in pixel shader programming, but here's my idea:
first you write a normal fragment shader with all features you need, like lighting etc.
now for every varying variable that goes into your formula that decides what to render you do some rounding - here's some pseudocode:
float roundTo = 0.05;
newSomeFloat = roundTo * int(someFloat/roundTo);
newNormal = vec3(
roundTo * int(normal.x/roundTo),
roundTo * int(normal.y/roundTo),
roundTo * int(normal.z/roundTo)
)
you do this with everything.. floats, vec3, colors.. you get the idea and then you apply your formula that calculates the color on the rounded values, not on the original values
This will make the values change less often and you will get some kind of low resolution effect, but the borders between changing values will probably not be horizontal and vertical, so you wont get big pixels, but it will be blocky in some sense...
Also, you can use different roundTo values for the different variables for different effects. I'd love a webplayer with sliders for all the different roundTo values to play with.
Please update this question with your results, this sounds interesting.
Thank you very much for your input! That's the best idea so far. I'll probably first just write a simple surface shader and round the finalcolor to get an idea of what it might look like. This should take way less effort (since I'm relatively new to shaders) than writing a whole fragment shader with all the lightning etc., and give an at leas somewhat similar result (just way less configurable) and if I like what I see, I'll write the whole thing myself. I'll post pictures of it as soon as I have them.
One idea I had for a short time was to use multiple cameras and put all pixelated objects on a separate layer then render just this layer with one camera and apply the pixelation as aftereffects. But upon thinking about it, it doesn't sound very practical:
Sadly, every object can just be part of one layer in Unity and you would at least not be able to render objects with this effect that absolutely have to be on another layer (water, ignore raycast, etc.)
(my biggest worries) You would somehow have to work around all the depth problems that you'd get. Imagine you have to render something pixelated in the background and something very near to the screen and in between you have normal looking objects.. don't see how this could work this way
I'd just prefer to have a render solution because I could use that as in as many projects as I want and without a complex setup beforehand
I know it probably wouldn't work because of the problems listed above (and some more) but I posted it just in case it gives someone an idea of how this problem could be solved. It also doesen't necessarely have to be a shader-only solution if someone else should happen to have another idea, I'm all ears!
Thinking again about this, you can also try to convert everything that is positional in world coordinates, so when you do the rounding, you actually get axis-aligned pixel boxes that don't move if you walk around the objects
Can't wait to see screenshots
Hey Happy$$anonymous$$oo $$anonymous$$aybe it's just too early in the morning here or it's just me. But I'm not sure I get what you're saying. Could you please describe it a bit more detailed? Thank you very much!
And just wen I was thinking I'd have a few quiet days here at the office and could maybe compensate some over time, one of our customers has a severe case of "I know we're already in the testing phase but we ABSOLUTELY need this new features (that we only thought of yesterday) in THIS release!"-syndrom. So, it might take me a while but I'll definitely deliver on this :)
When you take the position of a fragment and do your rounding on x,y,z components, these components are in local coordinates, which means, you get rounding seams along local axes.
You could also calculate the position in world coordinates in the vertex shader...
output.pos = mul(UNITY_$$anonymous$$ATRIX_$$anonymous$$VP, input.vertex);
output.worldPos = mul(_Object2World, input.vertex);
which allows you to do the rounding in world coordinates, so you get rounding seam along world coordinate axes.
The difference should be especially visible in rotated and moving objects, so make sure you have those in your test scenes.