Pixelization Shader [GLSL]

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
compsci89
Posts: 1
Joined: Fri Dec 30, 2011 12:11 am

Pixelization Shader [GLSL]

Post by compsci89 »

Hey all, this is currently for the OpenGL driver only (although the shader is simple enough, an HLSL port could be made).

Basically what I've created is a post-processing shader that will intentionally pixelate and reduce the color palette of the screen. The reason you might want to do this is if you're interested in giving your game a retro look or some kind of transitioning effect (you can change how pixilated the screen is at will). Whether you're interested or not, it's still an interesting effect to see in action.

Here are some screenshots with the shader off and then on.

Image

Image

You can download the pre-built binary (for windows) and source code here: http://www.henrywrites.com/downloads/IrrPixelate.zip

Directions
* Use the arrow keys and mouse to move/look around.
* Press the spacebar to toggle the effect (it's on by default).
* Press the escape key to quit.

In case the link goes down (in the future or even temporarily) I'll post the code below.

pixelate.frag

Code: Select all

uniform sampler2D texture;
uniform float new_w, new_h, palette;
 
varying vec2 texCoord;
 
vec4 pixelate(sampler2D tex, vec2 uv)
{
        vec2 coord = vec2( ceil(uv.x * new_w) / new_w,
        ceil(uv.y * new_h) / new_h );
        return texture2D(tex, coord);
}
 
vec4 reduce_palette(vec4 color, float max_colors_per_channel)
{
        if(max_colors_per_channel < 0) {
                return color;
        }
        
        return ceil(color * max_colors_per_channel) / max_colors_per_channel;
}
 
void main()
{
        vec4 color = pixelate(texture, texCoord);
        gl_FragColor = reduce_palette(color, palette);
}
pixelate.vert

Code: Select all

varying vec2 texCoord;
 
void main()
{
        vec2 Position;
        Position.xy = sign(gl_Vertex.xy);
        
        gl_Position = vec4(Position.xy, 0.0, 1.0);
        texCoord = Position.xy *.5 + .5;
}
IPostProcessPixelate.h

Code: Select all

#ifndef __POST_PROCESS_EFFECT_PIXELATE__
#define __POST_PROCESS_EFFECT_PIXELATE__
 
#include <irrlicht.h>
 
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
 
 
class IPixelate_Callback : public video::IShaderConstantSetCallBack
{
        public:
                float height;
                float width;
                float palette;
                
                virtual void OnSetConstants(video::IMaterialRendererServices* services, s32 userData)
                {
                        int texture = 0;
                        services->setPixelShaderConstant("texture", (float*)(&texture), 1);
 
                        services->setPixelShaderConstant("new_h",   reinterpret_cast<f32*>(&height),  1);
                        services->setPixelShaderConstant("new_w",   reinterpret_cast<f32*>(&width),   1);
                        services->setPixelShaderConstant("palette", reinterpret_cast<f32*>(&palette), 1);
                }
};
 
 
 
 
class IPostProcessPixelate : public scene::ISceneNode
{
    public:
                core::aabbox3d<f32> Box;
                video::S3DVertex Vertices[6];//the vertices for the onscreenquad
                video::SMaterial Material;
                video::ITexture* rt0; //the rendertarget
                int mat;
                IPixelate_Callback* callback;
 
        IPostProcessPixelate(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id): scene::ISceneNode(parent, mgr, id)
        {
                Vertices[0] = video::S3DVertex(-1.0f, -1.0f, 0.0f,1,1,0, video::SColor(255,0,255,255), 0.0f, 1.0f);
                Vertices[1] = video::S3DVertex(-1.0f,  1.0f, 0.0f,1,1,0, video::SColor(255,0,255,255), 0.0f, 0.0f);
                Vertices[2] = video::S3DVertex( 1.0f,  1.0f, 0.0f,1,1,0, video::SColor(255,0,255,255), 1.0f, 0.0f);
                Vertices[3] = video::S3DVertex( 1.0f, -1.0f, 0.0f,1,1,0, video::SColor(255,0,255,255), 1.0f, 1.0f);
                Vertices[4] = video::S3DVertex(-1.0f, -1.0f, 0.0f,1,1,0, video::SColor(255,0,255,255), 0.0f, 1.0f);
                Vertices[5] = video::S3DVertex( 1.0f,  1.0f, 0.0f,1,1,0, video::SColor(255,0,255,255), 1.0f, 0.0f);
        }
 
        ~IPostProcessPixelate()
        {
        //      Material.MaterialType = video::EMT_SOLID;
        //      Material = video::SMaterial();
                callback->drop();
        }
 
        void setPixelatedResolution(float width, float height)
        {
                if(callback)
                {
                        callback->width = width;
                        callback->height = height;
                }
        }
 
        void setReducedColorPalette(float palette)
        {
                if(callback)
                {
                        callback->palette = palette;
                }
        }
 
    void initiate(int sizeW, int sizeH, scene::ISceneManager* smgr)
        {
                video::IVideoDriver* driver = smgr->getVideoDriver();
                video::IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
 
                callback = new IPixelate_Callback();
                callback->width = (f32)sizeW;
                callback->height = (f32)sizeH;
                Material.MaterialType = (E_MATERIAL_TYPE)gpu->addHighLevelShaderMaterialFromFiles
                (
                                "media/pixelate.vert", "main", video::EVST_VS_1_1,
                                "media/pixelate.frag", "main", video::EPST_PS_1_1,
                                callback, (video::EMT_SOLID)
                );
 
 
                rt0 = driver->addRenderTargetTexture(dimension2d<u32>(sizeW,sizeH), "RTT1");
                Material.Wireframe = false;
                Material.Lighting = false;
                Material.TextureLayer[0].Texture = rt0;
        }
 
 
   virtual void OnPreRender(){}
 
   virtual void render()
   {
           u16 indices[] = {0,1,2,3,4,5};
       video::IVideoDriver* driver = SceneManager->getVideoDriver();
 
       SMaterial m = driver->getMaterial2D();
       driver->setMaterial(Material);
       driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
       driver->drawIndexedTriangleList(&Vertices[0], 6, &indices[0], 2);
       driver->setMaterial(m);
   }
 
  virtual u32 getMaterialCount(){return 1;}
  virtual video::SMaterial& getMaterial(s32 i){return (Material);}
  virtual const core::aabbox3d<f32>& getBoundingBox() const{return Box;}
};
 
#endif
main.cpp

Code: Select all

#include <irrlicht.h>
#include "IPostProcessPixelate.h"
 
using namespace irr;
 
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif
 
 
const int WINDOW_WIDTH = 1024;
const int WINDOW_HEIGHT = 768;
 
 
class MyEventReceiver : public IEventReceiver
{
        public:
                // This is the one method that we have to implement
                virtual bool OnEvent(const SEvent& event)
                {
                        // Remember whether each key is down or up
                        if (event.EventType == irr::EET_KEY_INPUT_EVENT)
                                KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
 
                        return false;
                }
 
                // This is used to check whether a key is being held down
                virtual bool IsKeyDown(EKEY_CODE keyCode) const
                {
                        return KeyIsDown[keyCode];
                }
 
                MyEventReceiver()
                {
                        for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
                                KeyIsDown[i] = false;
                }
 
        private:
                // We use this array to store the current state of each key
                bool KeyIsDown[KEY_KEY_CODES_COUNT];
};
 
 
int main()
{
        // create device.
        MyEventReceiver receiver;
        IrrlichtDevice *device = createDevice(video::EDT_OPENGL, core::dimension2d<u32>(WINDOW_WIDTH, WINDOW_HEIGHT), 32, false, false, false, &receiver);
        if (device == 0)
                return 1; // could not create selected driver.
 
        video::IVideoDriver* driver = device->getVideoDriver();
        scene::ISceneManager* smgr = device->getSceneManager();
 
        device->getFileSystem()->addZipFileArchive("media/map-20kdm2.pk3");
 
        scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
        scene::IMeshSceneNode* q3node = 0;
        if (q3levelmesh)
                q3node = smgr->addOctreeSceneNode(q3levelmesh->getMesh(0), 0);
        scene::ITriangleSelector* selector = 0;
        if (q3node)
        {
                q3node->setPosition(core::vector3df(-1350,-130,-1400));
 
                selector = smgr->createOctreeTriangleSelector(
                                q3node->getMesh(), q3node, 128);
                q3node->setTriangleSelector(selector);
        }
 
        // Set a jump speed of 3 units per second, which gives a fairly realistic jump
        // when used with the gravity of (0, -10, 0) in the collision response animator.
        scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS(0, 100.0f, .3f, 0, 0, 0, true, 3.f);
        camera->setPosition(core::vector3df(50,50,-60));
        camera->setTarget(core::vector3df(-70,30,-60));
        if (selector)
        {
                scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
                        selector, camera, core::vector3df(30,50,30),
                        core::vector3df(0,-10,0), core::vector3df(0,30,0));
                selector->drop(); // As soon as we're done with the selector, drop it.
                camera->addAnimator(anim);
                anim->drop();  // And likewise, drop the animator when we're done referring to it.
        }
 
        // First, let's get rid of the mouse cursor.
        device->getCursorControl()->setVisible(false);
 
        /* Add 3 animated hominids. */
        scene::IAnimatedMeshSceneNode* node = 0;
 
        // This X files uses skeletal animation, but without skinning.
        node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("media/dwarf.x"), 0);
        node->setScale(core::vector3df(1.5, 1.5, 1.5));
        node->setPosition(core::vector3df(-70,-66,-60));
        node->setRotation(core::vector3df(0,-90,0));
        node->setAnimationSpeed(20.f);
 
        // Create our pixelation post processing effect.
        IPostProcessPixelate* pixelate = new IPostProcessPixelate(0, smgr, 672);
        pixelate->initiate(WINDOW_WIDTH, WINDOW_HEIGHT, smgr); // create a render target of size 1024x768.
        pixelate->setPixelatedResolution(160, 120); // render the scene as if it were 160x120 pixels.
        pixelate->setReducedColorPalette(16); // 16 colors per channel (16 red colors, 16 blue colors, 16 green colors) and any combonation of these.
        bool usePixelate = true;
 
        // FPS stuff.
        int lastFPS = -1;
 
        while(device->run())
        {
                if (device->isWindowActive())
                {
                        driver->beginScene(true, true, 0);
 
                        // Check if we want to toggle our pixelation effect.
                        // We have to do a little hacking here, because irrlicht doesn't currently let you detect
                        // a single keypress (that is, we don't want to keep getting 'key presses' when the key
                        // is held down, but only on initial contact).
                        static bool isDown = false;
                        if(!receiver.IsKeyDown(irr::KEY_SPACE)) isDown = false;
                        if(receiver.IsKeyDown(irr::KEY_SPACE) && !isDown)
                        {
                                isDown = true;
                                usePixelate = !usePixelate;
                                if(usePixelate)
                                {
                                        pixelate->setPixelatedResolution(160, 120);
                                        pixelate->setReducedColorPalette(16);
                                }
                                else
                                {
                                        pixelate->setPixelatedResolution(1024, 768);
                                        pixelate->setReducedColorPalette(-1);
                                }
                        }
 
                        // Escape to quit.
                        if(receiver.IsKeyDown(irr::KEY_ESCAPE))
                        {
                                break;
                        }
 
                        // Capture the scene to our render target.
                        driver->setRenderTarget(pixelate->rt0, true, true, SColor(255,255,255,255));
                        smgr->drawAll();
 
                        // Render our capture and process scene.
                        driver->setRenderTarget(0);
                        pixelate->render();
 
                        // We're all done drawing, so end the scene.
                        driver->endScene();
 
                        int fps = driver->getFPS();
                        if (lastFPS != fps)
                        {
                                core::stringw str = L"IrrPixelate Demo - Irrlicht Engine [";
                                str += driver->getName();
                                str += "] FPS:";
                                str += fps;
                                str += "    (press ESC to quit)";
 
                                device->setWindowCaption(str.c_str());
                                lastFPS = fps;
                        }
                }
        }
 
        device->drop();
 
        return 0;
}
hbraun
Posts: 28
Joined: Tue Dec 16, 2008 7:33 pm
Location: Porto Alegre, Brazil
Contact:

Re: Pixelization Shader [GLSL]

Post by hbraun »

Very nice!
Thanks :)
teto
Posts: 159
Joined: Thu Dec 03, 2009 9:37 pm
Location: /home
Contact:

Re: Pixelization Shader [GLSL]

Post by teto »

sweet ! thx for sharing :)
Using trunk with mingw/gcc 4.6, Windows 7 64 bits driver opengl
kazymjir
Posts: 727
Joined: Sat Feb 20, 2010 4:05 pm
Location: Munich, Bayern

Re: Pixelization Shader [GLSL]

Post by kazymjir »

Nice!! Remembers me good old Quake I times :)
Virion
Competition winner
Posts: 2148
Joined: Mon Dec 18, 2006 5:04 am

Re: Pixelization Shader [GLSL]

Post by Virion »

off topic:
the screenshots remind me of Apple.
before - pixelated!
after - retina display! HD!

:D
Post Reply