Skip to content

Instantly share code, notes, and snippets.

Created January 21, 2015 10:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/35f9a540a82ad369e4e3 to your computer and use it in GitHub Desktop.
Save anonymous/35f9a540a82ad369e4e3 to your computer and use it in GitHub Desktop.
Use LWJGL to render a scende into a PNG without showing windows
import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MAJOR;
import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MINOR;
import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_CORE_PROFILE;
import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_FORWARD_COMPAT;
import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_PROFILE;
import static org.lwjgl.glfw.GLFW.GLFW_SAMPLES;
import static org.lwjgl.glfw.GLFW.GLFW_VISIBLE;
import static org.lwjgl.glfw.GLFW.glfwCreateWindow;
import static org.lwjgl.glfw.GLFW.glfwDestroyWindow;
import static org.lwjgl.glfw.GLFW.glfwInit;
import static org.lwjgl.glfw.GLFW.glfwMakeContextCurrent;
import static org.lwjgl.glfw.GLFW.glfwTerminate;
import static org.lwjgl.glfw.GLFW.glfwWindowHint;
import static org.lwjgl.opengl.EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT;
import static org.lwjgl.opengl.EXTFramebufferObject.GL_FRAMEBUFFER_EXT;
import static org.lwjgl.opengl.EXTFramebufferObject.glBindFramebufferEXT;
import static org.lwjgl.opengl.EXTFramebufferObject.glFramebufferTexture2DEXT;
import static org.lwjgl.opengl.EXTFramebufferObject.glGenFramebuffersEXT;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_FALSE;
import static org.lwjgl.opengl.GL11.GL_FLOAT;
import static org.lwjgl.opengl.GL11.GL_INT;
import static org.lwjgl.opengl.GL11.GL_LINEAR;
import static org.lwjgl.opengl.GL11.GL_RGBA;
import static org.lwjgl.opengl.GL11.GL_RGBA8;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER;
import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
import static org.lwjgl.opengl.GL11.GL_TRUE;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE;
import static org.lwjgl.opengl.GL11.glBindTexture;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glDrawArrays;
import static org.lwjgl.opengl.GL11.glGenTextures;
import static org.lwjgl.opengl.GL11.glGetTexImage;
import static org.lwjgl.opengl.GL11.glTexImage2D;
import static org.lwjgl.opengl.GL11.glTexParameterf;
import static org.lwjgl.opengl.GL11.glViewport;
import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL15.glBufferData;
import static org.lwjgl.opengl.GL15.glDeleteBuffers;
import static org.lwjgl.opengl.GL15.glGenBuffers;
import static org.lwjgl.opengl.GL20.glDisableVertexAttribArray;
import static org.lwjgl.opengl.GL20.glEnableVertexAttribArray;
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glDeleteVertexArrays;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
import static org.lwjgl.system.MemoryUtil.NULL;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.imageio.ImageIO;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GLContext;
public class RenderToPng
{
private int vaoID;
private int vboID;
int screenTextureID;
int framebufferID;
private long windowID;
private void initFramebuffer() {
framebufferID = glGenFramebuffersEXT(); // create a new framebuffer
screenTextureID = glGenTextures(); // and a new texture used as a color buffer
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebufferID); // switch to the new framebuffer
// initialize color texture
glBindTexture(GL_TEXTURE_2D, screenTextureID); // Bind the colorbuffer texture
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // make it linear filterd
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0,GL_RGBA, GL_INT, (java.nio.ByteBuffer) null); // Create the texture data
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,GL_TEXTURE_2D, screenTextureID, 0); // attach it to the framebuffer
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // Swithch back to normal framebuffer rendering
}
private void initWindow() {
// Window Hints for OpenGL context
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_VISIBLE, GL_FALSE);
windowID = glfwCreateWindow(512, 512, "Invisible", NULL, NULL);
if (windowID == NULL)
{
System.err.println("Error creating a window");
System.exit(1);
}
glfwMakeContextCurrent(windowID);
GLContext.createFromCurrent();
}
private void initGeometry()
{
// Generate and bind a Vertex Array
vaoID = glGenVertexArrays();
glBindVertexArray(vaoID);
// The vertices of our Triangle
float[] vertices = new float[]
{
+0.0f, +0.8f, // Top coordinate
-0.8f, -0.8f, // Bottom-left coordinate
+0.8f, -0.8f // Bottom-right coordinate
};
// Create a FloatBuffer of vertices
FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length);
verticesBuffer.put(vertices).flip();
// Create a Buffer Object and upload the vertices buffer
vboID = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);
glBindVertexArray(0);
}
public void renderToTexture() {
glViewport (0, 0, 512, 512); // set The Current Viewport to the fbo size
glBindTexture(GL_TEXTURE_2D, 0); // unlink textures because if we dont it all is gonna fail
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebufferID); // switch to rendering on our FBO
renderAsNormal();
ByteBuffer buffer = extractRawTextureData();
encodePng(buffer);
}
private void encodePng(ByteBuffer buffer)
{
BufferedImage image = new BufferedImage(512, 512, BufferedImage.TYPE_INT_RGB);
// possibly there's a faster method?
for(int x = 0; x < 512; x++)
{
for(int y = 0; y < 512; y++)
{
int i = (x + (512 * y)) * 4;
int r = buffer.get(i) & 0xFF;
int g = buffer.get(i + 1) & 0xFF;
int b = buffer.get(i + 2) & 0xFF;
image.setRGB(x, 512 - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
}
}
try {
ImageIO.write(image, "PNG", new File("out.png"));
} catch (IOException e) { e.printStackTrace(); }
}
private ByteBuffer extractRawTextureData() {
ByteBuffer buffer = ByteBuffer.allocateDirect(512 * 512 * 4).order(ByteOrder.nativeOrder());
glBindTexture(GL_TEXTURE_2D, screenTextureID);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
return buffer;
}
public void renderAsNormal()
{
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT);
// Bind the vertex array and enable our location
glBindVertexArray(vaoID);
glEnableVertexAttribArray(0);
// Draw a triangle of 3 vertices
glDrawArrays(GL_TRIANGLES, 0, 3);
// Disable our location
glDisableVertexAttribArray(0);
glBindVertexArray(0);
}
public void disposeAll()
{
// Dispose the vertex array
glBindVertexArray(0);
glDeleteVertexArrays(vaoID);
// Dispose the buffer object
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteBuffers(vboID);
// Destroy the window
glfwDestroyWindow(windowID);
glfwTerminate();
}
public void run()
{
if (glfwInit() != GL_TRUE)
{
System.err.println("Error initializing GLFW");
System.exit(1);
}
initWindow();
initGeometry();
initFramebuffer();
renderToTexture();
disposeAll();
}
public static void main(String[] args)
{
new RenderToPng().run();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment