import java.io.IOException;
import java.io.StringReader;
import java.nio.FloatBuffer;
import java.util.Arrays;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
/** Renders a colored triangle using Vertex Buffer Objects
*/
public class GLSLExample {
final int maxFps = 60;
private int fps;
private long nextFps;
private long lastFrame;
private int modelViewMatrixUniformLocation;
private int projectionMatrixUniformLocation;
private VAO vao;
public GLSLExample() {
float[] modelViewMatrix = null;
float[] projectionMatrix = null;
float horzFOV = 90;
float vertFOV = 70;
float zNear = 1.9f;
float zFar = 11f;
float widthRatio = horzFOV/vertFOV;
initGL("Open GL/GLSL Test", 800, 600);
projectionMatrix = frustumMatrix(-widthRatio, widthRatio, -1, 1, zNear, zFar);
//projectionMatrix = orthoMatrix(-1, 1, -1, 1, zNear, zFar);
FloatBuffer projectionBuffer = BufferUtils.createFloatBuffer(16);
projectionBuffer.put(projectionMatrix);
projectionBuffer.flip();
modelViewMatrix = lookAtMatrix(0, 0, 0, 270, 0);
FloatBuffer modelViewBuffer = BufferUtils.createFloatBuffer(16);
modelViewBuffer.put(modelViewMatrix);
modelViewBuffer.flip();
Shader shader = shaderInit();
modelViewMatrixUniformLocation = GL20.glGetUniformLocation(shader.getProgram(), "modelViewMatrix");
projectionMatrixUniformLocation = GL20.glGetUniformLocation(shader.getProgram(), "projectionMatrix");
GL20.glUniformMatrix4(modelViewMatrixUniformLocation, false, modelViewBuffer);
GL20.glUniformMatrix4(projectionMatrixUniformLocation, false, projectionBuffer);
System.out.println("ModelView: " + Arrays.toString(modelViewMatrix) );
System.out.println("Projection: " + Arrays.toString(projectionMatrix) );
this.vao = new VAO();
vao.createVAO();
gameLoop();
vao.destroyVAO();
destroy();
}
/** initGL, initialize Open GL display and create vbo
* @return two ints, the first is the vbo created and the second is the ibo created
*/
private void initGL(String title, int width, int height) {
try {
Display.setDisplayMode(new DisplayMode(width, height));
Display.setTitle(title);
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
}
}
private Shader shaderInit() {
Shader shader = null;
StringReader vertexShaderSource = new StringReader(
"#version 130\n" +
"in vec3 in_Position;\n" +
"in vec4 in_Color;\n" +
"out vec4 frag_Color;\n" +
"uniform mat4 modelViewMatrix;\n" +
"uniform mat4 projectionMatrix;\n" +
"void main(void) {\n" +
"gl_Position = (projectionMatrix * modelViewMatrix) * vec4(in_Position, 1.0);\n" +
"frag_Color = in_Color;\n" +
"}\n");
StringReader fragmentShaderSource = new StringReader(
"#version 130\n" +
"in vec4 frag_Color;\n" +
"out vec4 out_Color;\n" +
"void main(void) {\n" +
"out_Color = frag_Color;\n" +
"}\n");
try {
shader = Shader.createShader(vertexShaderSource, fragmentShaderSource);
Shader.linkUseShader(shader.getProgram());
} catch (IOException e) {
System.err.println("Error loading shader files");
e.printStackTrace();
}
return shader;
}
private void gameLoop() {
nextFps = getTime();
while(!Display.isCloseRequested()) {
updateDelta();
updateFps();
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
render();
Display.update();
Display.sync(maxFps);
}
}
private void render() {
GL30.glBindVertexArray(this.vao.getId());
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
GL30.glBindVertexArray(0);
}
/** getDelta, calculate how many milliseconds have passed since last frame.
* @return milliseconds passed since last frame
*/
public long updateDelta() {
long time = getTime();
long delta = time - lastFrame;
lastFrame = time;
return delta;
}
/** getTime
* @return the current time in milliseconds
*/
public long getTime() {
return (Sys.getTime() * 1000) / Sys.getTimerResolution();
}
/** updateFps, calculate the FPS and set it in the title bar
*/
public void updateFps() {
if(getTime() > nextFps) {
Display.setTitle("Open GL/GLSL Test - FPS: " + fps);
fps = 0;
nextFps += 1000;
}
fps++;
}
private void destroy() {
Display.destroy();
}
/** orthoMatrix, generates a projection/orthogonal matrix from the input parameters
* @return an array of 16 floats in column major order for use as an Open GL projection matrix
*/
public static float[] orthoMatrix(float left, float right, float bottom, float top, float near, float far) {
float[] mat = new float[] {
2/(right-left), 0, 0, 0,
0, 2/(top-bottom), 0, 0,
0, 0, -2/(far-near), 0,
-(right+left)/(right-left), -(top+bottom)/(top-bottom), -(far+near)/(far-near), 1,
};
return mat;
}
/** frustumMatrix, generate a projection/frustrum matrix from the input parameters
* @param near - the distance to the near culling plan (should be greater than 0)
* @param far - the distance to the far culling plan (should be greater than 'near')
* @return an array of 16 floats in column major order for use as an Open GL projection matrix
*/
public static float[] frustumMatrix(float left, float right, float bottom, float top, float near, float far) {
float[] mat = new float[] {
(2*near)/(right-left), 0, 0, 0,
0, (2*near)/(top-bottom), 0, 0,
(right+left)/(right-left), (top+bottom)/(top-bottom), -(far+near)/(far-near), -1,
0, 0, -(2*far*near)/(far-near), 0,
};
return mat;
}
/** lookAtMatrix, generates a model-view matrix 'looking at' the specified direction
* @return
*/
public static float[] lookAtMatrix(float xCenter, float yCenter, float zCenter, float horzRotation, float vertRotation) {
float panRadius = 1.0f; // Causes the camera to rotate around the central point
float xLook = (float) Math.cos( (horzRotation)*Math.PI/180 );
float zLook = (float) Math.sin( (horzRotation)*Math.PI/180 );
float yLook = (float) Math.sin( (vertRotation)*Math.PI/180 );
// Because we are creating a 3-dimensional vector, the square of the cosine of the 3rd vector should equal
// the sum of the squares of the first 2 vectors, so we multiple the first 2 vectors by the cosine of the
// 3rd vector to ensure that the sum of the square of all 3 vector parts equal 1
float xzNormalize = (float) Math.cos( (vertRotation)*Math.PI/180 ); // The square of the x and z parts of the vector should equal the square of this cosine value
// If the camera angle is at +-90 than adjust it a very small amount back toward level to prevent the camera view from flipping over
if(xzNormalize < 0.001f) { xzNormalize = 0.001f; }
// Multiple the x and z part of the vector by the cosine value calculated to adjust/average them into the unit vector
float ratioXNormalize = xLook * xzNormalize;
float ratioZNormalize = zLook * xzNormalize;
float xEye = -(ratioXNormalize*panRadius)+xCenter;
float yEye = -(yLook*panRadius)+yCenter;
float zEye = -(ratioZNormalize*panRadius)+zCenter;
float xUp = 0;
float yUp = 1;
float zUp = 0;
float[] eye = new float[] {xEye, yEye, zEye};
float[] f = normalize(xCenter-xEye, yCenter-yEye, zCenter-zEye);
float[] u = normalize(xUp, yUp, zUp);
float[] s = normalize(cross(f, u));
u = cross(s, f);
float[] m = new float[16];
m[0+0] = s[0];
m[4+0] = s[1];
m[8+0] = s[2];
m[0+1] = u[0];
m[4+1] = u[1];
m[8+1] = u[2];
m[0+2] = -f[0];
m[4+2] = -f[1];
m[8+2] = -f[2];
m[12+0] = -dot(s, eye);
m[12+1] = -dot(u, eye);
m[12+2] = dot(f, eye);
m[15] = 1.0f;
return m;
}
public static float[] normalize(float x, float y, float z) {
float length = (float) Math.sqrt(x*x + y*y + z*z);
return new float[] {
x/length,
y/length,
z/length,
};
}
public static float[] normalize(float[] a) {
float length = (float) Math.sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
return new float[] {
a[0]/length,
a[1]/length,
a[2]/length,
};
}
public static float[] cross(float[] a, float[] b) {
return new float[] {
a[1]*b[2] - a[2]*b[1],
a[2]*b[0] - a[0]*b[2],
a[0]*b[1] - a[1]*b[0],
};
}
public static float dot(float[] a, float[] b) {
return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
}
/** Main method for testing the example
*/
public static void main(String[] args) {
new GLSLExample();
}
}