Skip to content
Ben Fry edited this page Aug 8, 2015 · 28 revisions

Textures

All images in Processing are backed up by OpenGL textures. By default, Processing uses the highest-quality texture filtering mode: GL_LINEAR_MIPMAP_LINEAR for the minification filter with mipmaps, GL_LINEAR for the magnification filter, and anisotropic filtering if supported by the hardware. This combination is denoted as trilinear because of the LINEAR_MIPMAP_LINEAR and GL_LINEAR options. The mipmap generation can be disabled using hint(DISABLE_TEXTURE_MIPMAPS).

There is no corresponding PApplet method to change the filtering options, however the renderer class, PGraphicsOpenGL, exposes a function that can be used for that purpose, called textureSampling(int mode), where mode can take the values 2 (nearest), 3 (linear), 4 (bilinear), and 5 (trilinear):

PImage img;
boolean highq = true;

void setup() {
  img = loadImage("moonwalk.jpg");
}

void draw() {
  translate(0, 0, -500);
  rotateX(QUARTER_PI);
  image(img, 0, 0);
}

void keyPressed() {
  if (highq) {
    hint(DISABLE_TEXTURE_MIPMAPS);
    ((PGraphicsOpenGL)g).textureSampling(2);
    highq = false;
  } else {    
    hint(ENABLE_TEXTURE_MIPMAPS);
    ((PGraphicsOpenGL)g).textureSampling(5);
    highq = true;
  }
}

Using the low-level OpenGL functions

Generally, this should be only considered a last-ditch way to get at a particular GL-specific feature. It can make your code incompatible with everything else (such as future versions of Processing or other 3D renderers) and will confuse the hell outta people when you post your code to the web. Again, if this works for you great, but if not, we aren't responsible. If you want to write code for Java and JOGL, then find yourself a good Java IDE and get JOGL installed, Processing is probably not the way to do it because that's the kind of confusing mess we're trying to insulate you from.

Due to the changes in the OpenGL renderer from Processing 1.x to 2.x, the way to access the low-level functions is quite different between the two, so examples using 1.x syntax will not work in later releases.

Vertex coordinates are in Model Space

By default, all vertices are sent to the vertex shader already multiplied by the modelview matrix, and the modelview matrix in the vertex shader is set to the identity to avoid applying the transformation twice.

The reason for this seemingly puzzling approach is to optimize performance when drawing multiple shapes: since it is most efficient to minimize the number of buffer copies between CPU and GPU memory, the renderer batches as many shapes together as possible to draw them in a single call. However, if each shape had a different transformation, then it wouldn't be possible to group them because they would have different uniform matrices. You can disable the batching with hint(DISABLE_OPTIMIZED_STROKE). More here.

Processing 3.x

In Processing 3 (alpha 9) we upgraded to JOGL 2.3.1, which introduced some API changes. Most noticeably, the package javax.media was replaced by com.jogamp. All the previous examples for 2.x should work in Processing 3.x after making that change.

Processing 2.x

The OpenGL renderer in Processing 2 uses JOGL 2, which introduced the concept of profiles to access different versions of the GL API. The GL2 profile is probably the one you want to use if you were working with low-level GL in Processing 1.x, since it offers backwards compatibility with Open GL 1.x (i.e.: fixed function pipeline), while supporting shaders and other more recent functionality.

Advanced Example

To access the GL2 profile, you would do:

PGraphicsOpenGL pg = (PGraphicsOpenGL)g;
PGL pgl = beginPGL();  
gl2 = ((PJOGL)pgl).gl.getGL2();

You need to enclose the GL calls between beginPGL() and endPGL():

import com.jogamp.opengl.GL2;  // Processing 3
//import javax.media.opengl.GL2;  // Processing 2
 
float a;

PGraphicsOpenGL pg; 
PGL pgl;
GL2 gl;

float[] projMatrix;
float[] mvMatrix;
 
void setup() {
  size(800, 600, P3D);
  projMatrix = new float[16];
  mvMatrix = new float[16]; 
}
 
void draw() {
  background(255);
 
  pg = (PGraphicsOpenGL) g;
  pgl = beginPGL();  
  gl = ((PJOGL) pgl).gl.getGL2();
  
  copyMatrices();  
  // Do some things with gl.xxx functions here.
  // For example, the program above is translated into:
  gl.glColor4f(0.7, 0.7, 0.7, 0.8);
  gl.glTranslatef(width/2, height/2, 0);
  gl.glRotatef(a, 1, 0, 0);
  gl.glRotatef(a*2, 0, 1, 0);
  gl.glRectf(-200, -200, 200, 200);
  gl.glRotatef(90, 1, 0, 0);
  gl.glRectf(-200, -200, 200, 200);
 
  endPGL();
 
  a += 0.5;
}

// Copies the current projection and modelview matrices from Processing to OpenGL.
// It needs to be done explicitly, otherwise GL will use identity matrices by default!
void copyMatrices() {
  PGraphicsOpenGL pg = (PGraphicsOpenGL) g;
 
  gl.glMatrixMode(GL2.GL_PROJECTION);
  projMatrix[0] = pg.projection.m00;
  projMatrix[1] = pg.projection.m10;
  projMatrix[2] = pg.projection.m20;
  projMatrix[3] = pg.projection.m30;
 
  projMatrix[4] = pg.projection.m01;
  projMatrix[5] = pg.projection.m11;
  projMatrix[6] = pg.projection.m21;
  projMatrix[7] = pg.projection.m31;
 
  projMatrix[8] = pg.projection.m02;
  projMatrix[9] = pg.projection.m12;
  projMatrix[10] = pg.projection.m22;
  projMatrix[11] = pg.projection.m32;
 
  projMatrix[12] = pg.projection.m03;
  projMatrix[13] = pg.projection.m13;
  projMatrix[14] = pg.projection.m23;
  projMatrix[15] = pg.projection.m33;
 
  gl.glLoadMatrixf(projMatrix, 0);
 
  gl.glMatrixMode(GL2.GL_MODELVIEW);
  mvMatrix[0] = pg.modelview.m00;
  mvMatrix[1] = pg.modelview.m10;
  mvMatrix[2] = pg.modelview.m20;
  mvMatrix[3] = pg.modelview.m30;
 
  mvMatrix[4] = pg.modelview.m01;
  mvMatrix[5] = pg.modelview.m11;
  mvMatrix[6] = pg.modelview.m21;
  mvMatrix[7] = pg.modelview.m31;
 
  mvMatrix[8] = pg.modelview.m02;
  mvMatrix[9] = pg.modelview.m12;
  mvMatrix[10] = pg.modelview.m22;
  mvMatrix[11] = pg.modelview.m32;
 
  mvMatrix[12] = pg.modelview.m03;
  mvMatrix[13] = pg.modelview.m13;
  mvMatrix[14] = pg.modelview.m23;
  mvMatrix[15] = pg.modelview.m33;
  gl.glLoadMatrixf(mvMatrix, 0);
}

Render a Million Points

Below you have a slightly more advanced example to render 1,000,000 points using Vertex Arrays.

import com.jogamp.opengl.GL2;  // Processing 3
//import javax.media.opengl.GL2;  // Processing 2
import java.nio.*;
 
// let's try 1,000,000 points
int numPoints = 1000000;
 
// pan, zoom and rotate
float tx = 0, ty = 0;
float sc = 1;
float a = 0.0;
 
FloatBuffer vbuffer;
FloatBuffer cbuffer;
float[] projMatrix;
float[] mvMatrix;
 
PGraphicsOpenGL pg; 
PGL pgl;
GL2 gl2;

void setup() {
  size(displayWidth/2, displayHeight/2, P3D); 
 
  pg = (PGraphicsOpenGL)g;
  pgl = beginPGL();  
  gl2 = ((PJOGL)pgl).gl.getGL2();
 
  int vSize = (numPoints * 2);
  int cSize = (numPoints * 3);
  vSize = vSize << 2;
  cSize = cSize << 2;
 
  vbuffer = ByteBuffer.allocateDirect(vSize).order(ByteOrder.nativeOrder()).asFloatBuffer();
  cbuffer = ByteBuffer.allocateDirect(cSize).order(ByteOrder.nativeOrder()).asFloatBuffer();
  for (int i = 0; i < numPoints; i++) {
    // random x,y
    vbuffer.put(random(width));
    vbuffer.put(random(height));
    // random r,g,b
    cbuffer.put(random(1.0));
    cbuffer.put(random(1.0));
    cbuffer.put(random(1.0));
  }
  vbuffer.rewind();
  cbuffer.rewind();
 
  gl2.glEnableClientState(GL2.GL_VERTEX_ARRAY);
  gl2.glVertexPointer(2, GL2.GL_FLOAT, 0, vbuffer);
 
  gl2.glEnableClientState(GL2.GL_COLOR_ARRAY);
  gl2.glColorPointer(3, GL2.GL_FLOAT, 0, cbuffer);
 
  pg.endPGL();
 
  projMatrix = new float[16];
  mvMatrix = new float[16]; 
}
 
void draw() {
  background(0);
 
  pgl = beginPGL();  
  gl2 = ((PJOGL)pgl).gl.getGL2();
 
  copyMatrices();
 
  gl2.glPushMatrix();
  gl2.glTranslatef(width/2, height/2, 0);
  gl2.glScalef(sc,sc,sc);
  gl2.glRotatef(a, 0.0, 0.0, 1.0);
  gl2.glTranslatef(-width/2, -height/2, 0);
  gl2.glTranslatef(tx,ty, 0);
 
  gl2.glPointSize(2.0);
 
  gl2.glDrawArrays(GL2.GL_POINTS, 0, numPoints);
 
  gl2.glPopMatrix();
 
  endPGL();
}
 
void mouseDragged() {
  float dx = (mouseX - pmouseX) / sc;
  float dy = (mouseY - pmouseY) / sc;
  float angle = radians(-a);
  float rx = cos(angle)*dx - sin(angle)*dy;
  float ry = sin(angle)*dx + cos(angle)*dy;
  tx += rx;
  ty += ry;
}

void copyMatrices() {
  gl2.glMatrixMode(GL2.GL_PROJECTION);
  
  projMatrix[0] = pg.projection.m00;
  projMatrix[1] = pg.projection.m10;
  projMatrix[2] = pg.projection.m20;
  projMatrix[3] = pg.projection.m30;
 
  projMatrix[4] = pg.projection.m01;
  projMatrix[5] = pg.projection.m11;
  projMatrix[6] = pg.projection.m21;
  projMatrix[7] = pg.projection.m31;
 
  projMatrix[8] = pg.projection.m02;
  projMatrix[9] = pg.projection.m12;
  projMatrix[10] = pg.projection.m22;
  projMatrix[11] = pg.projection.m32;
 
  projMatrix[12] = pg.projection.m03;
  projMatrix[13] = pg.projection.m13;
  projMatrix[14] = pg.projection.m23;
  projMatrix[15] = pg.projection.m33;
 
  gl2.glLoadMatrixf(projMatrix, 0);
 
  gl2.glMatrixMode(GL2.GL_MODELVIEW);
  mvMatrix[0] = pg.modelview.m00;
  mvMatrix[1] = pg.modelview.m10;
  mvMatrix[2] = pg.modelview.m20;
  mvMatrix[3] = pg.modelview.m30;
 
  mvMatrix[4] = pg.modelview.m01;
  mvMatrix[5] = pg.modelview.m11;
  mvMatrix[6] = pg.modelview.m21;
  mvMatrix[7] = pg.modelview.m31;
 
  mvMatrix[8] = pg.modelview.m02;
  mvMatrix[9] = pg.modelview.m12;
  mvMatrix[10] = pg.modelview.m22;
  mvMatrix[11] = pg.modelview.m32;
 
  mvMatrix[12] = pg.modelview.m03;
  mvMatrix[13] = pg.modelview.m13;
  mvMatrix[14] = pg.modelview.m23;
  mvMatrix[15] = pg.modelview.m33;
  gl2.glLoadMatrixf(mvMatrix, 0);
}

Clone this wiki locally