// This code example is created for educational purpose // by Thorsten Thormaehlen (contact: www.thormae.de). // It is distributed without any warranty. import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import com.jogamp.opengl.GL3; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLEventListener; import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.awt.GLCanvas; import javax.swing.JFrame; import com.jogamp.common.nio.Buffers; import com.jogamp.opengl.util.FPSAnimator; class Renderer { public float t = 0.0f; public int modeVal = 1; private enum VAOs {Scene, numVAOs}; private enum VBOs {SceneAll, numVBOs}; private int[] vaoID = new int[VAOs.numVAOs.ordinal()]; private int[] bufID = new int[VBOs.numVBOs.ordinal()]; private int sceneVertNo = 0; private int progID = 0; private int vertID = 0; private int fragID = 0; private int vertexLoc = 0; private int texCoordLoc = 0; private int normalLoc = 0; private int projectionLoc = 0; private int modelviewLoc = 0; private int normalMatrixLoc = 0; private int modeLoc = 0; private float[] projection = new float[16]; private float[] modelview = new float[16]; public void init(GLAutoDrawable d) { GL3 gl = d.getGL().getGL3(); // get the OpenGL 3 graphics context gl.glEnable(GL3.GL_DEPTH_TEST); setupShaders(d); // create a Vertex Array Objects (VAO) gl.glGenVertexArrays(VAOs.numVAOs.ordinal(), vaoID, 0); // generate a Vertex Buffer Object (VBO) gl.glGenBuffers(VBOs.numVBOs.ordinal(), bufID, 0); // binding the Triangle VAO gl.glBindVertexArray(vaoID[VAOs.Scene.ordinal()]); int perVertexFloats = (3+2+3); float data[] = loadVertexData("./teapot.vbo", perVertexFloats); sceneVertNo = data.length / perVertexFloats; FloatBuffer sceneVertexFB = Buffers.newDirectFloatBuffer(data.length); sceneVertexFB.put(data); sceneVertexFB.flip(); gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, bufID[VBOs.SceneAll.ordinal()]); gl.glBufferData(GL3.GL_ARRAY_BUFFER, sceneVertexFB.capacity()*Buffers.SIZEOF_FLOAT, sceneVertexFB, GL3.GL_STATIC_DRAW); int stride = (3+2+3)*Buffers.SIZEOF_FLOAT; int offset = 0; // position if (vertexLoc != -1) { gl.glVertexAttribPointer(vertexLoc, 3, GL3.GL_FLOAT, false, stride, offset); gl.glEnableVertexAttribArray(vertexLoc); } // texCoord if (texCoordLoc != -1) { offset = 0 + 3 * Buffers.SIZEOF_FLOAT; gl.glVertexAttribPointer(texCoordLoc, 2, GL3.GL_FLOAT, false, stride, offset); gl.glEnableVertexAttribArray(texCoordLoc); } // normal if (normalLoc != -1) { offset = 0 + (3 + 2) * Buffers.SIZEOF_FLOAT; gl.glVertexAttribPointer(normalLoc, 3, GL3.GL_FLOAT, false, stride, offset); gl.glEnableVertexAttribArray(normalLoc); } } public void resize(GLAutoDrawable d, int w, int h) { GL3 gl = d.getGL().getGL3(); // get the OpenGL 3 graphics context gl.glViewport(0, 0, w, h); // this function replaces gluPerspective mat4Perspective(projection, 45.0f, (float)w/(float)h, 0.5f, 4.0f); //mat4Print(projection); } public void display(GLAutoDrawable d) { GL3 gl = d.getGL().getGL3(); // get the OpenGL >= 3 graphics context gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); gl.glClear(GL3.GL_COLOR_BUFFER_BIT | GL3.GL_DEPTH_BUFFER_BIT); // camera orbits in the z=1.5 plane // and looks at the origin // mat4LookAt replaces gluLookAt double rad = Math.PI / 180.0f * t; mat4LookAt(modelview, 1.5f*(float)Math.cos(rad), 1.5f*(float)Math.sin(rad), 1.5f, // eye 0.0f, 0.0f, 0.0f, // look at 0.0f, 0.0f, 1.0f); // up float modelviewInv[] = new float[16]; float normalmatrix[] = new float[16]; mat4Invert(modelview, modelviewInv); mat4Transpose(modelviewInv, normalmatrix); gl.glUseProgram(progID); // load the current projection and modelview matrix into the // corresponding UNIFORM variables of the shader gl.glUniformMatrix4fv(projectionLoc, 1, false, projection, 0); gl.glUniformMatrix4fv(modelviewLoc, 1, false, modelview, 0); gl.glUniformMatrix4fv(normalMatrixLoc, 1, false, normalmatrix, 0); gl.glUniform1i(modeLoc, modeVal); // bind scene VAO gl.glBindVertexArray(vaoID[VAOs.Scene.ordinal()]); // render data gl.glDrawArrays(GL3.GL_TRIANGLES, 0, sceneVertNo); gl.glFlush(); } public void dispose(GLAutoDrawable d) { GL3 gl = d.getGL().getGL3(); // get the OpenGL >= 3 graphics context gl.glDeleteVertexArrays(VAOs.numVAOs.ordinal(), vaoID, 0); gl.glDeleteBuffers(VBOs.numVBOs.ordinal(), bufID, 0); gl.glDeleteProgram(progID); gl.glDeleteShader(vertID); gl.glDeleteShader(fragID); } public void setupShaders(GLAutoDrawable d) { GL3 gl = d.getGL().getGL3(); // get the OpenGL 3 graphics context vertID = gl.glCreateShader(GL3.GL_VERTEX_SHADER); fragID = gl.glCreateShader(GL3.GL_FRAGMENT_SHADER); String[] vs = loadShaderSrc("./pass.vert"); String[] fs = loadShaderSrc("./pass.frag"); gl.glShaderSource(vertID, 1, vs, null, 0); gl.glShaderSource(fragID, 1, fs, null, 0); // compile the shader gl.glCompileShader(vertID); gl.glCompileShader(fragID); // check for errors printShaderInfoLog(d, vertID); printShaderInfoLog(d, fragID); // create program and attach shaders progID = gl.glCreateProgram(); gl.glAttachShader(progID, vertID); gl.glAttachShader(progID, fragID); // "outColor" is a user-provided OUT variable // of the fragment shader. // Its output is bound to the first color buffer // in the framebuffer gl.glBindFragDataLocation(progID, 0, "outputColor"); // link the program gl.glLinkProgram(progID); // output error messages printProgramInfoLog(d, progID); // retrieve the location of the IN variables of the vertex shader vertexLoc = gl.glGetAttribLocation(progID,"inputPosition"); texCoordLoc = gl.glGetAttribLocation(progID,"inputTexCoord"); normalLoc = gl.glGetAttribLocation(progID, "inputNormal"); // retrieve the location of the UNIFORM variables of the vertex shader projectionLoc = gl.glGetUniformLocation(progID, "projection"); modelviewLoc = gl.glGetUniformLocation(progID, "modelview"); normalMatrixLoc = gl.glGetUniformLocation(progID, "normalMat"); modeLoc = gl.glGetUniformLocation(progID, "mode"); } private String[] loadShaderSrc(String name){ StringBuilder sb = new StringBuilder(); try{ InputStream is = getClass().getResourceAsStream(name); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String line; while ((line = br.readLine())!=null){ sb.append(line); sb.append('\n'); } is.close(); } catch (Exception e){ e.printStackTrace(); } return new String[]{sb.toString()}; } private void printShaderInfoLog(GLAutoDrawable d, int obj) { GL3 gl = d.getGL().getGL3(); // get the OpenGL 3 graphics context IntBuffer infoLogLengthBuf = IntBuffer.allocate(1); int infoLogLength; gl.glGetShaderiv(obj, GL3.GL_INFO_LOG_LENGTH, infoLogLengthBuf); infoLogLength = infoLogLengthBuf.get(0); if (infoLogLength > 0) { ByteBuffer byteBuffer = ByteBuffer.allocate(infoLogLength); gl.glGetShaderInfoLog(obj, infoLogLength, infoLogLengthBuf, byteBuffer); for (byte b:byteBuffer.array()){ System.err.print((char)b); } } } private void printProgramInfoLog(GLAutoDrawable d, int obj) { GL3 gl = d.getGL().getGL3(); // get the OpenGL 3 graphics context IntBuffer infoLogLengthBuf = IntBuffer.allocate(1); int infoLogLength; gl.glGetProgramiv(obj, GL3.GL_INFO_LOG_LENGTH, infoLogLengthBuf); infoLogLength = infoLogLengthBuf.get(0); if (infoLogLength > 0) { ByteBuffer byteBuffer = ByteBuffer.allocate(infoLogLength); gl.glGetProgramInfoLog(obj, infoLogLength, infoLogLengthBuf, byteBuffer); for (byte b:byteBuffer.array()){ System.err.print((char)b); } } } private float[] loadVertexData(String filename, int perVertexFloats) { float[] floatArray = new float[0]; // read vertex data from file int vertSize = 0; try { InputStream is = getClass().getResourceAsStream(filename); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String line = br.readLine(); if (line != null) { vertSize = Integer.parseInt(line); floatArray = new float[vertSize]; } int i = 0; while ((line = br.readLine()) != null && i < floatArray.length) { floatArray[i] = Float.parseFloat(line); i++; } if (i != vertSize || (vertSize % perVertexFloats) != 0) { floatArray = new float[0]; } br.close(); } catch (FileNotFoundException e) { System.out.println("Can not find vbo data file " + filename); } catch (IOException e) { e.printStackTrace(); } return floatArray; } // the following functions are some matrix and vector helpers // they work for this demo but in general it is recommended // to use more advanced matrix libraries private float vec3Dot(float[] a, float[] b) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; } private void vec3Cross(float[] a, float[] b, float[] res) { res[0] = a[1] * b[2] - b[1] * a[2]; res[1] = a[2] * b[0] - b[2] * a[0]; res[2] = a[0] * b[1] - b[0] * a[1]; } private void vec3Normalize(float[] a) { float mag = (float) Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); a[0] /= mag; a[1] /= mag; a[2] /= mag; } private void mat4Identity(float[] a) { for (int i = 0; i < 16; ++i) a[i] = 0.0f; for (int i = 0; i < 4; ++i) a[i + i * 4] = 1.0f; } private void mat4Multiply(float[] a, float[] b, float[] res) { for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { res[j*4 + i] = 0.0f; for (int k = 0; k < 4; ++k) { res[j*4 + i] += a[k*4 + i] * b[j*4 + k]; } } } } private void mat4Perspective(float[] a, float fov, float aspect, float zNear, float zFar) { float f = 1.0f / (float) (Math.tan (fov/2.0f * (Math.PI / 180.0f))); mat4Identity(a); a[0] = f / aspect; a[1 * 4 + 1] = f; a[2 * 4 + 2] = (zFar + zNear) / (zNear - zFar); a[3 * 4 + 2] = (2.0f * zFar * zNear) / (zNear - zFar); a[2 * 4 + 3] = -1.0f; a[3 * 4 + 3] = 0.0f; } private void mat4LookAt(float[] viewMatrix, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ) { float dir[] = new float[3]; float right[] = new float[3]; float up[] = new float[3]; float eye[] = new float[3]; up[0]=upX; up[1]=upY; up[2]=upZ; eye[0]=eyeX; eye[1]=eyeY; eye[2]=eyeZ; dir[0]=centerX-eyeX; dir[1]=centerY-eyeY; dir[2]=centerZ-eyeZ; vec3Normalize(dir); vec3Cross(dir,up,right); vec3Normalize(right); vec3Cross(right,dir,up); vec3Normalize(up); // first row viewMatrix[0] = right[0]; viewMatrix[4] = right[1]; viewMatrix[8] = right[2]; viewMatrix[12] = -vec3Dot(right, eye); // second row viewMatrix[1] = up[0]; viewMatrix[5] = up[1]; viewMatrix[9] = up[2]; viewMatrix[13] = -vec3Dot(up, eye); // third row viewMatrix[2] = -dir[0]; viewMatrix[6] = -dir[1]; viewMatrix[10] = -dir[2]; viewMatrix[14] = vec3Dot(dir, eye); // forth row viewMatrix[3] = 0.0f; viewMatrix[7] = 0.0f; viewMatrix[11] = 0.0f; viewMatrix[15] = 1.0f; } private void mat4Print(float[] a) { // opengl uses column major order for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { System.out.print( a[j * 4 + i] + " "); } System.out.println(" "); } } private void mat4Transpose(float[] a, float[]transposed) { int t = 0; for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { transposed[t++] = a[j * 4 + i]; } } } private boolean mat4Invert(float[] m, float[] inverse) { float inv[] = new float[16]; inv[0] = m[5]*m[10]*m[15]-m[5]*m[11]*m[14]-m[9]*m[6]*m[15]+ m[9]*m[7]*m[14]+m[13]*m[6]*m[11]-m[13]*m[7]*m[10]; inv[4] = -m[4]*m[10]*m[15]+m[4]*m[11]*m[14]+m[8]*m[6]*m[15]- m[8]*m[7]*m[14]-m[12]*m[6]*m[11]+m[12]*m[7]*m[10]; inv[8] = m[4]*m[9]*m[15]-m[4]*m[11]*m[13]-m[8]*m[5]*m[15]+ m[8]*m[7]*m[13]+m[12]*m[5]*m[11]-m[12]*m[7]*m[9]; inv[12]= -m[4]*m[9]*m[14]+m[4]*m[10]*m[13]+m[8]*m[5]*m[14]- m[8]*m[6]*m[13]-m[12]*m[5]*m[10]+m[12]*m[6]*m[9]; inv[1] = -m[1]*m[10]*m[15]+m[1]*m[11]*m[14]+m[9]*m[2]*m[15]- m[9]*m[3]*m[14]-m[13]*m[2]*m[11]+m[13]*m[3]*m[10]; inv[5] = m[0]*m[10]*m[15]-m[0]*m[11]*m[14]-m[8]*m[2]*m[15]+ m[8]*m[3]*m[14]+m[12]*m[2]*m[11]-m[12]*m[3]*m[10]; inv[9] = -m[0]*m[9]*m[15]+m[0]*m[11]*m[13]+m[8]*m[1]*m[15]- m[8]*m[3]*m[13]-m[12]*m[1]*m[11]+m[12]*m[3]*m[9]; inv[13]= m[0]*m[9]*m[14]-m[0]*m[10]*m[13]-m[8]*m[1]*m[14]+ m[8]*m[2]*m[13]+m[12]*m[1]*m[10]-m[12]*m[2]*m[9]; inv[2] = m[1]*m[6]*m[15]-m[1]*m[7]*m[14]-m[5]*m[2]*m[15]+ m[5]*m[3]*m[14]+m[13]*m[2]*m[7]-m[13]*m[3]*m[6]; inv[6] = -m[0]*m[6]*m[15]+m[0]*m[7]*m[14]+m[4]*m[2]*m[15]- m[4]*m[3]*m[14]-m[12]*m[2]*m[7]+m[12]*m[3]*m[6]; inv[10]= m[0]*m[5]*m[15]-m[0]*m[7]*m[13]-m[4]*m[1]*m[15]+ m[4]*m[3]*m[13]+m[12]*m[1]*m[7]-m[12]*m[3]*m[5]; inv[14]= -m[0]*m[5]*m[14]+m[0]*m[6]*m[13]+m[4]*m[1]*m[14]- m[4]*m[2]*m[13]-m[12]*m[1]*m[6]+m[12]*m[2]*m[5]; inv[3] = -m[1]*m[6]*m[11]+m[1]*m[7]*m[10]+m[5]*m[2]*m[11]- m[5]*m[3]*m[10]-m[9]*m[2]*m[7]+m[9]*m[3]*m[6]; inv[7] = m[0]*m[6]*m[11]-m[0]*m[7]*m[10]-m[4]*m[2]*m[11]+ m[4]*m[3]*m[10]+m[8]*m[2]*m[7]-m[8]*m[3]*m[6]; inv[11]= -m[0]*m[5]*m[11]+m[0]*m[7]*m[9]+m[4]*m[1]*m[11]- m[4]*m[3]*m[9]-m[8]*m[1]*m[7]+m[8]*m[3]*m[5]; inv[15]= m[0]*m[5]*m[10]-m[0]*m[6]*m[9]-m[4]*m[1]*m[10]+ m[4]*m[2]*m[9]+m[8]*m[1]*m[6]-m[8]*m[2]*m[5]; float det = m[0]*inv[0]+m[1]*inv[4]+m[2]*inv[8]+m[3]*inv[12]; if (det == 0) return false; det = 1.0f / det; for (int i = 0; i < 16; i++) inverse[i] = inv[i] * det; return true; } } class MyGui extends JFrame implements GLEventListener { private Renderer renderer; public void createGUI() { setTitle("Transforming normals"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); GLProfile glp = GLProfile.getDefault(); GLCapabilities caps = new GLCapabilities(glp); GLCanvas canvas = new GLCanvas(caps); setSize(320, 320); getContentPane().add(canvas); final FPSAnimator ani = new FPSAnimator(canvas, 60, true); canvas.addGLEventListener(this); setVisible(true); renderer = new Renderer(); canvas.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent event) { boolean redraw = false; String modeStr = ""; switch(event.getKeyCode()) { case '1': renderer.modeVal = 1; redraw = true; modeStr = "Global Normals"; break; case '2': renderer.modeVal = 2; redraw = true; modeStr = "Local Normals"; break; case '3': renderer.modeVal = 3; redraw = true; modeStr = "Global Vertex Positions"; break; case '4': renderer.modeVal = 4; redraw = true; modeStr = "Local Vertex Positions"; break; case '5': renderer.modeVal = 5; redraw = true; modeStr ="Texture Coordinates"; break; } if(redraw) { setTitle(modeStr); } } }); ani.start(); } @Override public void init(GLAutoDrawable d) { renderer.init(d); } @Override public void reshape(GLAutoDrawable d, int x, int y, int width, int height) { renderer.resize(d, width, height); } @Override public void display(GLAutoDrawable d) { float offset = 1.0f; renderer.t += offset; renderer.display(d); } @Override public void dispose(GLAutoDrawable d) { renderer.dispose(d); } } public class ShaderNormalTrans { public static void main(String[] args) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { MyGui myGUI = new MyGui(); myGUI.createGUI(); } }); } }