You definitely can combine OpenGL with Processing, but it takes a little bit of care. First, when you use a vertex array object, you have to query and save Processing’s default VAO and then restore it after using your own. Second, you have to use Processing’s camera transformation and projection matrices to make sure that your OpenGL rendering depth-sorts consistently with objects drawn with Processing.
Here’s an example where I’m using OpenGL to render 1 million points and using Processing to render a box outline around them. In drawPoints()
, I save the current VAO id before binding my own and then set it back after drawing. In the vertex shader, I transform the points by both the modelview
and projection
matrices that Processing passes into all shaders.
import java.nio.*;
import com.jogamp.opengl.*;
int nS = 100;
int N = nS * nS * nS;
PVector[] pos;
PVector[] vel;
void setup() {
size( 900, 900, P3D );
frameRate( 960 );
colorMode( HSB, 1, 1, 1, 1 );
pos = new PVector[ N ];
vel = new PVector[ N ];
for( int i=0; i<N; i++ ) {
pos[i] = new PVector( (i % nS)*2.0/nS-1,
((i/nS)%nS)*2.0/nS-1,
((i/nS/nS)%nS)*2.0/nS-1 );
vel[i] = PVector.random3D().mult(0.001);
}
initOglBuffers();
}
void draw() {
for( int i=0; i<N; i++ ) {
pos[i].add( vel[i] );
if( pos[i].x < -1 ) { pos[i].x = -2-pos[i].x; vel[i].x = -vel[i].x; }
if( pos[i].x > 1 ) { pos[i].x = 2-pos[i].x; vel[i].x = -vel[i].x; }
if( pos[i].y < -1 ) { pos[i].y = -2-pos[i].y; vel[i].y = -vel[i].y; }
if( pos[i].y > 1 ) { pos[i].y = 2-pos[i].y; vel[i].y = -vel[i].y; }
if( pos[i].z < -1 ) { pos[i].z = -2-pos[i].z; vel[i].z = -vel[i].z; }
if( pos[i].z > 1 ) { pos[i].z = 2-pos[i].z; vel[i].z = -vel[i].z; }
}
background( 0 );
pushMatrix();
camera( 0, 0, -3, 0, 0.2, 0, 0, 1, 0 );
perspective( PI/3., 1.*width/height, 1, 10 );
rotateX( 0.5 );
float t = frameCount/60.0;
rotateY( TAU*0.02*t );
drawPoints();
stroke( 1 );
noFill();
box( 2, 2, 2 );
popMatrix();
camera();
perspective();
fill( 1 );
text( N, 4, 16 );
text( frameRate, 4, 32 );
}
////
FloatBuffer posBuffer;
IntBuffer colBuffer;
int posVboId, colVboId;
int vaoId;
PJOGL pgl;
GL4 gl;
PShader shdr;
void initOglBuffers() {
pgl = (PJOGL) beginPGL();
gl = pgl.gl.getGL4();
shdr = new PShader( this, vertSrc, fragSrc );
posBuffer = allocateDirectFloatBuffer( 3*N );
colBuffer = allocateDirectIntBuffer( 1*N );
colBuffer.rewind();
for( int i=0; i<N; i++ ) {
color c = color( 1.*i/N, random(0.4, 1), random(0.7, 1) );
colBuffer.put( c );
}
colBuffer.rewind();
// Get GL ids for all the buffers
IntBuffer intBuffer = IntBuffer.allocate(2);
gl.glGenBuffers(2, intBuffer);
posVboId = intBuffer.get(0);
colVboId = intBuffer.get(1);
gl.glGenVertexArrays( 1, intBuffer );
vaoId = intBuffer.get(0);
gl.glGetIntegerv( GL3.GL_VERTEX_ARRAY_BINDING, intBuffer );
int savedVaoId = intBuffer.get(0);
gl.glBindVertexArray( vaoId );
// Set up vertex position VBO
gl.glBindBuffer( GL.GL_ARRAY_BUFFER, posVboId );
// glVertexAttribPointer( index, size, type, normalized, stride, pointer )
gl.glVertexAttribPointer( 0, 3, GL.GL_FLOAT, false, 3*Float.BYTES, 0 );
gl.glEnableVertexAttribArray( 0 ); // position
// Copy vertex color data to VBOs
gl.glBindBuffer( GL.GL_ARRAY_BUFFER, colVboId );
// glBufferData( target, size, data, usage )
gl.glBufferData( GL.GL_ARRAY_BUFFER, Integer.BYTES*N, colBuffer, GL.GL_STATIC_DRAW );
// glVertexAttribPointer( index, size, type, normalized, stride, pointer )
gl.glVertexAttribPointer( 1, 4, GL.GL_UNSIGNED_BYTE, true, 4, 0 );
gl.glEnableVertexAttribArray( 1 ); // color
gl.glBindVertexArray( savedVaoId );
endPGL();
}
void drawPoints() {
posBuffer.rewind();
for( int i=0; i<N; i++ ) {
posBuffer.put( pos[i].x );
posBuffer.put( pos[i].y );
posBuffer.put( pos[i].z );
}
posBuffer.rewind();
pgl = (PJOGL) beginPGL();
gl = pgl.gl.getGL4();
shdr.bind();
// Copy vertex position data to VBOs
gl.glBindBuffer( GL.GL_ARRAY_BUFFER, posVboId );
// glBufferData( target, size, data, usage )
gl.glBufferData( GL.GL_ARRAY_BUFFER, Float.BYTES*3*N, posBuffer, GL.GL_DYNAMIC_DRAW );
// Draw the points
gl.glEnable( GL3.GL_PROGRAM_POINT_SIZE );
IntBuffer intBuffer = IntBuffer.allocate(1);
gl.glGetIntegerv( GL3.GL_VERTEX_ARRAY_BINDING, intBuffer );
int savedVaoId = intBuffer.get(0);
gl.glBindVertexArray( vaoId );
gl.glDrawArrays( PGL.POINTS, 0, N );
gl.glBindVertexArray( savedVaoId );
shdr.unbind();
endPGL();
}
FloatBuffer allocateDirectFloatBuffer(int n) {
return ByteBuffer.allocateDirect(n * Float.BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
}
IntBuffer allocateDirectIntBuffer(int n) {
return ByteBuffer.allocateDirect(n * Integer.BYTES).order(ByteOrder.nativeOrder()).asIntBuffer();
}
String[] vertSrc = { """
#version 330 core
uniform mat4 modelview;
uniform mat4 projection;
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec4 aCol;
out vec4 vColor;
void main() {
vec4 p = modelview * vec4( aPos, 1. );
gl_Position = projection * p;
gl_PointSize = 6./-p.z;
vColor = vec4( mix( vec3(0.), aCol.bgr, clamp((4.5+p.z)/2.5, 0., 1.)), 1. );
}
""" };
String[] fragSrc = { """
#version 330 core
in vec4 vColor;
out vec4 outColor;
void main() {
outColor = vColor;
}
""" };