COpenGLNormalMapRenderer.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. // Copyright (C) 2002-2012 Nikolaus Gebhardt
  2. // This file is part of the "Irrlicht Engine".
  3. // For conditions of distribution and use, see copyright notice in irrlicht.h
  4. #include "IrrCompileConfig.h"
  5. #ifdef _IRR_COMPILE_WITH_OPENGL_
  6. #include "COpenGLNormalMapRenderer.h"
  7. #include "IGPUProgrammingServices.h"
  8. #include "IShaderConstantSetCallBack.h"
  9. #include "IVideoDriver.h"
  10. #include "os.h"
  11. #include "COpenGLDriver.h"
  12. namespace irr
  13. {
  14. namespace video
  15. {
  16. // Irrlicht Engine OpenGL render path normal map vertex shader
  17. // I guess it could be optimized a lot, because I wrote it in D3D ASM and
  18. // transferred it 1:1 to OpenGL
  19. const char OPENGL_NORMAL_MAP_VSH[] =
  20. "!!ARBvp1.0\n"\
  21. "#input\n"\
  22. "# 0-3: transposed world matrix;\n"\
  23. "#;12: Light01 position \n"\
  24. "#;13: x,y,z: Light01 color; .w: 1/LightRadius^2 \n"\
  25. "#;14: Light02 position \n"\
  26. "#;15: x,y,z: Light02 color; .w: 1/LightRadius^2 \n"\
  27. "\n"\
  28. "ATTRIB InPos = vertex.position;\n"\
  29. "ATTRIB InColor = vertex.color;\n"\
  30. "ATTRIB InNormal = vertex.normal;\n"\
  31. "ATTRIB InTexCoord = vertex.texcoord[0];\n"\
  32. "ATTRIB InTangent = vertex.texcoord[1];\n"\
  33. "ATTRIB InBinormal = vertex.texcoord[2];\n"\
  34. "\n"\
  35. "#output\n"\
  36. "OUTPUT OutPos = result.position;\n"\
  37. "OUTPUT OutLightColor1 = result.color.primary;\n"\
  38. "OUTPUT OutLightColor2 = result.color.secondary;\n"\
  39. "OUTPUT OutTexCoord = result.texcoord[0];\n"\
  40. "OUTPUT OutLightVector1 = result.texcoord[1];\n"\
  41. "OUTPUT OutLightVector2 = result.texcoord[2];\n"\
  42. "\n"\
  43. "PARAM MVP[4] = { state.matrix.mvp }; # modelViewProjection matrix.\n"\
  44. "TEMP Temp;\n"\
  45. "TEMP TempColor;\n"\
  46. "TEMP TempLightVector1;\n"\
  47. "TEMP TempLightVector2;\n"\
  48. "TEMP TempTransLightV1;\n"\
  49. "TEMP TempTransLightV2;\n"\
  50. "\n"\
  51. "# transform position to clip space \n"\
  52. "DP4 OutPos.x, MVP[0], InPos;\n"\
  53. "DP4 OutPos.y, MVP[1], InPos;\n"\
  54. "DP4 Temp.z, MVP[2], InPos;\n"\
  55. "DP4 OutPos.w, MVP[3], InPos;\n"\
  56. "MOV OutPos.z, Temp.z;\n"\
  57. "MOV result.fogcoord.x, Temp.z;\n"\
  58. "\n"\
  59. "# vertex - lightpositions \n"\
  60. "SUB TempLightVector1, program.local[12], InPos; \n"\
  61. "SUB TempLightVector2, program.local[14], InPos; \n"\
  62. "\n"\
  63. "# transform the light vector 1 with U, V, W \n"\
  64. "DP3 TempTransLightV1.x, InTangent, TempLightVector1; \n"\
  65. "DP3 TempTransLightV1.y, InBinormal, TempLightVector1; \n"\
  66. "DP3 TempTransLightV1.z, InNormal, TempLightVector1; \n"\
  67. "\n"\
  68. "# transform the light vector 2 with U, V, W \n"\
  69. "DP3 TempTransLightV2.x, InTangent, TempLightVector2; \n"\
  70. "DP3 TempTransLightV2.y, InBinormal, TempLightVector2; \n"\
  71. "DP3 TempTransLightV2.z, InNormal, TempLightVector2; \n"\
  72. "\n"\
  73. "# normalize light vector 1 \n"\
  74. "DP3 TempTransLightV1.w, TempTransLightV1, TempTransLightV1; \n"\
  75. "RSQ TempTransLightV1.w, TempTransLightV1.w; \n"\
  76. "MUL TempTransLightV1, TempTransLightV1, TempTransLightV1.w;\n"\
  77. "\n"\
  78. "# normalize light vector 2 \n"\
  79. "DP3 TempTransLightV2.w, TempTransLightV2, TempTransLightV2; \n"\
  80. "RSQ TempTransLightV2.w, TempTransLightV2.w; \n"\
  81. "MUL TempTransLightV2, TempTransLightV2, TempTransLightV2.w;\n"\
  82. "\n"\
  83. "\n"\
  84. "# move light vectors out\n"\
  85. "MAD OutLightVector1, TempTransLightV1, {0.5,0.5,0.5,0.5}, {0.5,0.5,0.5,0.5}; \n"\
  86. "MAD OutLightVector2, TempTransLightV2, {0.5,0.5,0.5,0.5}, {0.5,0.5,0.5,0.5}; \n"\
  87. "\n"\
  88. "# calculate attenuation of light 1\n"\
  89. "MOV TempLightVector1.w, {0,0,0,0}; \n"\
  90. "DP3 TempLightVector1.x, TempLightVector1, TempLightVector1; \n"\
  91. "MUL TempLightVector1.x, TempLightVector1.x, program.local[13].w; \n"\
  92. "RSQ TempLightVector1, TempLightVector1.x; \n"\
  93. "MUL OutLightColor1, TempLightVector1, program.local[13]; # resulting light color = lightcolor * attenuation \n"\
  94. "\n"\
  95. "# calculate attenuation of light 2\n"\
  96. "MOV TempLightVector2.w, {0,0,0,0}; \n"\
  97. "DP3 TempLightVector2.x, TempLightVector2, TempLightVector2; \n"\
  98. "MUL TempLightVector2.x, TempLightVector2.x, program.local[15].w; \n"\
  99. "RSQ TempLightVector2, TempLightVector2.x; \n"\
  100. "MUL OutLightColor2, TempLightVector2, program.local[15]; # resulting light color = lightcolor * attenuation \n"\
  101. "\n"\
  102. "# move out texture coordinates and original alpha value\n"\
  103. "MOV OutTexCoord, InTexCoord; \n"\
  104. "MOV OutLightColor1.w, InColor.w; \n"\
  105. "\n"\
  106. "END\n";
  107. // Irrlicht Engine OpenGL render path normal map pixel shader
  108. // I guess it could be optimized a bit, because I wrote it in D3D ASM and
  109. // transfered it 1:1 to OpenGL
  110. const char OPENGL_NORMAL_MAP_PSH[] =
  111. "!!ARBfp1.0\n"\
  112. "#_IRR_FOG_MODE_\n"\
  113. "\n"\
  114. "#Input\n"\
  115. "ATTRIB inTexCoord = fragment.texcoord[0]; \n"\
  116. "ATTRIB light1Vector = fragment.texcoord[1]; \n"\
  117. "ATTRIB light2Vector = fragment.texcoord[2]; \n"\
  118. "ATTRIB light1Color = fragment.color.primary; \n"\
  119. "ATTRIB light2Color = fragment.color.secondary; \n"\
  120. "\n"\
  121. "#Output\n"\
  122. "OUTPUT outColor = result.color;\n"\
  123. "TEMP temp;\n"\
  124. "TEMP temp2;\n"\
  125. "TEMP colorMapColor;\n"\
  126. "TEMP normalMapColor;\n"\
  127. "\n"\
  128. "# fetch color and normal map; \n"\
  129. "TXP colorMapColor, inTexCoord, texture[0], 2D; \n"\
  130. "TXP normalMapColor, inTexCoord, texture[1], 2D; \n"\
  131. "\n"\
  132. "# calculate color of light1; \n"\
  133. "MAD normalMapColor, normalMapColor, {2,2,2,2}, {-1,-1,-1,-1}; \n"\
  134. "MAD temp, light1Vector, {2,2,2,2}, {-1,-1,-1,-1}; \n"\
  135. "DP3_SAT temp, normalMapColor, temp; \n"\
  136. "MUL temp, light1Color, temp; \n"\
  137. "\n"\
  138. "# calculate color of light2; \n"\
  139. "MAD temp2, light2Vector, {2,2,2,2}, {-1,-1,-1,-1}; \n"\
  140. "DP3_SAT temp2, normalMapColor, temp2; \n"\
  141. "MAD temp, light2Color, temp2, temp; \n"\
  142. "\n"\
  143. "# luminance * base color; \n"\
  144. "MUL outColor, temp, colorMapColor; \n"\
  145. "MOV outColor.a, light1Color.a; #write interpolated vertex alpha value\n"\
  146. "\n"\
  147. "END\n";
  148. //! Constructor
  149. COpenGLNormalMapRenderer::COpenGLNormalMapRenderer(video::COpenGLDriver* driver,
  150. s32& outMaterialTypeNr, IMaterialRenderer* baseMaterial)
  151. : COpenGLShaderMaterialRenderer(driver, 0, baseMaterial), CompiledShaders(true)
  152. {
  153. #ifdef _DEBUG
  154. setDebugName("COpenGLNormalMapRenderer");
  155. #endif
  156. // set this as callback. We could have done this in
  157. // the initialization list, but some compilers don't like it.
  158. CallBack = this;
  159. // basically, this thing simply compiles the hardcoded shaders if the
  160. // hardware is able to do them, otherwise it maps to the base material
  161. if (!driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1) ||
  162. !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1))
  163. {
  164. // this hardware is not able to do shaders. Fall back to
  165. // base material.
  166. outMaterialTypeNr = driver->addMaterialRenderer(this);
  167. return;
  168. }
  169. // check if already compiled normal map shaders are there.
  170. video::IMaterialRenderer* renderer = driver->getMaterialRenderer(EMT_NORMAL_MAP_SOLID);
  171. if (renderer)
  172. {
  173. // use the already compiled shaders
  174. video::COpenGLNormalMapRenderer* nmr = reinterpret_cast<video::COpenGLNormalMapRenderer*>(renderer);
  175. CompiledShaders = false;
  176. VertexShader = nmr->VertexShader;
  177. PixelShader = nmr->PixelShader;
  178. outMaterialTypeNr = driver->addMaterialRenderer(this);
  179. }
  180. else
  181. {
  182. // compile shaders on our own
  183. init(outMaterialTypeNr, OPENGL_NORMAL_MAP_VSH, OPENGL_NORMAL_MAP_PSH, EVT_TANGENTS);
  184. }
  185. // fallback if compilation has failed
  186. if (-1==outMaterialTypeNr)
  187. outMaterialTypeNr = driver->addMaterialRenderer(this);
  188. }
  189. //! Destructor
  190. COpenGLNormalMapRenderer::~COpenGLNormalMapRenderer()
  191. {
  192. if (CallBack == this)
  193. CallBack = 0;
  194. if (!CompiledShaders)
  195. {
  196. // prevent this from deleting shaders we did not create
  197. VertexShader = 0;
  198. PixelShader.clear();
  199. }
  200. }
  201. //! Returns the render capability of the material.
  202. s32 COpenGLNormalMapRenderer::getRenderCapability() const
  203. {
  204. if (Driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1) &&
  205. Driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1))
  206. return 0;
  207. return 1;
  208. }
  209. //! Called by the engine when the vertex and/or pixel shader constants for an
  210. //! material renderer should be set.
  211. void COpenGLNormalMapRenderer::OnSetConstants(IMaterialRendererServices* services, s32 userData)
  212. {
  213. video::IVideoDriver* driver = services->getVideoDriver();
  214. // set transposed world matrix
  215. const core::matrix4& tWorld = driver->getTransform(video::ETS_WORLD).getTransposed();
  216. services->setVertexShaderConstant(tWorld.pointer(), 0, 4);
  217. // set transposed worldViewProj matrix
  218. core::matrix4 worldViewProj(driver->getTransform(video::ETS_PROJECTION));
  219. worldViewProj *= driver->getTransform(video::ETS_VIEW);
  220. worldViewProj *= driver->getTransform(video::ETS_WORLD);
  221. core::matrix4 tr(worldViewProj.getTransposed());
  222. services->setVertexShaderConstant(tr.pointer(), 8, 4);
  223. // here we fetch the fixed function lights from the driver
  224. // and set them as constants
  225. u32 cnt = driver->getDynamicLightCount();
  226. // Load the inverse world matrix.
  227. core::matrix4 invWorldMat;
  228. driver->getTransform(video::ETS_WORLD).getInverse(invWorldMat);
  229. for (u32 i=0; i<2; ++i)
  230. {
  231. video::SLight light;
  232. if (i<cnt)
  233. light = driver->getDynamicLight(i);
  234. else
  235. {
  236. light.DiffuseColor.set(0,0,0); // make light dark
  237. light.Radius = 1.0f;
  238. }
  239. light.DiffuseColor.a = 1.0f/(light.Radius*light.Radius); // set attenuation
  240. // Transform the light by the inverse world matrix to get it into object space.
  241. invWorldMat.transformVect(light.Position);
  242. services->setVertexShaderConstant(
  243. reinterpret_cast<const f32*>(&light.Position), 12+(i*2), 1);
  244. services->setVertexShaderConstant(
  245. reinterpret_cast<const f32*>(&light.DiffuseColor), 13+(i*2), 1);
  246. }
  247. }
  248. } // end namespace video
  249. } // end namespace irr
  250. #endif