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.

Pixelization Shader [GLSL]

Postby compsci89 » Sat Dec 31, 2011 7:20 am

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
cpp 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
cpp 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
cpp 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
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;
}
compsci89
 
Posts: 1
Joined: Fri Dec 30, 2011 12:11 am

Re: Pixelization Shader [GLSL]

Postby hbraun » Wed Apr 04, 2012 9:28 pm

Very nice!
Thanks :)
User avatar
hbraun
 
Posts: 28
Joined: Tue Dec 16, 2008 7:33 pm
Location: Porto Alegre, Brazil

Re: Pixelization Shader [GLSL]

Postby teto » Thu Apr 05, 2012 12:19 pm

sweet ! thx for sharing :)
Using trunk with mingw/gcc 4.6, Windows 7 64 bits driver opengl
User avatar
teto
 
Posts: 159
Joined: Thu Dec 03, 2009 9:37 pm
Location: /home

Re: Pixelization Shader [GLSL]

Postby kazymjir » Fri Apr 06, 2012 4:36 am

Nice!! Remembers me good old Quake I times :)
Warning: Unknown: failed to open stream: No such file or directory in Unknown on line 0
kazymjir
 
Posts: 669
Joined: Sat Feb 20, 2010 4:05 pm
Location: Poland

Re: Pixelization Shader [GLSL]

Postby Virion » Sun Apr 08, 2012 7:30 pm

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

:D
User avatar
Virion
 
Posts: 2102
Joined: Mon Dec 18, 2006 5:04 am
Location: Malaysia


Return to Code Snippets

Who is online

Users browsing this forum: No registered users and 1 guest