main.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. /** Example 027 Post Processing
  2. This tutorial shows how to implement post processing for D3D9 and OpenGL with
  3. the engine. In order to do post processing, scene objects are firstly rendered
  4. to render target. With the help of screen quad, the render target texture
  5. is then drawn on the quad with shader-defined effects applied.
  6. This tutorial shows how to create a screen quad. It also shows how to create a
  7. render target texture and associate it with the quad. Effects are defined as
  8. shaders which are applied during rendering the quad with the render target
  9. texture attached to it.
  10. A simple color inverse example is presented in this tutorial. The effect is
  11. written in HLSL and GLSL.
  12. @author Boshen Guan
  13. We include all headers and define necessary variables as we have done before.
  14. */
  15. #include "driverChoice.h"
  16. #include "exampleHelper.h"
  17. #include <irrlicht.h>
  18. using namespace irr;
  19. #ifdef _MSC_VER
  20. #pragma comment(lib, "Irrlicht.lib")
  21. #endif
  22. /*
  23. We write a class derived from IShaderConstantSetCallBack class and implement
  24. OnSetConstants callback interface. In this callback, we will set constants
  25. used by the shader.
  26. In this example, our HLSL shader needs texture size as input in its vertex
  27. shader. Therefore, we set texture size in OnSetConstants callback using
  28. setVertexShaderConstant function.
  29. */
  30. IrrlichtDevice* device = 0;
  31. video::ITexture* rt = 0;
  32. class QuadShaderCallBack : public video::IShaderConstantSetCallBack
  33. {
  34. public:
  35. QuadShaderCallBack() : FirstUpdate(true), TextureSizeID(-1), TextureSamplerID(-1)
  36. { }
  37. virtual void OnSetConstants(video::IMaterialRendererServices* services,
  38. s32 userData)
  39. {
  40. core::dimension2d<u32> size = rt->getSize();
  41. // get texture size array
  42. f32 textureSize[] =
  43. {
  44. (f32)size.Width, (f32)size.Height
  45. };
  46. if ( FirstUpdate )
  47. {
  48. TextureSizeID = services->getVertexShaderConstantID("TextureSize");
  49. TextureSamplerID = services->getPixelShaderConstantID("TextureSampler");
  50. }
  51. // set texture size to vertex shader
  52. services->setVertexShaderConstant(TextureSizeID, reinterpret_cast<f32*>(textureSize), 2);
  53. // set texture for an OpenGL driver
  54. s32 textureLayer = 0;
  55. services->setPixelShaderConstant(TextureSamplerID, &textureLayer, 1);
  56. }
  57. private:
  58. bool FirstUpdate;
  59. s32 TextureSizeID;
  60. s32 TextureSamplerID;
  61. };
  62. class ScreenQuad : public IReferenceCounted
  63. {
  64. public:
  65. ScreenQuad(video::IVideoDriver* driver)
  66. : Driver(driver)
  67. {
  68. // --------------------------------> u
  69. // |[1](-1, 1)----------[2](1, 1)
  70. // | | ( 0, 0) / | (1, 0)
  71. // | | / |
  72. // | | / |
  73. // | | / |
  74. // | | / |
  75. // | | / |
  76. // | | / |
  77. // | | / |
  78. // | | / |
  79. // |[0](-1, -1)---------[3](1, -1)
  80. // | ( 0, 1) (1, 1)
  81. // V
  82. // v
  83. /*
  84. A screen quad is composed of two adjacent triangles with 4 vertices.
  85. Vertex [0], [1] and [2] create the first triangle and Vertex [0],
  86. [2] and [3] create the second one. To map texture on the quad, UV
  87. coordinates are assigned to the vertices. The origin of UV coordinate
  88. locates on the top-left corner. And the value of UVs range from 0 to 1.
  89. */
  90. // define vertices array
  91. 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);
  92. 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);
  93. 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);
  94. 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);
  95. // define indices for triangles
  96. Indices[0] = 0;
  97. Indices[1] = 1;
  98. Indices[2] = 2;
  99. Indices[3] = 0;
  100. Indices[4] = 2;
  101. Indices[5] = 3;
  102. // turn off lighting as default
  103. Material.setFlag(video::EMF_LIGHTING, false);
  104. // set texture warp settings to clamp to edge pixel
  105. for (u32 i = 0; i < video::MATERIAL_MAX_TEXTURES; i++)
  106. {
  107. Material.TextureLayer[i].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
  108. Material.TextureLayer[i].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
  109. }
  110. }
  111. virtual ~ScreenQuad() {}
  112. //! render the screen quad
  113. virtual void render()
  114. {
  115. // set the material of screen quad
  116. Driver->setMaterial(Material);
  117. // set matrices to fit the quad to full viewport
  118. Driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
  119. Driver->setTransform(video::ETS_VIEW, core::IdentityMatrix);
  120. Driver->setTransform(video::ETS_PROJECTION, core::IdentityMatrix);
  121. // draw screen quad
  122. Driver->drawVertexPrimitiveList(Vertices, 4, Indices, 2);
  123. }
  124. //! sets a flag of material to a new value
  125. virtual void setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)
  126. {
  127. Material.setFlag(flag, newvalue);
  128. }
  129. //! sets the texture of the specified layer in material to the new texture.
  130. void setMaterialTexture(u32 textureLayer, video::ITexture* texture)
  131. {
  132. Material.setTexture(textureLayer, texture);
  133. }
  134. //! sets the material type to a new material type.
  135. virtual void setMaterialType(video::E_MATERIAL_TYPE newType)
  136. {
  137. Material.MaterialType = newType;
  138. }
  139. private:
  140. video::IVideoDriver *Driver;
  141. video::S3DVertex Vertices[4];
  142. u16 Indices[6];
  143. video::SMaterial Material;
  144. };
  145. /*
  146. We start up the engine just like before. Then shader programs are selected
  147. according to the driver type.
  148. */
  149. int main()
  150. {
  151. // ask user for driver
  152. video::E_DRIVER_TYPE driverType=driverChoiceConsole();
  153. if (driverType==video::EDT_COUNT)
  154. return 1;
  155. // create device
  156. device = createDevice(driverType, core::dimension2d<u32>(640, 480));
  157. if (device == 0)
  158. return 1; // could not create selected driver.
  159. video::IVideoDriver* driver = device->getVideoDriver();
  160. scene::ISceneManager* smgr = device->getSceneManager();
  161. /*
  162. In this example, high level post processing shaders are loaded for both
  163. Direct3D and OpenGL drivers.
  164. File pp_d3d9.hlsl is for Direct3D 9, and pp_opengl.frag/pp_opengl.vert
  165. are for OpenGL.
  166. */
  167. const io::path mediaPath = getExampleMediaPath();
  168. io::path vsFileName; // filename for the vertex shader
  169. io::path psFileName; // filename for the pixel shader
  170. switch(driverType)
  171. {
  172. case video::EDT_DIRECT3D9:
  173. psFileName = mediaPath + "pp_d3d9.hlsl";
  174. vsFileName = psFileName; // both shaders are in the same file
  175. break;
  176. case video::EDT_OPENGL:
  177. psFileName = mediaPath + "pp_opengl.frag";
  178. vsFileName = mediaPath + "pp_opengl.vert";
  179. break;
  180. }
  181. /*
  182. Check for hardware capability of executing the corresponding shaders
  183. on selected renderer. This is not necessary though.
  184. */
  185. if (!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
  186. !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1))
  187. {
  188. device->getLogger()->log("WARNING: Pixel shaders disabled "\
  189. "because of missing driver/hardware support.");
  190. psFileName = "";
  191. }
  192. if (!driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
  193. !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1))
  194. {
  195. device->getLogger()->log("WARNING: Vertex shaders disabled "\
  196. "because of missing driver/hardware support.");
  197. vsFileName = "";
  198. }
  199. /*
  200. An animated mesh is loaded to be displayed. As in most examples,
  201. we'll take the fairy md2 model.
  202. */
  203. // load and display animated fairy mesh
  204. scene::IAnimatedMeshSceneNode* fairy = smgr->addAnimatedMeshSceneNode(
  205. smgr->getMesh(mediaPath + "faerie.md2"));
  206. if (fairy)
  207. {
  208. fairy->setMaterialTexture(0,
  209. driver->getTexture(mediaPath + "faerie2.bmp")); // set diffuse texture
  210. fairy->setMaterialFlag(video::EMF_LIGHTING, false); // disable dynamic lighting
  211. fairy->setPosition(core::vector3df(-10,0,-100));
  212. fairy->setMD2Animation ( scene::EMAT_STAND );
  213. }
  214. // add scene camera
  215. smgr->addCameraSceneNode(0, core::vector3df(10,10,-80),
  216. core::vector3df(-10,10,-100));
  217. /*
  218. We create a render target texture (RTT) with the same size as frame buffer.
  219. Instead of rendering the scene directly to the frame buffer, we firstly
  220. render it to this RTT. Post processing is then applied based on this RTT.
  221. RTT size needs not to be the same with frame buffer though. However in this
  222. example, we expect the result of rendering to RTT to be consistent with the
  223. result of rendering directly to the frame buffer. Therefore, the size of
  224. RTT keeps the same with frame buffer.
  225. */
  226. // create render target
  227. if (driver->queryFeature(video::EVDF_RENDER_TO_TARGET))
  228. {
  229. rt = driver->addRenderTargetTexture(core::dimension2d<u32>(640, 480), "RTT1");
  230. }
  231. else
  232. {
  233. device->getLogger()->log("Your hardware or this renderer is not able to use the "\
  234. "render to texture feature. RTT Disabled.");
  235. }
  236. /*
  237. Post processing is achieved by rendering a screen quad with this RTT (with
  238. previously rendered result) as a texture on the quad. A screen quad is
  239. geometry of flat plane composed of two adjacent triangles covering the
  240. entire area of viewport. In this pass of rendering, RTT works just like
  241. a normal texture and is drawn on the quad during rendering. We can then
  242. take control of this rendering process by applying various shader-defined
  243. materials to the quad. In other words, we can achieve different effect by
  244. writing different shaders.
  245. This process is called post processing because it normally does not rely
  246. on scene geometry. The inputs of this process are just textures, or in
  247. other words, just images. With the help of screen quad, we can draw these
  248. images on the screen with different effects. For example, we can adjust
  249. contrast, make grayscale, add noise, do more fancy effect such as blur,
  250. bloom, ghost, or just like in this example, we invert the color to produce
  251. negative image.
  252. Note that post processing is not limited to use only one texture. It can
  253. take multiple textures as shader inputs to provide desired result. In
  254. addition, post processing can also be chained to produce compound result.
  255. */
  256. // we create a screen quad
  257. ScreenQuad *screenQuad = new ScreenQuad(driver);
  258. // turn off mip maps and bilinear filter since we do not want interpolated result
  259. screenQuad->setMaterialFlag(video::EMF_USE_MIP_MAPS, false);
  260. screenQuad->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
  261. // set quad texture to RTT we just create
  262. screenQuad->setMaterialTexture(0, rt);
  263. /*
  264. Let's create material for the quad. Like in other example, we create material
  265. using IGPUProgrammingServices and call addShaderMaterialFromFiles, which
  266. returns a material type identifier.
  267. */
  268. // create materials
  269. video::IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
  270. s32 ppMaterialType = 0;
  271. if (gpu)
  272. {
  273. // We write a QuadShaderCallBack class that implements OnSetConstants
  274. // callback of IShaderConstantSetCallBack class at the beginning of
  275. // this tutorial. We set shader constants in this callback.
  276. // create an instance of callback class
  277. QuadShaderCallBack* mc = new QuadShaderCallBack();
  278. // create material from post processing shaders
  279. ppMaterialType = gpu->addHighLevelShaderMaterialFromFiles(
  280. vsFileName, "vertexMain", video::EVST_VS_1_1,
  281. psFileName, "pixelMain", video::EPST_PS_1_1, mc);
  282. mc->drop();
  283. }
  284. // set post processing material type to the quad
  285. screenQuad->setMaterialType((video::E_MATERIAL_TYPE)ppMaterialType);
  286. /*
  287. Now draw everything. That's all.
  288. */
  289. int lastFPS = -1;
  290. while(device->run())
  291. {
  292. if (device->isWindowActive())
  293. {
  294. driver->beginScene(true, true, video::SColor(255,0,0,0));
  295. if (rt)
  296. {
  297. // draw scene into render target
  298. // set render target to RTT
  299. driver->setRenderTarget(rt, true, true, video::SColor(255,0,0,0));
  300. // draw scene to RTT just like normal rendering
  301. smgr->drawAll();
  302. // after rendering to RTT, we change render target back
  303. driver->setRenderTarget(0, true, true, video::SColor(255,0,0,0));
  304. // render screen quad to apply post processing
  305. screenQuad->render();
  306. }
  307. else
  308. {
  309. // draw scene normally
  310. smgr->drawAll();
  311. }
  312. driver->endScene();
  313. int fps = driver->getFPS();
  314. if (lastFPS != fps)
  315. {
  316. core::stringw str = L"Irrlicht Engine - Post processing example [";
  317. str += driver->getName();
  318. str += "] FPS:";
  319. str += fps;
  320. device->setWindowCaption(str.c_str());
  321. lastFPS = fps;
  322. }
  323. }
  324. }
  325. // do not forget to manually drop the screen quad
  326. screenQuad->drop();
  327. device->drop();
  328. return 0;
  329. }
  330. /*
  331. **/