package com.javamarcher.renderer; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import org.joml.Matrix4f; import org.joml.Vector3f; import org.lwjgl.BufferUtils; import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL30; import com.javamarcher.configuration.GlobalConfig; import com.javamarcher.gamestate.Notifiable; import com.javamarcher.geometry.Quad; import com.javamarcher.geometry.VertexData; import com.javamarcher.input.ScrollInput; import com.javamarcher.shader.ShaderCreator; import com.javamarcher.systems.CameraSystem; import com.javamarcher.util.TextureUnit; import com.javamarcher.window.MainWindow; public class Renderer implements Notifiable { private final Quad quad = new Quad(); private int vaoId; // VBO for positions + colors + uv's private int interleavedVboId; private int vboVertId; private ShaderCreator shader; private CameraSystem cameraSystem; // Shader uniform id's private int projectionMatrixLocation; private int viewMatrixLocation; private int modelMatrixLocation; private int frustumMatrixLocation; private int inverseViewMatrixLocation; private int cameraPositionLocation; private int deltaTimeLocation; private int globalTimeLocation; private int sunHeightLocation; private int maxStepsLocation; private int maxDistanceLocation; private int epsilonLocation; private int sunDirectionLocation; private int fogColorLocation; private int fogStrengthLocation; private int shadowStrengthLocation; private int shadowSoftnessLocation; private int shadowIterationLocation; private int highQualityModeLocation; private int performanceCountLocation; private int cloudIterationsLocation; private int aoEnabledLocation; private FloatBuffer matrix44Buffer = BufferUtils.createFloatBuffer(4 * 4); private FloatBuffer vector3Buffer = BufferUtils.createFloatBuffer(3); //Texture variable private TextureUnit noiseTexture; private TextureUnit noiseColorTexture; private TextureUnit waterBumpTexture; private TextureUnit cliffBumpTexture; private ScrollInput scrollInput; public void setCameraSystem(CameraSystem camSystem) { this.cameraSystem = camSystem; } @Override public void start() { // Create a new Vertex Array Object and bind it vaoId = GL30.glGenVertexArrays(); GL30.glBindVertexArray(vaoId); GLFW.glfwSetScrollCallback(MainWindow.WINDOW, scrollInput = new ScrollInput()); initializeInterleavedData(); // Deselect VAO -> Bind to 0 GL30.glBindVertexArray(0); // VBO for indices initializeIndexVBO(); // Shader setup initializeShaderProgram(); noiseTexture = new TextureUnit("Textures/noise.png", GL13.GL_TEXTURE0, shader.getProgramId(), 0, "texNoise"); noiseColorTexture = new TextureUnit("Textures/ColorNoise.png", GL13.GL_TEXTURE1, shader.getProgramId(), 1, "texColorNoise"); waterBumpTexture = new TextureUnit("Textures/WaterBump.png", GL13.GL_TEXTURE2, shader.getProgramId(), 2, "texWaterBump"); cliffBumpTexture = new TextureUnit("Textures/CliffBump.png", GL13.GL_TEXTURE3, shader.getProgramId(), 3, "texCliffBump"); } /** * Creates an interleaved buffer with the position and color values. * Binds VBO's and attribute pointers. */ private void initializeInterleavedData() { // Generate an interleaved buffer which contains position and color data final FloatBuffer interleavedBuffer = BufferUtils.createFloatBuffer(quad.getVertices().length * VertexData.ELEMENT_COUNT); for(final VertexData v : quad.getVertices()) { interleavedBuffer.put(v.getPosition()); interleavedBuffer.put(v.getColor()); interleavedBuffer.put(v.getUV()); // X, Y, Z, W | R, G, B, A | U, V } interleavedBuffer.flip(); interleavedVboId = GL15.glGenBuffers(); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, interleavedVboId); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, interleavedBuffer, GL15.GL_STATIC_DRAW); // Set attribute pointer with stride and offset GL20.glVertexAttribPointer(0, 4, GL11.GL_FLOAT, false, VertexData.VERTEX_BYTE_SIZE, 0); GL20.glVertexAttribPointer(1, 4, GL11.GL_FLOAT, false, VertexData.VERTEX_BYTE_SIZE, 4 * VertexData.ELEMENT_BYTE_SIZE); GL20.glVertexAttribPointer(2, 2, GL11.GL_FLOAT, false, VertexData.VERTEX_BYTE_SIZE, 4 * VertexData.ELEMENT_BYTE_SIZE); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GL30.glBindVertexArray(0); } private void initializeShaderProgram() { shader = new ShaderCreator(GlobalConfig.MAIN_RAYMARCHER_VERT, GlobalConfig.MAIN_RAYMARCHER_FRAG); shader.createShader(); // Position information will be attribute 0 GL20.glBindAttribLocation(shader.getProgramId(), 0, "iPosition"); // Color information will be attribute 1 GL20.glBindAttribLocation(shader.getProgramId(), 1, "iColor"); // UV -> attribute 2 GL20.glBindAttribLocation(shader.getProgramId(), 2, "iUv"); // Link and validate shader program GL20.glLinkProgram(shader.getProgramId()); GL20.glValidateProgram(shader.getProgramId()); // Uniform locations projectionMatrixLocation = GL20.glGetUniformLocation(shader.getProgramId(), "projectionMatrix"); viewMatrixLocation = GL20.glGetUniformLocation(shader.getProgramId(), "viewMatrix"); modelMatrixLocation = GL20.glGetUniformLocation(shader.getProgramId(), "modelMatrix"); frustumMatrixLocation = GL20.glGetUniformLocation(shader.getProgramId(), "frustumMatrix"); inverseViewMatrixLocation = GL20.glGetUniformLocation(shader.getProgramId(), "inverseViewMatrix"); cameraPositionLocation = GL20.glGetUniformLocation(shader.getProgramId(), "cameraWorldPosition"); deltaTimeLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uDeltaTime"); globalTimeLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uGlobalTime"); sunHeightLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uSunHeight"); maxStepsLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uMaxSteps"); maxDistanceLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uMaxDistance"); epsilonLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uEpsilon"); sunDirectionLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uSunDirection"); fogColorLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uFogColor"); fogStrengthLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uFogStrength"); shadowStrengthLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uShadowStrength"); shadowSoftnessLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uShadowSoftness"); shadowIterationLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uShadowIterations"); highQualityModeLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uHighQualityMode"); performanceCountLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uVisualizePerformance"); cloudIterationsLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uCloudIterations"); aoEnabledLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uAOEnabled"); } private void initializeIndexVBO() { final ByteBuffer indicesBuffer = BufferUtils.createByteBuffer(quad.getIndices().length); indicesBuffer.put(quad.getIndices()); indicesBuffer.flip(); // Create new VBO for the indices vboVertId = GL15.glGenBuffers(); GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboVertId); GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW); // Deselect VBO -> Bind to 0 GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); } @Override public void update(double deltaTime) { // Bind Shader GL20.glUseProgram(shader.getProgramId()); // Load uniform data into shader prepareShaderUniforms(); // Bind the VAO which has the vertex information GL30.glBindVertexArray(vaoId); GL20.glEnableVertexAttribArray(0); GL20.glEnableVertexAttribArray(1); GL20.glEnableVertexAttribArray(2); // Bind to the index VBO that has all the information about the order of the vertices GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboVertId); // Draw Quad GL11.glDrawElements(GL11.GL_TRIANGLES, quad.getIndices().length, GL11.GL_UNSIGNED_BYTE, 0); // Deselect GL20.glDisableVertexAttribArray(0); GL20.glDisableVertexAttribArray(1); GL20.glDisableVertexAttribArray(2); GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); GL30.glBindVertexArray(0); // Unbind Shader GL20.glUseProgram(0); } private void prepareShaderUniforms() { cameraSystem.getDefaultCamera().getProjectionMatrix().get(matrix44Buffer); matrix44Buffer.flip(); matrix44Buffer.clear(); GL20.glUniformMatrix4fv(projectionMatrixLocation, false, matrix44Buffer); cameraSystem.getDefaultCamera().getViewMatrix().get(matrix44Buffer); matrix44Buffer.flip(); matrix44Buffer.clear(); GL20.glUniformMatrix4fv(viewMatrixLocation, false, matrix44Buffer); quad.getTransform().getModelMatrix().get(matrix44Buffer); matrix44Buffer.flip(); matrix44Buffer.clear(); GL20.glUniformMatrix4fv(modelMatrixLocation, false, matrix44Buffer); // Necessary for Raymarching cameraSystem.getDefaultCamera().GetFrustumCorners().get(matrix44Buffer); matrix44Buffer.flip(); matrix44Buffer.clear(); GL20.glUniformMatrix4fv(frustumMatrixLocation, false, matrix44Buffer); Matrix4f inverseView = new Matrix4f(cameraSystem.getDefaultCamera().getViewMatrix()); inverseView.invert(); inverseView.get(matrix44Buffer); matrix44Buffer.flip(); matrix44Buffer.clear(); GL20.glUniformMatrix4fv(inverseViewMatrixLocation, false, matrix44Buffer); vector3Buffer.clear(); cameraSystem.getDefaultCamera().getTransform().position.get(vector3Buffer); vector3Buffer.flip(); vector3Buffer.clear(); GL20.glUniform3fv(cameraPositionLocation, vector3Buffer); // Delta time & Global time GL20.glUniform1f(deltaTimeLocation, (float) MainWindow.getDeltaTime()); GL20.glUniform1f(globalTimeLocation, (float) MainWindow.getGlobalTime()); // Sun Height GL20.glUniform1f(sunHeightLocation, scrollInput.getyOffset()); // Raymarcher parameters GL20.glUniform1i(maxStepsLocation, GlobalConfig.MAX_STEPS); GL20.glUniform1f(maxDistanceLocation, (float) GlobalConfig.MAX_DISTANCE); GL20.glUniform1f(epsilonLocation, (float) GlobalConfig.EPSILON); // Sun parameters final Vector3f normalizedSunDirection = new Vector3f(GlobalConfig.SUN_DIRECTION); normalizedSunDirection.normalize(); GL20.glUniform3f(sunDirectionLocation, normalizedSunDirection.x, normalizedSunDirection.y, normalizedSunDirection.z); //Cloud iterations GL20.glUniform1i(cloudIterationsLocation, GlobalConfig.CLOUD_ITERATIONS); // Fog GL20.glUniform3f(fogColorLocation, GlobalConfig.FOG_COLOR.x, GlobalConfig.FOG_COLOR.y, GlobalConfig.FOG_COLOR.z); GL20.glUniform1f(fogStrengthLocation, (float) GlobalConfig.FOG_STRENGTH); // Shadow GL20.glUniform1f(shadowStrengthLocation, (float) GlobalConfig.SHADOW_STRENGTH); GL20.glUniform1f(shadowSoftnessLocation, (float) GlobalConfig.SHADOW_SOFTNESS); GL20.glUniform1i(shadowIterationLocation, GlobalConfig.SHADOW_ITERATIONS); // Quality Mode GL20.glUniform1i(highQualityModeLocation, GlobalConfig.HIGH_QUALITY_MODE ? 1 : 0); // Performance Visualization GL20.glUniform1f(performanceCountLocation, GlobalConfig.PERFORMANCE_VISUALIZATION ? 1 : 0); // Ambient Occlusion GL20.glUniform1f(aoEnabledLocation, GlobalConfig.AO_ENABLED ? 1 : 0); } @Override public void destroy() { // Delete the texture GL11.glDeleteTextures(noiseTexture.getTextureId()); GL11.glDeleteTextures(noiseColorTexture.getTextureId()); GL11.glDeleteTextures(waterBumpTexture.getTextureId()); GL11.glDeleteTextures(cliffBumpTexture.getTextureId()); GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); GL15.glDeleteBuffers(vboVertId); // Disable the VBO index from the VAO attributes list GL20.glDisableVertexAttribArray(0); // Delete the VBO GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GL15.glDeleteBuffers(vboVertId); // Delete the VAO GL30.glBindVertexArray(0); GL30.glDeleteVertexArrays(vaoId); // Shader cleanup shader.cleanup(); } }