package demos.renderToTexture; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.media.opengl.DebugGL; import javax.media.opengl.GL; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCanvas; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; import javax.media.opengl.GLPbuffer; import javax.swing.JFrame; import com.sun.opengl.utils.GLUT; public class RenderToTextureBasic implements GLEventListener { private static GLUT glut = new GLUT(); public static void main(String[] args) { RenderToTextureBasic renderer = new RenderToTextureBasic(); GLCanvas canvas = new GLCanvas(); canvas.addGLEventListener(renderer); JFrame frame = new JFrame("Render To Texture Demo"); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent evt) { System.exit(0); } }); frame.getContentPane().add(canvas); canvas.setSize(300, 300); frame.pack(); frame.setVisible(true); } private static float[] lightPosition = new float[] { 1, 0, 10, 1 }; private OffscreenRenderer offscreenRenderer; private boolean isOffscreenRendererCreated = false; public void init(GLAutoDrawable drawable) { drawable.setGL(new DebugGL(drawable.getGL())); GL gl = drawable.getGL(); gl.glEnable(GL.GL_DEPTH_TEST); gl.glEnable(GL.GL_NORMALIZE); gl.glEnable(GL.GL_LIGHTING); gl.glClearColor(0.5f, 0.5f, 0.5f, 0); // Place the camera gl.glPushAttrib(GL.GL_TRANSFORM_BIT); { gl.glMatrixMode(GL.GL_PROJECTION); gl.glPushMatrix(); gl.glLoadIdentity(); gl.glFrustum(-2.0 / 10, 2.0 / 10, -2.0 / 10, 2.0 / 10, 1, 11); gl.glMatrixMode(GL.GL_MODELVIEW); gl.glPushMatrix(); gl.glLoadIdentity(); gl.glTranslated(0, 0, -10); } // Configure the light gl.glEnable(GL.GL_LIGHT0); gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, lightPosition, 0); if (!isOffscreenRendererCreated) { offscreenRenderer = new OffscreenRenderer(256); offscreenRenderer.initFromParent(drawable); isOffscreenRendererCreated = true; } } public void display(GLAutoDrawable drawable) { GL gl = drawable.getGL(); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); offscreenRenderer.renderOffscreen(); // Draw the teapot and a repeated picture of the offscreen colour // buffer. // We'll use multi-texture unit 0 for the picture offscreenRenderer.prepareForColouredRendering(drawable, GL.GL_TEXTURE0); drawSquare(drawable); offscreenRenderer.stopColouredRendering(drawable); drawFloatingTeapot(drawable); } private static void drawFloatingTeapot(GLAutoDrawable drawable) { GL gl = drawable.getGL(); gl.glPushAttrib(GL.GL_TRANSFORM_BIT); { gl.glMatrixMode(GL.GL_MODELVIEW); gl.glPushMatrix(); { gl.glTranslated(0.5, 0, 3); glut.glutSolidTeapot(0.25, true); } gl.glPopMatrix(); } gl.glPopAttrib(); } private static void drawSquare(GLAutoDrawable drawable) { GL gl = drawable.getGL(); gl.glBegin(GL.GL_QUADS); { gl.glNormal3d(0, 0, 1); gl.glMultiTexCoord2d(GL.GL_TEXTURE0, 0, 0); gl.glVertex3d(-1, -1, 0); gl.glMultiTexCoord2d(GL.GL_TEXTURE0, 2, 0); gl.glVertex3d(1, -1, 0); gl.glMultiTexCoord2d(GL.GL_TEXTURE0, 2, 2); gl.glVertex3d(1, 1, 0); gl.glMultiTexCoord2d(GL.GL_TEXTURE0, 0, 2); gl.glVertex3d(-1, 1, 0); } gl.glEnd(); } public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) { // Nothing to do } public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { // Nothing to do } private static class OffscreenRenderer implements GLEventListener { private GLPbuffer offscreenTarget; private int textureDimension; public OffscreenRenderer(int textureDimension) { super(); this.textureDimension = textureDimension; } private boolean isDebug = false; public void initFromParent(GLAutoDrawable parent) { isDebug = parent.getGL() instanceof DebugGL; if (!GLDrawableFactory.getFactory().canCreateGLPbuffer()) { throw new GLException("Can not create pbuffer"); } if (offscreenTarget != null) { offscreenTarget.destroy(); offscreenTarget = null; } GLCapabilities caps = new GLCapabilities(); caps.setDoubleBuffered(false); caps.setDepthBits(24); caps.setRedBits(8); caps.setGreenBits(8); caps.setBlueBits(8); caps.setAlphaBits(8); // We want to bind both the colour buffer and the depth buffer to // textures caps.setOffscreenRenderToTexture(true); caps.setOffscreenRenderToTextureRectangle(false); offscreenTarget = GLDrawableFactory.getFactory() .createGLPbuffer(caps, null, textureDimension, textureDimension, parent.getContext()); offscreenTarget.addGLEventListener(this); } public void init(GLAutoDrawable drawable) { if (isDebug && !(drawable.getGL() instanceof DebugGL)) { drawable.setGL(new DebugGL(drawable.getGL())); } GL gl = drawable.getGL(); gl.glEnable(GL.GL_DEPTH_TEST); gl.glEnable(GL.GL_NORMALIZE); gl.glDisable(GL.GL_LIGHTING); gl.glClearColor(0, 0, 1, 0); // Place the offscreen camera at the light position gl.glPushAttrib(GL.GL_TRANSFORM_BIT); { gl.glMatrixMode(GL.GL_PROJECTION); gl.glPushMatrix(); gl.glLoadIdentity(); applyLightFrustum(gl); gl.glMatrixMode(GL.GL_MODELVIEW); gl.glPushMatrix(); gl.glLoadIdentity(); applyLightTransformation(gl); } gl.glPopAttrib(); } private void applyLightFrustum(GL gl) { // Set up the viewing frustum so that the light only sees the // volume between itself and the unit X-Y square at the origin // (because that is the only volume in which we will be casting // shadows). gl.glFrustum(-2.0 / 10, 0, -1.0 / 10, 1.0 / 10, 1, 10); } private void applyLightTransformation(GL gl) { gl.glTranslated(-lightPosition[0], -lightPosition[1], -lightPosition[2]); } public void renderOffscreen() { offscreenTarget.display(); } private int counter = 0; public void display(GLAutoDrawable drawable) { System.out.println("Pbuffer rendering"); GL gl = drawable.getGL(); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); if (counter++ % 2 == 0) { // Draw the teapot as a red silhouette gl.glColor3d(1, 0, 0); } else { // Draw the teapot as a green silhouette gl.glColor3d(0, 1, 0); } drawFloatingTeapot(drawable); } public void prepareForColouredRendering(GLAutoDrawable targetDrawable, int textureUnitID) { GL gl = targetDrawable.getGL(); // The state modified by this preparation will be restored by a call // to finishShadowedRendering() gl.glPushAttrib(GL.GL_TEXTURE_BIT); gl.glActiveTexture(textureUnitID); // Use the pbuffer as a colour texture System.out.println("Pbuffer bound"); offscreenTarget.bindTexture(); // Set the texture up to be used for painting a surface int textureTarget = GL.GL_TEXTURE_2D; gl.glEnable(textureTarget); gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE); gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT); gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT); } public void stopColouredRendering(GLAutoDrawable targetDrawable) { GL gl = targetDrawable.getGL(); // The pbuffer MUST be released from the texture before it can be // drawn to again. System.out.println("Pbuffer released"); offscreenTarget.releaseTexture(); // Restore the active texture gl.glPopAttrib(); } public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) { // Nothing to do } public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { // Nothing to do } } }