123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- /** Example 027 Post Processing
- This tutorial shows how to implement post processing for D3D9 and OpenGL with
- the engine. In order to do post processing, scene objects are firstly rendered
- to render target. With the help of screen quad, the render target texture
- is then drawn on the quad with shader-defined effects applied.
- This tutorial shows how to create a screen quad. It also shows how to create a
- render target texture and associate it with the quad. Effects are defined as
- shaders which are applied during rendering the quad with the render target
- texture attached to it.
- A simple color inverse example is presented in this tutorial. The effect is
- written in HLSL and GLSL.
- @author Boshen Guan
- We include all headers and define necessary variables as we have done before.
- */
- #include "driverChoice.h"
- #include "exampleHelper.h"
- #include <irrlicht.h>
- using namespace irr;
- #ifdef _MSC_VER
- #pragma comment(lib, "Irrlicht.lib")
- #endif
- /*
- We write a class derived from IShaderConstantSetCallBack class and implement
- OnSetConstants callback interface. In this callback, we will set constants
- used by the shader.
- In this example, our HLSL shader needs texture size as input in its vertex
- shader. Therefore, we set texture size in OnSetConstants callback using
- setVertexShaderConstant function.
- */
- IrrlichtDevice* device = 0;
- video::ITexture* rt = 0;
- class QuadShaderCallBack : public video::IShaderConstantSetCallBack
- {
- public:
- QuadShaderCallBack() : FirstUpdate(true), TextureSizeID(-1), TextureSamplerID(-1)
- { }
- virtual void OnSetConstants(video::IMaterialRendererServices* services,
- s32 userData)
- {
- core::dimension2d<u32> size = rt->getSize();
- // get texture size array
- f32 textureSize[] =
- {
- (f32)size.Width, (f32)size.Height
- };
- if ( FirstUpdate )
- {
- TextureSizeID = services->getVertexShaderConstantID("TextureSize");
- TextureSamplerID = services->getPixelShaderConstantID("TextureSampler");
- }
- // set texture size to vertex shader
- services->setVertexShaderConstant(TextureSizeID, reinterpret_cast<f32*>(textureSize), 2);
-
- // set texture for an OpenGL driver
- s32 textureLayer = 0;
- services->setPixelShaderConstant(TextureSamplerID, &textureLayer, 1);
- }
- private:
- bool FirstUpdate;
- s32 TextureSizeID;
- s32 TextureSamplerID;
- };
- class ScreenQuad : public IReferenceCounted
- {
- public:
- ScreenQuad(video::IVideoDriver* driver)
- : Driver(driver)
- {
- // --------------------------------> u
- // |[1](-1, 1)----------[2](1, 1)
- // | | ( 0, 0) / | (1, 0)
- // | | / |
- // | | / |
- // | | / |
- // | | / |
- // | | / |
- // | | / |
- // | | / |
- // | | / |
- // |[0](-1, -1)---------[3](1, -1)
- // | ( 0, 1) (1, 1)
- // V
- // v
- /*
- A screen quad is composed of two adjacent triangles with 4 vertices.
- Vertex [0], [1] and [2] create the first triangle and Vertex [0],
- [2] and [3] create the second one. To map texture on the quad, UV
- coordinates are assigned to the vertices. The origin of UV coordinate
- locates on the top-left corner. And the value of UVs range from 0 to 1.
- */
- // define vertices array
- Vertices[0] = irr::video::S3DVertex(-1.0f, -1.0f, 0.0f, 1, 1, 0, irr::video::SColor(0,255,255,255), 0.0f, 1.0f);
- Vertices[1] = irr::video::S3DVertex(-1.0f, 1.0f, 0.0f, 1, 1, 0, irr::video::SColor(0,255,255,255), 0.0f, 0.0f);
- Vertices[2] = irr::video::S3DVertex( 1.0f, 1.0f, 0.0f, 1, 1, 0, irr::video::SColor(0,255,255,255), 1.0f, 0.0f);
- Vertices[3] = irr::video::S3DVertex( 1.0f, -1.0f, 0.0f, 1, 1, 0, irr::video::SColor(0,255,255,255), 1.0f, 1.0f);
- // define indices for triangles
- Indices[0] = 0;
- Indices[1] = 1;
- Indices[2] = 2;
- Indices[3] = 0;
- Indices[4] = 2;
- Indices[5] = 3;
- // turn off lighting as default
- Material.setFlag(video::EMF_LIGHTING, false);
- // set texture warp settings to clamp to edge pixel
- for (u32 i = 0; i < video::MATERIAL_MAX_TEXTURES; i++)
- {
- Material.TextureLayer[i].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
- Material.TextureLayer[i].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
- }
- }
- virtual ~ScreenQuad() {}
- //! render the screen quad
- virtual void render()
- {
- // set the material of screen quad
- Driver->setMaterial(Material);
- // set matrices to fit the quad to full viewport
- Driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
- Driver->setTransform(video::ETS_VIEW, core::IdentityMatrix);
- Driver->setTransform(video::ETS_PROJECTION, core::IdentityMatrix);
- // draw screen quad
- Driver->drawVertexPrimitiveList(Vertices, 4, Indices, 2);
- }
- //! sets a flag of material to a new value
- virtual void setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)
- {
- Material.setFlag(flag, newvalue);
- }
- //! sets the texture of the specified layer in material to the new texture.
- void setMaterialTexture(u32 textureLayer, video::ITexture* texture)
- {
- Material.setTexture(textureLayer, texture);
- }
- //! sets the material type to a new material type.
- virtual void setMaterialType(video::E_MATERIAL_TYPE newType)
- {
- Material.MaterialType = newType;
- }
- private:
- video::IVideoDriver *Driver;
- video::S3DVertex Vertices[4];
- u16 Indices[6];
- video::SMaterial Material;
- };
- /*
- We start up the engine just like before. Then shader programs are selected
- according to the driver type.
- */
- int main()
- {
- // ask user for driver
- video::E_DRIVER_TYPE driverType=driverChoiceConsole();
- if (driverType==video::EDT_COUNT)
- return 1;
- // create device
- device = createDevice(driverType, core::dimension2d<u32>(640, 480));
- if (device == 0)
- return 1; // could not create selected driver.
- video::IVideoDriver* driver = device->getVideoDriver();
- scene::ISceneManager* smgr = device->getSceneManager();
- /*
- In this example, high level post processing shaders are loaded for both
- Direct3D and OpenGL drivers.
- File pp_d3d9.hlsl is for Direct3D 9, and pp_opengl.frag/pp_opengl.vert
- are for OpenGL.
- */
- const io::path mediaPath = getExampleMediaPath();
- io::path vsFileName; // filename for the vertex shader
- io::path psFileName; // filename for the pixel shader
- switch(driverType)
- {
- case video::EDT_DIRECT3D9:
- psFileName = mediaPath + "pp_d3d9.hlsl";
- vsFileName = psFileName; // both shaders are in the same file
- break;
- case video::EDT_OPENGL:
- psFileName = mediaPath + "pp_opengl.frag";
- vsFileName = mediaPath + "pp_opengl.vert";
- break;
- }
- /*
- Check for hardware capability of executing the corresponding shaders
- on selected renderer. This is not necessary though.
- */
- if (!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
- !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1))
- {
- device->getLogger()->log("WARNING: Pixel shaders disabled "\
- "because of missing driver/hardware support.");
- psFileName = "";
- }
- if (!driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
- !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1))
- {
- device->getLogger()->log("WARNING: Vertex shaders disabled "\
- "because of missing driver/hardware support.");
- vsFileName = "";
- }
- /*
- An animated mesh is loaded to be displayed. As in most examples,
- we'll take the fairy md2 model.
- */
- // load and display animated fairy mesh
- scene::IAnimatedMeshSceneNode* fairy = smgr->addAnimatedMeshSceneNode(
- smgr->getMesh(mediaPath + "faerie.md2"));
- if (fairy)
- {
- fairy->setMaterialTexture(0,
- driver->getTexture(mediaPath + "faerie2.bmp")); // set diffuse texture
- fairy->setMaterialFlag(video::EMF_LIGHTING, false); // disable dynamic lighting
- fairy->setPosition(core::vector3df(-10,0,-100));
- fairy->setMD2Animation ( scene::EMAT_STAND );
- }
- // add scene camera
- smgr->addCameraSceneNode(0, core::vector3df(10,10,-80),
- core::vector3df(-10,10,-100));
- /*
- We create a render target texture (RTT) with the same size as frame buffer.
- Instead of rendering the scene directly to the frame buffer, we firstly
- render it to this RTT. Post processing is then applied based on this RTT.
- RTT size needs not to be the same with frame buffer though. However in this
- example, we expect the result of rendering to RTT to be consistent with the
- result of rendering directly to the frame buffer. Therefore, the size of
- RTT keeps the same with frame buffer.
- */
- // create render target
- if (driver->queryFeature(video::EVDF_RENDER_TO_TARGET))
- {
- rt = driver->addRenderTargetTexture(core::dimension2d<u32>(640, 480), "RTT1");
- }
- else
- {
- device->getLogger()->log("Your hardware or this renderer is not able to use the "\
- "render to texture feature. RTT Disabled.");
- }
- /*
- Post processing is achieved by rendering a screen quad with this RTT (with
- previously rendered result) as a texture on the quad. A screen quad is
- geometry of flat plane composed of two adjacent triangles covering the
- entire area of viewport. In this pass of rendering, RTT works just like
- a normal texture and is drawn on the quad during rendering. We can then
- take control of this rendering process by applying various shader-defined
- materials to the quad. In other words, we can achieve different effect by
- writing different shaders.
- This process is called post processing because it normally does not rely
- on scene geometry. The inputs of this process are just textures, or in
- other words, just images. With the help of screen quad, we can draw these
- images on the screen with different effects. For example, we can adjust
- contrast, make grayscale, add noise, do more fancy effect such as blur,
- bloom, ghost, or just like in this example, we invert the color to produce
- negative image.
- Note that post processing is not limited to use only one texture. It can
- take multiple textures as shader inputs to provide desired result. In
- addition, post processing can also be chained to produce compound result.
- */
- // we create a screen quad
- ScreenQuad *screenQuad = new ScreenQuad(driver);
- // turn off mip maps and bilinear filter since we do not want interpolated result
- screenQuad->setMaterialFlag(video::EMF_USE_MIP_MAPS, false);
- screenQuad->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
- // set quad texture to RTT we just create
- screenQuad->setMaterialTexture(0, rt);
- /*
- Let's create material for the quad. Like in other example, we create material
- using IGPUProgrammingServices and call addShaderMaterialFromFiles, which
- returns a material type identifier.
- */
- // create materials
- video::IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
- s32 ppMaterialType = 0;
- if (gpu)
- {
- // We write a QuadShaderCallBack class that implements OnSetConstants
- // callback of IShaderConstantSetCallBack class at the beginning of
- // this tutorial. We set shader constants in this callback.
-
- // create an instance of callback class
- QuadShaderCallBack* mc = new QuadShaderCallBack();
- // create material from post processing shaders
- ppMaterialType = gpu->addHighLevelShaderMaterialFromFiles(
- vsFileName, "vertexMain", video::EVST_VS_1_1,
- psFileName, "pixelMain", video::EPST_PS_1_1, mc);
- mc->drop();
- }
- // set post processing material type to the quad
- screenQuad->setMaterialType((video::E_MATERIAL_TYPE)ppMaterialType);
- /*
- Now draw everything. That's all.
- */
- int lastFPS = -1;
- while(device->run())
- {
- if (device->isWindowActive())
- {
- driver->beginScene(true, true, video::SColor(255,0,0,0));
- if (rt)
- {
- // draw scene into render target
- // set render target to RTT
- driver->setRenderTarget(rt, true, true, video::SColor(255,0,0,0));
- // draw scene to RTT just like normal rendering
- smgr->drawAll();
- // after rendering to RTT, we change render target back
- driver->setRenderTarget(0, true, true, video::SColor(255,0,0,0));
- // render screen quad to apply post processing
- screenQuad->render();
- }
- else
- {
- // draw scene normally
- smgr->drawAll();
- }
- driver->endScene();
- int fps = driver->getFPS();
- if (lastFPS != fps)
- {
- core::stringw str = L"Irrlicht Engine - Post processing example [";
- str += driver->getName();
- str += "] FPS:";
- str += fps;
- device->setWindowCaption(str.c_str());
- lastFPS = fps;
- }
- }
- }
- // do not forget to manually drop the screen quad
- screenQuad->drop();
- device->drop();
- return 0;
- }
- /*
- **/
|