|
|
|
@ -32,11 +32,46 @@ |
|
|
|
|
|
|
|
|
|
package com.jme3.texture; |
|
|
|
|
|
|
|
|
|
import com.jme3.renderer.Caps; |
|
|
|
|
import com.jme3.renderer.GLObject; |
|
|
|
|
import com.jme3.renderer.Renderer; |
|
|
|
|
import com.jme3.texture.Image.Format; |
|
|
|
|
import java.util.ArrayList; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* <p> |
|
|
|
|
* <code>FrameBuffer</code>s are rendering surfaces allowing |
|
|
|
|
* off-screen rendering and render-to-texture functionality. |
|
|
|
|
* Instead of the scene rendering to the screen, it is rendered into the |
|
|
|
|
* FrameBuffer, the result can be either a texture or a buffer. |
|
|
|
|
* <p> |
|
|
|
|
* A <code>FrameBuffer</code> supports two methods of rendering, |
|
|
|
|
* using a {@link Texture} or using a buffer. |
|
|
|
|
* When using a texture, the result of the rendering will be rendered |
|
|
|
|
* onto the texture, after which the texture can be placed on an object |
|
|
|
|
* and rendered as if the texture was uploaded from disk. |
|
|
|
|
* When using a buffer, the result is rendered onto |
|
|
|
|
* a buffer located on the GPU, the data of this buffer is not accessible |
|
|
|
|
* to the user. buffers are useful if one |
|
|
|
|
* wishes to retrieve only the color content of the scene, but still desires |
|
|
|
|
* depth testing (which requires a depth buffer). |
|
|
|
|
* Buffers can be copied to other framebuffers |
|
|
|
|
* including the main screen, by using |
|
|
|
|
* {@link Renderer#copyFrameBuffer(com.jme3.texture.FrameBuffer, com.jme3.texture.FrameBuffer) }. |
|
|
|
|
* The content of a {@link RenderBuffer} can be retrieved by using |
|
|
|
|
* {@link Renderer#readFrameBuffer(com.jme3.texture.FrameBuffer, java.nio.ByteBuffer) }. |
|
|
|
|
* <p> |
|
|
|
|
* <code>FrameBuffer</code>s have several attachment points, there are |
|
|
|
|
* several <em>color</em> attachment points and a single <em>depth</em> |
|
|
|
|
* attachment point. |
|
|
|
|
* The color attachment points support image formats such as |
|
|
|
|
* {@link Format#RGBA8}, allowing rendering the color content of the scene. |
|
|
|
|
* The depth attachment point requires a depth image format. |
|
|
|
|
* |
|
|
|
|
* @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer) |
|
|
|
|
* |
|
|
|
|
* @author Kirill Vainer |
|
|
|
|
*/ |
|
|
|
|
public class FrameBuffer extends GLObject { |
|
|
|
|
|
|
|
|
|
private int width = 0; |
|
|
|
@ -46,6 +81,11 @@ public class FrameBuffer extends GLObject { |
|
|
|
|
private RenderBuffer depthBuf = null; |
|
|
|
|
private int colorBufIndex = 0; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* <code>RenderBuffer</code> represents either a texture or a |
|
|
|
|
* buffer that will be rendered to. <code>RenderBuffer</code>s |
|
|
|
|
* are attached to an attachment slot on a <code>FrameBuffer</code>. |
|
|
|
|
*/ |
|
|
|
|
public class RenderBuffer { |
|
|
|
|
|
|
|
|
|
Texture tex; |
|
|
|
@ -53,30 +93,42 @@ public class FrameBuffer extends GLObject { |
|
|
|
|
int id = -1; |
|
|
|
|
int slot = -1; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return The image format of the render buffer. |
|
|
|
|
*/ |
|
|
|
|
public Format getFormat() { |
|
|
|
|
return format; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return The texture to render to for this <code>RenderBuffer</code> |
|
|
|
|
* or null if content should be rendered into a buffer. |
|
|
|
|
*/ |
|
|
|
|
public Texture getTexture(){ |
|
|
|
|
return tex; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Do not use. |
|
|
|
|
*/ |
|
|
|
|
public int getId() { |
|
|
|
|
return id; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Do not use. |
|
|
|
|
*/ |
|
|
|
|
public void setId(int id){ |
|
|
|
|
this.id = id; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Do not use. |
|
|
|
|
*/ |
|
|
|
|
public int getSlot() { |
|
|
|
|
return slot; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void setSlot(int slot) { |
|
|
|
|
this.slot = slot; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void resetObject(){ |
|
|
|
|
id = -1; |
|
|
|
|
} |
|
|
|
@ -101,6 +153,23 @@ public class FrameBuffer extends GLObject { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* <p> |
|
|
|
|
* Creates a new FrameBuffer with the given width, height, and number |
|
|
|
|
* of samples. If any textures are attached to this FrameBuffer, then |
|
|
|
|
* they must have the same number of samples as given in this constructor. |
|
|
|
|
* <p> |
|
|
|
|
* Note that if the {@link Renderer} does not expose the |
|
|
|
|
* {@link Caps#NonPowerOfTwoTextures}, then an exception will be thrown |
|
|
|
|
* if the width and height arguments are not power of two. |
|
|
|
|
* |
|
|
|
|
* @param width The width to use |
|
|
|
|
* @param height The height to use |
|
|
|
|
* @param samples The number of samples to use for a multisampled |
|
|
|
|
* framebuffer, or 1 if the framebuffer should be singlesampled. |
|
|
|
|
* |
|
|
|
|
* @throws IllegalArgumentException If width or height are not positive. |
|
|
|
|
*/ |
|
|
|
|
public FrameBuffer(int width, int height, int samples){ |
|
|
|
|
super(Type.FrameBuffer); |
|
|
|
|
if (width <= 0 || height <= 0) |
|
|
|
@ -124,6 +193,12 @@ public class FrameBuffer extends GLObject { |
|
|
|
|
*/ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Enables the use of a depth buffer for this <code>FrameBuffer</code>. |
|
|
|
|
* |
|
|
|
|
* @param format The format to use for the depth buffer. |
|
|
|
|
* @throws IllegalArgumentException If <code>format</code> is not a depth format. |
|
|
|
|
*/ |
|
|
|
|
public void setDepthBuffer(Image.Format format){ |
|
|
|
|
if (id != -1) |
|
|
|
|
throw new UnsupportedOperationException("FrameBuffer already initialized."); |
|
|
|
@ -136,6 +211,12 @@ public class FrameBuffer extends GLObject { |
|
|
|
|
depthBuf.format = format; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Enables the use of a color buffer for this <code>FrameBuffer</code>. |
|
|
|
|
* |
|
|
|
|
* @param format The format to use for the color buffer. |
|
|
|
|
* @throws IllegalArgumentException If <code>format</code> is not a color format. |
|
|
|
|
*/ |
|
|
|
|
public void setColorBuffer(Image.Format format){ |
|
|
|
|
if (id != -1) |
|
|
|
|
throw new UnsupportedOperationException("FrameBuffer already initialized."); |
|
|
|
@ -170,38 +251,85 @@ public class FrameBuffer extends GLObject { |
|
|
|
|
throw new IllegalStateException("Texture samples must match framebuffer samples"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* If enabled, any shaders rendering into this <code>FrameBuffer</code> |
|
|
|
|
* will be able to write several results into the renderbuffers |
|
|
|
|
* by using the <code>gl_FragData</code> array. Every slot in that |
|
|
|
|
* array maps into a color buffer attached to this framebuffer. |
|
|
|
|
* |
|
|
|
|
* @param enabled True to enable MRT (multiple rendering targets). |
|
|
|
|
*/ |
|
|
|
|
public void setMultiTarget(boolean enabled){ |
|
|
|
|
if (enabled) colorBufIndex = -1; |
|
|
|
|
else colorBufIndex = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return True if MRT (multiple rendering targets) is enabled. |
|
|
|
|
* @see FrameBuffer#setMultiTarget(boolean) |
|
|
|
|
*/ |
|
|
|
|
public boolean isMultiTarget(){ |
|
|
|
|
return colorBufIndex == -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* If MRT is not enabled ({@link FrameBuffer#setMultiTarget(boolean) } is false) |
|
|
|
|
* then this specifies the color target to which the scene should be rendered. |
|
|
|
|
* <p> |
|
|
|
|
* By default the value is 0. |
|
|
|
|
* |
|
|
|
|
* @param index The color attachment index. |
|
|
|
|
* @throws IllegalArgumentException If index is negative or doesn't map |
|
|
|
|
* to any attachment on this framebuffer. |
|
|
|
|
*/ |
|
|
|
|
public void setTargetIndex(int index){ |
|
|
|
|
if (index < 0 || index >= 16) |
|
|
|
|
throw new IllegalArgumentException(); |
|
|
|
|
|
|
|
|
|
if (colorBufs.size() >= index) |
|
|
|
|
throw new IndexOutOfBoundsException("The target at " + index + " is not set!"); |
|
|
|
|
throw new IllegalArgumentException("The target at " + index + " is not set!"); |
|
|
|
|
|
|
|
|
|
colorBufIndex = index; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public boolean isMultiTarget(){ |
|
|
|
|
return colorBufIndex == -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return The color target to which the scene should be rendered. |
|
|
|
|
* |
|
|
|
|
* @see FrameBuffer#setTargetIndex(int) |
|
|
|
|
*/ |
|
|
|
|
public int getTargetIndex(){ |
|
|
|
|
return colorBufIndex; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Set the color texture to use for this framebuffer. |
|
|
|
|
* This automatically clears all existing textures added previously |
|
|
|
|
* with {@link FrameBuffer#addColorTexture(com.jme3.texture.Texture2D) } |
|
|
|
|
* and adds this texture as the only target. |
|
|
|
|
* |
|
|
|
|
* @param tex The color texture to set. |
|
|
|
|
*/ |
|
|
|
|
public void setColorTexture(Texture2D tex){ |
|
|
|
|
clearColorTargets(); |
|
|
|
|
addColorTexture(tex); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Clears all color targets that were set or added previously. |
|
|
|
|
*/ |
|
|
|
|
public void clearColorTargets(){ |
|
|
|
|
colorBufs.clear(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Add a color texture to use for this framebuffer. |
|
|
|
|
* If MRT is enabled, then each subsequently added texture can be |
|
|
|
|
* rendered to through a shader that writes to the array <code>gl_FragData</code>. |
|
|
|
|
* If MRT is not enabled, then the index set with {@link FrameBuffer#setTargetIndex(int) } |
|
|
|
|
* is rendered to by the shader. |
|
|
|
|
* |
|
|
|
|
* @param tex The texture to add. |
|
|
|
|
*/ |
|
|
|
|
public void addColorTexture(Texture2D tex) { |
|
|
|
|
if (id != -1) |
|
|
|
|
throw new UnsupportedOperationException("FrameBuffer already initialized."); |
|
|
|
@ -217,6 +345,11 @@ public class FrameBuffer extends GLObject { |
|
|
|
|
colorBufs.add(colorBuf); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Set the depth texture to use for this framebuffer. |
|
|
|
|
* |
|
|
|
|
* @param tex The color texture to set. |
|
|
|
|
*/ |
|
|
|
|
public void setDepthTexture(Texture2D tex){ |
|
|
|
|
if (id != -1) |
|
|
|
|
throw new UnsupportedOperationException("FrameBuffer already initialized."); |
|
|
|
@ -230,33 +363,58 @@ public class FrameBuffer extends GLObject { |
|
|
|
|
depthBuf.format = img.getFormat(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return The number of color buffers attached to this texture. |
|
|
|
|
*/ |
|
|
|
|
public int getNumColorBuffers(){ |
|
|
|
|
return colorBufs.size(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @param index |
|
|
|
|
* @return The color buffer at the given index. |
|
|
|
|
*/ |
|
|
|
|
public RenderBuffer getColorBuffer(int index){ |
|
|
|
|
return colorBufs.get(index); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return The first color buffer attached to this FrameBuffer, or null |
|
|
|
|
* if no color buffers are attached. |
|
|
|
|
*/ |
|
|
|
|
public RenderBuffer getColorBuffer() { |
|
|
|
|
if (colorBufs.size() == 0) |
|
|
|
|
if (colorBufs.isEmpty()) |
|
|
|
|
return null; |
|
|
|
|
|
|
|
|
|
return colorBufs.get(0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return The depth buffer attached to this FrameBuffer, or null |
|
|
|
|
* if no depth buffer is attached |
|
|
|
|
*/ |
|
|
|
|
public RenderBuffer getDepthBuffer() { |
|
|
|
|
return depthBuf; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return The height in pixels of this framebuffer. |
|
|
|
|
*/ |
|
|
|
|
public int getHeight() { |
|
|
|
|
return height; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return The width in pixels of this framebuffer. |
|
|
|
|
*/ |
|
|
|
|
public int getWidth() { |
|
|
|
|
return width; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return The number of samples when using a multisample framebuffer, or |
|
|
|
|
* 1 if this is a singlesampled framebuffer. |
|
|
|
|
*/ |
|
|
|
|
public int getSamples() { |
|
|
|
|
return samples; |
|
|
|
|
} |
|
|
|
|