Created
June 10, 2015 11:29
-
-
Save alicanalbayrak/ace2d7f426a4e75ca008 to your computer and use it in GitHub Desktop.
Java Implementation of streaming textures using PBO (tutorial source: http://www.songho.ca/opengl/gl_pbo.html)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package pbo; | |
import static com.jogamp.opengl.GL.GL_COLOR_BUFFER_BIT; | |
import static com.jogamp.opengl.GL.GL_DEPTH_BUFFER_BIT; | |
import static com.jogamp.opengl.GL.GL_DEPTH_TEST; | |
import static com.jogamp.opengl.GL.GL_LEQUAL; | |
import static com.jogamp.opengl.GL.GL_NICEST; | |
import static com.jogamp.opengl.GL2ES1.GL_PERSPECTIVE_CORRECTION_HINT; | |
import static com.jogamp.opengl.fixedfunc.GLLightingFunc.GL_SMOOTH; | |
import java.awt.Dimension; | |
import java.awt.event.WindowAdapter; | |
import java.awt.event.WindowEvent; | |
import java.nio.IntBuffer; | |
import javax.swing.JFrame; | |
import javax.swing.SwingUtilities; | |
import com.jogamp.opengl.GL; | |
import com.jogamp.opengl.GL2; | |
import com.jogamp.opengl.GLAutoDrawable; | |
import com.jogamp.opengl.GLEventListener; | |
import com.jogamp.opengl.awt.GLCanvas; | |
import com.jogamp.opengl.glu.GLU; | |
import com.jogamp.opengl.util.FPSAnimator; | |
public class PBO2Texture extends GLCanvas implements GLEventListener { | |
private static final long serialVersionUID = 896825927734135562L; | |
private GLU glu; | |
// constants | |
private static int IMAGE_WIDTH = 1024; | |
private static int IMAGE_HEIGHT = 1024; | |
private static int CHANNEL_COUNT = 4; | |
private static int DATA_SIZE = IMAGE_WIDTH * IMAGE_HEIGHT * CHANNEL_COUNT; | |
private static int PIXEL_FORMAT = GL2.GL_BGRA; | |
// vars | |
private int[] pboIds = new int[2]; | |
private int[] textureId = new int[1]; | |
int screenWidth; | |
int screenHeight; | |
boolean pboSupported; | |
int pboMode; | |
private static int index = 0; | |
private int nextIndex = 0; | |
static int color = 0; | |
public PBO2Texture() { | |
this.addGLEventListener(this); | |
} | |
// =========================================================================== | |
// OpenGL Callback Methods | |
// =========================================================================== | |
@Override | |
public void init(GLAutoDrawable drawable) { | |
GL2 gl = drawable.getGL().getGL2(); | |
glu = new GLU(); // get GL Utilities | |
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // set background (clear) color | |
gl.glClearDepth(1.0f); // set clear depth value to farthest | |
gl.glEnable(GL_DEPTH_TEST); // enables depth testing | |
gl.glDepthFunc(GL_LEQUAL); // the type of depth test to do | |
gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // best | |
// perspective | |
// correction | |
gl.glShadeModel(GL_SMOOTH); // blends colors nicely, and smoothes out | |
// lighting | |
generateTexture(gl); | |
pboSupported = checkForPBOSupport(gl); | |
if (pboSupported) { | |
generatePBOs(gl); | |
} | |
} | |
@Override | |
public void dispose(GLAutoDrawable drawable) { | |
GL2 gl = drawable.getGL().getGL2(); | |
deletePBORelatedBuffers(gl); | |
} | |
@Override | |
public void display(GLAutoDrawable drawable) { | |
GL2 gl = drawable.getGL().getGL2(); | |
renderPBO2Texture(gl); | |
gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
gl.glLoadIdentity(); | |
drawTexture2Screen(gl); | |
} | |
@Override | |
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { | |
GL2 gl = drawable.getGL().getGL2(); | |
this.screenWidth = width; | |
this.screenHeight = height; | |
toPerspective(gl); | |
} | |
// =========================================================================== | |
// PBO Related Methods & Variables | |
// =========================================================================== | |
private boolean checkForPBOSupport(GL2 gl) { | |
boolean functionAvailability = gl.isFunctionAvailable("glGenBuffersARB") | |
&& gl.isFunctionAvailable("glBindBufferARB") && gl.isFunctionAvailable("glBufferDataARB") | |
&& gl.isFunctionAvailable("glBufferSubDataARB") && gl.isFunctionAvailable("glDeleteBuffersARB") | |
&& gl.isFunctionAvailable("glGetBufferParameterivARB") && gl.isFunctionAvailable("glMapBufferARB") | |
&& gl.isFunctionAvailable("glUnmapBufferARB"); | |
boolean wglExtSwapAvailability = gl.isExtensionAvailable("WGL_EXT_swap_control"); | |
boolean glArbPBOAvailability = gl.isExtensionAvailable("GL_ARB_pixel_buffer_object"); | |
if (functionAvailability && wglExtSwapAvailability && glArbPBOAvailability) { | |
System.out.println("Video card seems ok!"); | |
pboMode = 2; | |
return true; | |
} else { | |
pboMode = 0; | |
} | |
return false; | |
} | |
private void generateTexture(GL2 gl) { | |
gl.glGenTextures(1, textureId, 0); | |
gl.glBindTexture(GL2.GL_TEXTURE_2D, textureId[0]); | |
gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_NEAREST); | |
gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_NEAREST); | |
gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP); | |
gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP); | |
gl.glTexImage2D(GL2.GL_TEXTURE_2D, 0, GL2.GL_RGBA8, IMAGE_WIDTH, IMAGE_HEIGHT, 0, PIXEL_FORMAT, | |
GL2.GL_UNSIGNED_BYTE, null); | |
gl.glBindTexture(GL2.GL_TEXTURE_2D, 0); | |
} | |
private void generatePBOs(GL2 gl) { | |
gl.glGenBuffers(2, pboIds, 0); | |
gl.glBindBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, pboIds[0]); | |
gl.glBufferData(GL2.GL_PIXEL_UNPACK_BUFFER, DATA_SIZE, null, GL2.GL_STREAM_DRAW); | |
gl.glBindBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, pboIds[1]); | |
gl.glBufferData(GL2.GL_PIXEL_UNPACK_BUFFER, DATA_SIZE, null, GL2.GL_STREAM_DRAW); | |
gl.glBindBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, 0); | |
} | |
private void renderPBO2Texture(GL2 gl) { | |
if (pboMode == 1) { | |
index = nextIndex = 0; | |
} else if (pboMode == 2) { | |
index = (index + 1) % 2; | |
nextIndex = (index + 1) % 2; | |
} | |
// start to copy from PBO to texture object | |
gl.glBindTexture(GL2.GL_TEXTURE_2D, textureId[0]); | |
gl.glBindBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, pboIds[index]); | |
// copy pixels from PBO to texture object | |
// Use offset instead of ponter. | |
gl.glTexSubImage2D(GL2.GL_TEXTURE_2D, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, PIXEL_FORMAT, GL2.GL_UNSIGNED_BYTE, 0); | |
// bind PBO to update pixel values | |
gl.glBindBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, pboIds[nextIndex]); | |
// map the buffer object into client's memory | |
// Note that glMapBufferARB() causes sync issue. | |
// If GPU is working with this buffer, glMapBufferARB() will wait(stall) | |
// for GPU to finish its job. To avoid waiting (stall), you can call | |
// first glBufferDataARB() with NULL pointer before glMapBufferARB(). | |
// If you do that, the previous data in PBO will be discarded and | |
// glMapBufferARB() returns a new allocated pointer immediately | |
// even if GPU is still working with the previous data. | |
gl.glBufferData(GL2.GL_PIXEL_UNPACK_BUFFER, DATA_SIZE, null, GL2.GL_STREAM_DRAW); | |
IntBuffer intBuffer = gl.glMapBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, GL2.GL_WRITE_ONLY).asIntBuffer(); | |
if (intBuffer != null) { | |
updatePixels(intBuffer); | |
gl.glUnmapBuffer(GL2.GL_PIXEL_UNPACK_BUFFER); | |
} | |
// it is good idea to release PBOs with ID 0 after use. | |
// Once bound with 0, all pixel operations behave normal ways. | |
gl.glBindBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, 0); | |
} | |
private void updatePixels(IntBuffer intBuffer) { | |
// copy 4 bytes at once | |
for (int i = 0; i < IMAGE_HEIGHT; ++i) { | |
for (int j = 0; j < IMAGE_WIDTH; ++j) { | |
intBuffer.put(color); | |
} | |
color += 257; // add an arbitary number (no meaning) | |
} | |
++color; // scroll down | |
} | |
private void drawTexture2Screen(GL2 gl) { | |
gl.glTranslatef(0.0f, 0.0f, -3.0f); | |
gl.glEnable(GL.GL_TEXTURE_2D); | |
gl.glBindTexture(GL2.GL_TEXTURE_2D, textureId[0]); | |
gl.glColor4f(1, 1, 1, 1); | |
gl.glBegin(GL2.GL_QUADS); | |
gl.glTexCoord2f(0.0f, 0.0f); | |
gl.glVertex3f(-1.0f, -1.0f, 0.0f); | |
gl.glTexCoord2f(1.0f, 0.0f); | |
gl.glVertex3f(1f, -1.0f, 0.0f); | |
gl.glTexCoord2f(1.0f, 1.0f); | |
gl.glVertex3f(1.0f, 1.0f, 0.0f); | |
gl.glTexCoord2f(0.0f, 1.0f); | |
gl.glVertex3f(-1.0f, 1.0f, 0.0f); | |
gl.glEnd(); | |
gl.glBindTexture(GL2.GL_TEXTURE_2D, 0); | |
gl.glDisable(GL.GL_TEXTURE_2D); | |
} | |
private void deletePBORelatedBuffers(GL2 gl) { | |
// Delete generated texture | |
gl.glDeleteBuffers(1, textureId, 0); | |
// Delete pixel buffer objects | |
gl.glDeleteBuffers(2, pboIds, 0); | |
} | |
// =========================================================================== | |
// Helper Methods | |
// =========================================================================== | |
private void toPerspective(GL2 gl) { | |
// set viewport to be the entire window | |
gl.glViewport(0, 0, screenWidth, screenHeight); | |
// set perspective viewing frustum | |
gl.glMatrixMode(GL2.GL_PROJECTION); | |
gl.glLoadIdentity(); | |
glu.gluPerspective(45.0f, (float) (screenWidth) / screenHeight, 1.0f, 100.0f); | |
// switch to modelview matrix in order to set scene | |
gl.glMatrixMode(GL2.GL_MODELVIEW); | |
gl.glLoadIdentity(); | |
} | |
private void toOrtho(GL2 gl) { | |
// set viewport to be the entire window | |
gl.glViewport(0, 0, screenWidth, screenHeight); | |
// set orthographic viewing frustum | |
gl.glMatrixMode(GL2.GL_PROJECTION); | |
gl.glLoadIdentity(); | |
gl.glOrtho(0, screenWidth, 0, screenHeight, -1, 1); | |
// switch to modelview matrix in order to set scene | |
gl.glMatrixMode(GL2.GL_MODELVIEW); | |
gl.glLoadIdentity(); | |
} | |
// =========================================================================== | |
// Main Method & Variables | |
// =========================================================================== | |
private static int FPS = 60; | |
public static void main(String[] args) { | |
// Run the GUI codes in the event-dispatching thread for thread safety | |
SwingUtilities.invokeLater(new Runnable() { | |
@Override | |
public void run() { | |
GLCanvas canvas = new PBO2Texture(); | |
canvas.setPreferredSize(new Dimension(400, 300)); | |
final FPSAnimator animator = new FPSAnimator(canvas, FPS, false); | |
final JFrame frame = new JFrame(); | |
frame.getContentPane().add(canvas); | |
frame.addWindowListener(new WindowAdapter() { | |
@Override | |
public void windowClosing(WindowEvent e) { | |
new Thread() { | |
@Override | |
public void run() { | |
if (animator.isStarted()) | |
animator.stop(); | |
System.exit(0); | |
} | |
}.start(); | |
} | |
}); | |
frame.setTitle("PBO to Texture (Texture Streaming)"); | |
frame.pack(); | |
frame.setVisible(true); | |
animator.start(); // start the animation loop | |
} | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment