PageRenderTime 40ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/Graphics/OpenTK/OpenTKRenderToTexture.cs

#
C# | 418 lines | 315 code | 42 blank | 61 comment | 28 complexity | 221ee710a2e014a27c6ebab58d90125a MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using Delta.Engine;
  3. using Delta.Graphics.BaseOpenGL;
  4. using Delta.Utilities;
  5. using Delta.Utilities.Datatypes;
  6. using Delta.Utilities.Helpers;
  7. using OpenTK.Graphics.OpenGL;
  8. namespace Delta.Graphics.OpenTK
  9. {
  10. /// <summary>
  11. /// OpenGL implementation of RenderToTexture using FBO.
  12. /// </summary>
  13. internal class OpenTKRenderToTexture : BaseOpenGLRenderToTexture
  14. {
  15. #region Constructors
  16. /// <summary>
  17. /// Creates a OpenTKRenderTexture. Just passes everything on, which is
  18. /// important for the RenderToTexture.Create via the Factory.
  19. /// </summary>
  20. /// <param name="setTextureName">Name of the set texture.</param>
  21. /// <param name="setSize">Size of the set.</param>
  22. /// <param name="setIsDepthTexture">If set to <c>true</c> the texture
  23. /// target should be a depth texture</param>
  24. /// <param name="setIsShadowProjected">If set to <c>true</c> the texture
  25. /// target is projected shadow texture to be used in shadow mapping.
  26. /// </param>
  27. /// <param name="setUseStencilBuffer">
  28. /// Use the stencil flag for this RenderToTexture instance?
  29. /// </param>
  30. public OpenTKRenderToTexture(string setTextureName,
  31. Size setSize, bool setIsDepthTexture, bool setIsShadowProjected,
  32. bool setUseStencilBuffer)
  33. : base(setTextureName, setSize, setIsDepthTexture,
  34. setIsShadowProjected, setUseStencilBuffer)
  35. {
  36. // FBO creation happens dynamically.
  37. }
  38. #endregion
  39. #region Methods (Private)
  40. #region CreateFbo
  41. /// <summary>
  42. /// Create frame buffer object for render to texture, called when this
  43. /// render target is first used.
  44. /// </summary>
  45. private void CreateFbo()
  46. {
  47. bool supportFbo = OpenTKGraphic.SupportsExtension(
  48. "GL_EXT_framebuffer_object");
  49. if (supportFbo == false)
  50. {
  51. Log.Warning(
  52. "Can't create FBO RenderTexture. Please update your graphic card.");
  53. return;
  54. }
  55. if (UseStencilBuffer)
  56. {
  57. GL.Enable(EnableCap.StencilTest);
  58. GL.ClearStencil((int)StencilColor.PackedRGBA);
  59. }
  60. GL.GenTextures(1, out textureId);
  61. GL.BindTexture(TextureTarget.Texture2D, textureId);
  62. if (IsDepthTexture && UseStencilBuffer)
  63. {
  64. PixelInternalFormat depthFormat = PixelInternalFormat.DepthStencil;
  65. switch (Settings.Extra.DepthBufferSize)
  66. {
  67. case 16:
  68. Log.Warning("Cannot create depthbuffer, 16 bit depthbuffer " +
  69. "not supported in combination with a stencilbuffer.");
  70. return;
  71. case 24:
  72. depthFormat = PixelInternalFormat.Depth24Stencil8;
  73. break;
  74. case 32:
  75. depthFormat = PixelInternalFormat.Depth32fStencil8;
  76. break;
  77. }
  78. GL.TexImage2D(TextureTarget.Texture2D, 0,
  79. depthFormat, (int)PixelSize.Width,
  80. (int)PixelSize.Height, 0, PixelFormat.DepthStencil,
  81. PixelType.UnsignedByte, IntPtr.Zero);
  82. int textureFiler = (int)TextureMinFilter.Nearest;
  83. int textureAddress = (int)TextureWrapMode.ClampToEdge;
  84. // If texture is used as shadow2DProj, apply linear for
  85. // nice looking shadow
  86. if (IsShadowProjected)
  87. {
  88. textureFiler = (int)TextureMinFilter.Linear;
  89. textureAddress = (int)TextureWrapMode.ClampToEdge;
  90. // This is quite important
  91. GL.TexParameter(TextureTarget.Texture2D,
  92. TextureParameterName.TextureCompareMode,
  93. (int)TextureCompareMode.CompareRToTexture);
  94. GL.TexParameter(TextureTarget.Texture2D,
  95. TextureParameterName.TextureCompareFunc,
  96. (int)All.Lequal);
  97. GL.TexParameter(TextureTarget.Texture2D,
  98. TextureParameterName.DepthTextureMode,
  99. (int)All.Intensity);
  100. }
  101. GL.TexParameter(TextureTarget.Texture2D,
  102. TextureParameterName.TextureMinFilter,
  103. textureFiler);
  104. GL.TexParameter(TextureTarget.Texture2D,
  105. TextureParameterName.TextureMagFilter,
  106. textureFiler);
  107. GL.TexParameter(TextureTarget.Texture2D,
  108. TextureParameterName.TextureWrapS, textureAddress);
  109. GL.TexParameter(TextureTarget.Texture2D,
  110. TextureParameterName.TextureWrapT, textureAddress);
  111. }
  112. else if (IsDepthTexture)
  113. {
  114. PixelInternalFormat depthFormat = PixelInternalFormat.DepthComponent;
  115. switch (Settings.Extra.DepthBufferSize)
  116. {
  117. case 16:
  118. depthFormat = PixelInternalFormat.DepthComponent16;
  119. break;
  120. case 24:
  121. depthFormat = PixelInternalFormat.DepthComponent24;
  122. break;
  123. case 32:
  124. depthFormat = PixelInternalFormat.DepthComponent32;
  125. break;
  126. }
  127. GL.TexImage2D(TextureTarget.Texture2D, 0,
  128. depthFormat, (int)PixelSize.Width,
  129. (int)PixelSize.Height, 0, PixelFormat.DepthComponent,
  130. PixelType.UnsignedByte, IntPtr.Zero);
  131. int textureFiler = (int)TextureMinFilter.Nearest;
  132. int textureAddress = (int)TextureWrapMode.ClampToEdge;
  133. // If texture is used as shadow2DProj, apply linear for
  134. // nice looking shadow
  135. if (IsShadowProjected)
  136. {
  137. textureFiler = (int)TextureMinFilter.Linear;
  138. textureAddress = (int)TextureWrapMode.ClampToEdge;
  139. // This is quite important
  140. GL.TexParameter(TextureTarget.Texture2D,
  141. TextureParameterName.TextureCompareMode,
  142. (int)TextureCompareMode.CompareRToTexture);
  143. GL.TexParameter(TextureTarget.Texture2D,
  144. TextureParameterName.TextureCompareFunc,
  145. (int)All.Lequal);
  146. GL.TexParameter(TextureTarget.Texture2D,
  147. TextureParameterName.DepthTextureMode,
  148. (int)All.Intensity);
  149. }
  150. GL.TexParameter(TextureTarget.Texture2D,
  151. TextureParameterName.TextureMinFilter,
  152. textureFiler);
  153. GL.TexParameter(TextureTarget.Texture2D,
  154. TextureParameterName.TextureMagFilter,
  155. textureFiler);
  156. GL.TexParameter(TextureTarget.Texture2D,
  157. TextureParameterName.TextureWrapS, textureAddress);
  158. GL.TexParameter(TextureTarget.Texture2D,
  159. TextureParameterName.TextureWrapT, textureAddress);
  160. }
  161. else if (UseStencilBuffer)
  162. {
  163. GL.TexParameter(TextureTarget.Texture2D,
  164. TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
  165. GL.TexParameter(TextureTarget.Texture2D,
  166. TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
  167. GL.TexParameter(TextureTarget.Texture2D,
  168. TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToBorder);
  169. GL.TexParameter(TextureTarget.Texture2D,
  170. TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToBorder);
  171. GL.TexImage2D(TextureTarget.Texture2D, 0, (PixelInternalFormat)All.StencilIndex,
  172. (int)PixelSize.Width, (int)PixelSize.Height, 0, PixelFormat.StencilIndex,
  173. PixelType.UnsignedByte, IntPtr.Zero);
  174. }
  175. else
  176. {
  177. GL.TexParameter(TextureTarget.Texture2D,
  178. TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
  179. GL.TexParameter(TextureTarget.Texture2D,
  180. TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
  181. GL.TexParameter(TextureTarget.Texture2D,
  182. TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToBorder);
  183. GL.TexParameter(TextureTarget.Texture2D,
  184. TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToBorder);
  185. GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8,
  186. (int)PixelSize.Width, (int)PixelSize.Height, 0, PixelFormat.Rgba,
  187. PixelType.UnsignedByte, IntPtr.Zero);
  188. }
  189. GL.BindTexture(TextureTarget.Texture2D, 0);
  190. // Create a frame buffer object
  191. GL.Ext.GenFramebuffers(1, out fboId);
  192. GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, fboId);
  193. if (UseStencilBuffer)
  194. {
  195. GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt,
  196. FramebufferAttachment.StencilAttachmentExt, TextureTarget.Texture2D,
  197. textureId, 0);
  198. }
  199. if (IsDepthTexture)
  200. {
  201. GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt,
  202. FramebufferAttachment.DepthAttachmentExt, TextureTarget.Texture2D,
  203. textureId, 0);
  204. // Disable drawing to any buffers, we only want the depth
  205. GL.DrawBuffer(DrawBufferMode.None);
  206. GL.ReadBuffer((ReadBufferMode)All.None);
  207. }
  208. else
  209. {
  210. // Attach color texture to FBO handle
  211. GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt,
  212. FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D,
  213. textureId, 0);
  214. }
  215. #region Error check
  216. // Check FBO status
  217. FramebufferErrorCode fboStatus = GL.Ext.CheckFramebufferStatus(
  218. FramebufferTarget.FramebufferExt);
  219. switch (fboStatus)
  220. {
  221. case FramebufferErrorCode.FramebufferCompleteExt:
  222. // No need to log, it is all good.
  223. break;
  224. case FramebufferErrorCode.FramebufferIncompleteAttachmentExt:
  225. Log.Warning(
  226. "FBO: One or more attachment points are not framebuffer " +
  227. "attachment complete. This could mean there’s no texture " +
  228. "attached or the format isn’t renderable. For color textures " +
  229. "this means the base format must be RGB or RGBA and for depth " +
  230. "textures it must be a DEPTH_COMPONENT format. Other causes of " +
  231. "this error are that the width or height is zero or the " +
  232. "z-offset is out of range in case of render to volume.");
  233. break;
  234. case FramebufferErrorCode.FramebufferIncompleteMissingAttachmentExt:
  235. Log.Warning("FBO: There are no attachments.");
  236. break;
  237. case FramebufferErrorCode.FramebufferIncompleteDimensionsExt:
  238. Log.Warning(
  239. "FBO: Attachments are of different size. All attachments must " +
  240. "have the same width and height.");
  241. break;
  242. case FramebufferErrorCode.FramebufferIncompleteFormatsExt:
  243. Log.Warning(
  244. "FBO: The color attachments have different format. All color " +
  245. "attachments must have the same format.");
  246. break;
  247. case FramebufferErrorCode.FramebufferIncompleteDrawBufferExt:
  248. Log.Warning(
  249. "FBO: An attachment point referenced by GL.DrawBuffers() " +
  250. "doesn’t have an attachment.");
  251. break;
  252. case FramebufferErrorCode.FramebufferIncompleteReadBufferExt:
  253. Log.Warning(
  254. "FBO: The attachment point referenced by GL.ReadBuffers() " +
  255. "doesn’t have an attachment.");
  256. break;
  257. case FramebufferErrorCode.FramebufferUnsupportedExt:
  258. Log.Warning(
  259. "FBO: This particular FBO configuration is not supported by the " +
  260. "implementation.");
  261. break;
  262. default:
  263. Log.Warning("FBO: Status unknown (yes, this is really bad.)");
  264. break;
  265. }
  266. if (UseStencilBuffer)
  267. {
  268. int value = 0;
  269. GL.GetInteger(GetPName.StencilBits, out value);
  270. if (value == 0)
  271. {
  272. Log.Warning("FBO: No stencil buffer found.");
  273. }
  274. }
  275. // using FBO might have changed states, e.g. the FBO might not support
  276. // stereoscopic views or double buffering
  277. ErrorCode gLGetError = GL.GetError();
  278. if (gLGetError != ErrorCode.NoError)
  279. {
  280. Log.Warning("Last GL Error: " + gLGetError);
  281. }
  282. #endregion
  283. // switch back to window-system-provided framebuffer
  284. GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
  285. }
  286. #endregion
  287. #region Bind
  288. /// <summary>
  289. /// Implementation of Bind.
  290. /// </summary>
  291. protected override void Bind()
  292. {
  293. // All the context turn on
  294. GL.PushAttrib(AttribMask.ViewportBit);
  295. // Change screen aspect ration.
  296. ScreenSpace.CalculateAspectRatio(PixelSize.Width, PixelSize.Height);
  297. // In the case we render the shadow map to a higher resolution,
  298. // the viewport must be modified accordingly.
  299. GL.Viewport(0, 0, (int)PixelSize.Width, (int)PixelSize.Height);
  300. GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, fboId);
  301. ClearBufferMask clearMask = ClearBufferMask.ColorBufferBit;
  302. // Clear with black color if depth rendering
  303. if (IsDepthTexture)
  304. {
  305. GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  306. // Disable color rendering, we only want to write to the depth buffer
  307. GL.ColorMask(false, false, false, false);
  308. // Culling switching, rendering only backface, this is done to avoid self-shadowing
  309. GL.CullFace(CullFaceMode.Front);
  310. clearMask |= ClearBufferMask.DepthBufferBit;
  311. }
  312. // Add stencilbuffer mask if required
  313. if (UseStencilBuffer)
  314. {
  315. clearMask |= ClearBufferMask.StencilBufferBit;
  316. GL.Enable(EnableCap.StencilTest);
  317. GL.ClearStencil((int)StencilColor.PackedRGBA);
  318. GL.ColorMask(false, false, false, false);
  319. GL.StencilFunc(StencilFunction.Always, 1, 1);
  320. GL.StencilOp(StencilOp.Replace, StencilOp.Replace, StencilOp.Replace);
  321. }
  322. var backColor = Application.BackgroundColor;
  323. GL.ClearColor(backColor.R, backColor.G, backColor.B, backColor.A);
  324. // Clear previous frame values
  325. GL.Clear(clearMask);
  326. }
  327. #endregion
  328. #region Unbind
  329. /// <summary>
  330. /// Implementation of Unbind.
  331. /// </summary>
  332. protected override void Unbind()
  333. {
  334. GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
  335. GL.PopAttrib();
  336. // Reset the original aspect ration and screen size.
  337. ScreenSpace.CalculateAspectRatio();
  338. if (IsDepthTexture)
  339. {
  340. // When depth rendering enable color mask, disabled on Bind methods
  341. GL.ColorMask(true, true, true, true);
  342. // Restore cull mode
  343. GL.CullFace(CullFaceMode.Back);
  344. }
  345. }
  346. #endregion
  347. #region MakeSureRenderTargetIsInitialized
  348. /// <summary>
  349. /// Make sure is render target is initialized, called before render to
  350. /// make sure we can actually use this class.
  351. /// </summary>
  352. protected override void MakeSureRenderTargetIsInitialized()
  353. {
  354. if (fboId == MathHelper.InvalidIndex)
  355. {
  356. CreateFbo();
  357. }
  358. }
  359. #endregion
  360. #region DisposeNativeData
  361. /// <summary>
  362. /// Disposes fbo texture and gl texture too.
  363. /// </summary>
  364. protected override void DisposeNativeData()
  365. {
  366. if (fboId != MathHelper.InvalidIndex)
  367. {
  368. GL.Ext.DeleteFramebuffers(1, ref fboId);
  369. GL.DeleteTexture(textureId);
  370. fboId = MathHelper.InvalidIndex;
  371. }
  372. }
  373. #endregion
  374. #endregion
  375. }
  376. }