FrameBuffer.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /*
  2. * Copyright (c) 2009-2010 jMonkeyEngine
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are
  7. * met:
  8. *
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. *
  12. * * Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. *
  16. * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  22. * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  24. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  25. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  27. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  28. * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  29. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  30. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. */
  32. package com.jme3.texture;
  33. import com.jme3.renderer.Caps;
  34. import com.jme3.renderer.Renderer;
  35. import com.jme3.texture.Image.Format;
  36. import com.jme3.util.NativeObject;
  37. import java.util.ArrayList;
  38. /**
  39. * <p>
  40. * <code>FrameBuffer</code>s are rendering surfaces allowing
  41. * off-screen rendering and render-to-texture functionality.
  42. * Instead of the scene rendering to the screen, it is rendered into the
  43. * FrameBuffer, the result can be either a texture or a buffer.
  44. * <p>
  45. * A <code>FrameBuffer</code> supports two methods of rendering,
  46. * using a {@link Texture} or using a buffer.
  47. * When using a texture, the result of the rendering will be rendered
  48. * onto the texture, after which the texture can be placed on an object
  49. * and rendered as if the texture was uploaded from disk.
  50. * When using a buffer, the result is rendered onto
  51. * a buffer located on the GPU, the data of this buffer is not accessible
  52. * to the user. buffers are useful if one
  53. * wishes to retrieve only the color content of the scene, but still desires
  54. * depth testing (which requires a depth buffer).
  55. * Buffers can be copied to other framebuffers
  56. * including the main screen, by using
  57. * {@link Renderer#copyFrameBuffer(com.jme3.texture.FrameBuffer, com.jme3.texture.FrameBuffer) }.
  58. * The content of a {@link RenderBuffer} can be retrieved by using
  59. * {@link Renderer#readFrameBuffer(com.jme3.texture.FrameBuffer, java.nio.ByteBuffer) }.
  60. * <p>
  61. * <code>FrameBuffer</code>s have several attachment points, there are
  62. * several <em>color</em> attachment points and a single <em>depth</em>
  63. * attachment point.
  64. * The color attachment points support image formats such as
  65. * {@link Format#RGBA8}, allowing rendering the color content of the scene.
  66. * The depth attachment point requires a depth image format.
  67. *
  68. * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer)
  69. *
  70. * @author Kirill Vainer
  71. */
  72. public class FrameBuffer extends NativeObject {
  73. private int width = 0;
  74. private int height = 0;
  75. private int samples = 1;
  76. private ArrayList<RenderBuffer> colorBufs = new ArrayList<RenderBuffer>();
  77. private RenderBuffer depthBuf = null;
  78. private int colorBufIndex = 0;
  79. /**
  80. * <code>RenderBuffer</code> represents either a texture or a
  81. * buffer that will be rendered to. <code>RenderBuffer</code>s
  82. * are attached to an attachment slot on a <code>FrameBuffer</code>.
  83. */
  84. public class RenderBuffer {
  85. Texture tex;
  86. Image.Format format;
  87. int id = -1;
  88. int slot = -1;
  89. /**
  90. * @return The image format of the render buffer.
  91. */
  92. public Format getFormat() {
  93. return format;
  94. }
  95. /**
  96. * @return The texture to render to for this <code>RenderBuffer</code>
  97. * or null if content should be rendered into a buffer.
  98. */
  99. public Texture getTexture(){
  100. return tex;
  101. }
  102. /**
  103. * Do not use.
  104. */
  105. public int getId() {
  106. return id;
  107. }
  108. /**
  109. * Do not use.
  110. */
  111. public void setId(int id){
  112. this.id = id;
  113. }
  114. /**
  115. * Do not use.
  116. */
  117. public int getSlot() {
  118. return slot;
  119. }
  120. public void resetObject(){
  121. id = -1;
  122. }
  123. public RenderBuffer createDestructableClone(){
  124. if (tex != null){
  125. return null;
  126. }else{
  127. RenderBuffer destructClone = new RenderBuffer();
  128. destructClone.id = id;
  129. return destructClone;
  130. }
  131. }
  132. @Override
  133. public String toString(){
  134. if (tex != null){
  135. return "TextureTarget[format=" + format + "]";
  136. }else{
  137. return "BufferTarget[format=" + format + "]";
  138. }
  139. }
  140. }
  141. /**
  142. * <p>
  143. * Creates a new FrameBuffer with the given width, height, and number
  144. * of samples. If any textures are attached to this FrameBuffer, then
  145. * they must have the same number of samples as given in this constructor.
  146. * <p>
  147. * Note that if the {@link Renderer} does not expose the
  148. * {@link Caps#NonPowerOfTwoTextures}, then an exception will be thrown
  149. * if the width and height arguments are not power of two.
  150. *
  151. * @param width The width to use
  152. * @param height The height to use
  153. * @param samples The number of samples to use for a multisampled
  154. * framebuffer, or 1 if the framebuffer should be singlesampled.
  155. *
  156. * @throws IllegalArgumentException If width or height are not positive.
  157. */
  158. public FrameBuffer(int width, int height, int samples){
  159. super(FrameBuffer.class);
  160. if (width <= 0 || height <= 0)
  161. throw new IllegalArgumentException("FrameBuffer must have valid size.");
  162. this.width = width;
  163. this.height = height;
  164. this.samples = samples == 0 ? 1 : samples;
  165. }
  166. protected FrameBuffer(FrameBuffer src){
  167. super(FrameBuffer.class, src.id);
  168. /*
  169. for (RenderBuffer renderBuf : src.colorBufs){
  170. RenderBuffer clone = renderBuf.createDestructableClone();
  171. if (clone != null)
  172. this.colorBufs.add(clone);
  173. }
  174. this.depthBuf = src.depthBuf.createDestructableClone();
  175. */
  176. }
  177. /**
  178. * Enables the use of a depth buffer for this <code>FrameBuffer</code>.
  179. *
  180. * @param format The format to use for the depth buffer.
  181. * @throws IllegalArgumentException If <code>format</code> is not a depth format.
  182. */
  183. public void setDepthBuffer(Image.Format format){
  184. if (id != -1)
  185. throw new UnsupportedOperationException("FrameBuffer already initialized.");
  186. if (!format.isDepthFormat())
  187. throw new IllegalArgumentException("Depth buffer format must be depth.");
  188. depthBuf = new RenderBuffer();
  189. depthBuf.slot = -100; // -100 == special slot for DEPTH_BUFFER
  190. depthBuf.format = format;
  191. }
  192. /**
  193. * Enables the use of a color buffer for this <code>FrameBuffer</code>.
  194. *
  195. * @param format The format to use for the color buffer.
  196. * @throws IllegalArgumentException If <code>format</code> is not a color format.
  197. */
  198. public void setColorBuffer(Image.Format format){
  199. if (id != -1)
  200. throw new UnsupportedOperationException("FrameBuffer already initialized.");
  201. if (format.isDepthFormat())
  202. throw new IllegalArgumentException("Color buffer format must be color/luminance.");
  203. RenderBuffer colorBuf = new RenderBuffer();
  204. colorBuf.slot = 0;
  205. colorBuf.format = format;
  206. colorBufs.clear();
  207. colorBufs.add(colorBuf);
  208. }
  209. private void checkSetTexture(Texture tex, boolean depth){
  210. Image img = tex.getImage();
  211. if (img == null)
  212. throw new IllegalArgumentException("Texture not initialized with RTT.");
  213. if (depth && !img.getFormat().isDepthFormat())
  214. throw new IllegalArgumentException("Texture image format must be depth.");
  215. else if (!depth && img.getFormat().isDepthFormat())
  216. throw new IllegalArgumentException("Texture image format must be color/luminance.");
  217. // check that resolution matches texture resolution
  218. if (width != img.getWidth() || height != img.getHeight())
  219. throw new IllegalArgumentException("Texture image resolution " +
  220. "must match FB resolution");
  221. if (samples != tex.getImage().getMultiSamples())
  222. throw new IllegalStateException("Texture samples must match framebuffer samples");
  223. }
  224. /**
  225. * If enabled, any shaders rendering into this <code>FrameBuffer</code>
  226. * will be able to write several results into the renderbuffers
  227. * by using the <code>gl_FragData</code> array. Every slot in that
  228. * array maps into a color buffer attached to this framebuffer.
  229. *
  230. * @param enabled True to enable MRT (multiple rendering targets).
  231. */
  232. public void setMultiTarget(boolean enabled){
  233. if (enabled) colorBufIndex = -1;
  234. else colorBufIndex = 0;
  235. }
  236. /**
  237. * @return True if MRT (multiple rendering targets) is enabled.
  238. * @see FrameBuffer#setMultiTarget(boolean)
  239. */
  240. public boolean isMultiTarget(){
  241. return colorBufIndex == -1;
  242. }
  243. /**
  244. * If MRT is not enabled ({@link FrameBuffer#setMultiTarget(boolean) } is false)
  245. * then this specifies the color target to which the scene should be rendered.
  246. * <p>
  247. * By default the value is 0.
  248. *
  249. * @param index The color attachment index.
  250. * @throws IllegalArgumentException If index is negative or doesn't map
  251. * to any attachment on this framebuffer.
  252. */
  253. public void setTargetIndex(int index){
  254. if (index < 0 || index >= 16)
  255. throw new IllegalArgumentException("Target index must be between 0 and 16");
  256. if (colorBufs.size() < index)
  257. throw new IllegalArgumentException("The target at " + index + " is not set!");
  258. colorBufIndex = index;
  259. }
  260. /**
  261. * @return The color target to which the scene should be rendered.
  262. *
  263. * @see FrameBuffer#setTargetIndex(int)
  264. */
  265. public int getTargetIndex(){
  266. return colorBufIndex;
  267. }
  268. /**
  269. * Set the color texture to use for this framebuffer.
  270. * This automatically clears all existing textures added previously
  271. * with {@link FrameBuffer#addColorTexture(com.jme3.texture.Texture2D) }
  272. * and adds this texture as the only target.
  273. *
  274. * @param tex The color texture to set.
  275. */
  276. public void setColorTexture(Texture2D tex){
  277. clearColorTargets();
  278. addColorTexture(tex);
  279. }
  280. /**
  281. * Clears all color targets that were set or added previously.
  282. */
  283. public void clearColorTargets(){
  284. colorBufs.clear();
  285. }
  286. /**
  287. * Add a color texture to use for this framebuffer.
  288. * If MRT is enabled, then each subsequently added texture can be
  289. * rendered to through a shader that writes to the array <code>gl_FragData</code>.
  290. * If MRT is not enabled, then the index set with {@link FrameBuffer#setTargetIndex(int) }
  291. * is rendered to by the shader.
  292. *
  293. * @param tex The texture to add.
  294. */
  295. public void addColorTexture(Texture2D tex) {
  296. if (id != -1)
  297. throw new UnsupportedOperationException("FrameBuffer already initialized.");
  298. Image img = tex.getImage();
  299. checkSetTexture(tex, false);
  300. RenderBuffer colorBuf = new RenderBuffer();
  301. colorBuf.slot = colorBufs.size();
  302. colorBuf.tex = tex;
  303. colorBuf.format = img.getFormat();
  304. colorBufs.add(colorBuf);
  305. }
  306. /**
  307. * Set the depth texture to use for this framebuffer.
  308. *
  309. * @param tex The color texture to set.
  310. */
  311. public void setDepthTexture(Texture2D tex){
  312. if (id != -1)
  313. throw new UnsupportedOperationException("FrameBuffer already initialized.");
  314. Image img = tex.getImage();
  315. checkSetTexture(tex, true);
  316. depthBuf = new RenderBuffer();
  317. depthBuf.slot = -100; // indicates GL_DEPTH_ATTACHMENT
  318. depthBuf.tex = tex;
  319. depthBuf.format = img.getFormat();
  320. }
  321. /**
  322. * @return The number of color buffers attached to this texture.
  323. */
  324. public int getNumColorBuffers(){
  325. return colorBufs.size();
  326. }
  327. /**
  328. * @param index
  329. * @return The color buffer at the given index.
  330. */
  331. public RenderBuffer getColorBuffer(int index){
  332. return colorBufs.get(index);
  333. }
  334. /**
  335. * @return The first color buffer attached to this FrameBuffer, or null
  336. * if no color buffers are attached.
  337. */
  338. public RenderBuffer getColorBuffer() {
  339. if (colorBufs.isEmpty())
  340. return null;
  341. return colorBufs.get(0);
  342. }
  343. /**
  344. * @return The depth buffer attached to this FrameBuffer, or null
  345. * if no depth buffer is attached
  346. */
  347. public RenderBuffer getDepthBuffer() {
  348. return depthBuf;
  349. }
  350. /**
  351. * @return The height in pixels of this framebuffer.
  352. */
  353. public int getHeight() {
  354. return height;
  355. }
  356. /**
  357. * @return The width in pixels of this framebuffer.
  358. */
  359. public int getWidth() {
  360. return width;
  361. }
  362. /**
  363. * @return The number of samples when using a multisample framebuffer, or
  364. * 1 if this is a singlesampled framebuffer.
  365. */
  366. public int getSamples() {
  367. return samples;
  368. }
  369. @Override
  370. public String toString(){
  371. StringBuilder sb = new StringBuilder();
  372. String mrtStr = colorBufIndex >= 0 ? "" + colorBufIndex : "mrt";
  373. sb.append("FrameBuffer[format=").append(width).append("x").append(height)
  374. .append("x").append(samples).append(", drawBuf=").append(mrtStr).append("]\n");
  375. if (depthBuf != null)
  376. sb.append("Depth => ").append(depthBuf).append("\n");
  377. for (RenderBuffer colorBuf : colorBufs){
  378. sb.append("Color(").append(colorBuf.slot)
  379. .append(") => ").append(colorBuf).append("\n");
  380. }
  381. return sb.toString();
  382. }
  383. @Override
  384. public void resetObject() {
  385. this.id = -1;
  386. for (int i = 0; i < colorBufs.size(); i++) {
  387. colorBufs.get(i).resetObject();
  388. }
  389. if (depthBuf != null)
  390. depthBuf.resetObject();
  391. setUpdateNeeded();
  392. }
  393. @Override
  394. public void deleteObject(Object rendererObject) {
  395. ((Renderer)rendererObject).deleteFrameBuffer(this);
  396. }
  397. public NativeObject createDestructableClone(){
  398. return new FrameBuffer(this);
  399. }
  400. }