From 22e4221efac9e0558591c0199b0b8db2dd8d8885 Mon Sep 17 00:00:00 2001 From: Kai Burjack Date: Wed, 29 Apr 2015 19:27:33 +0200 Subject: [PATCH] Add shadow mapping demo. It uses a FBO with a depth-attachment ony. The fragment shader samples the depth values from a sampler2D and uses clip-space to NDC-space perspective divide together with a bias matrix and a small depth offset to combat shadow acne. --- res/demo/shadowMapping-fs.glsl | 9 + res/demo/shadowMapping-vs.glsl | 13 + res/demo/shadowMappingShade-fs.glsl | 38 ++ res/demo/shadowMappingShade-vs.glsl | 28 ++ .../org/lwjgl/demo/opengl/DemoUtils.java | 101 +++++ .../lwjgl/demo/opengl/ShadowMappingDemo.java | 397 ++++++++++++++++++ .../lwjgl/demo/opengl/raytracing/Scene.java | 6 + src/tests/org/lwjgl/demo/util/Matrix4f.java | 29 ++ 8 files changed, 621 insertions(+) create mode 100644 res/demo/shadowMapping-fs.glsl create mode 100644 res/demo/shadowMapping-vs.glsl create mode 100644 res/demo/shadowMappingShade-fs.glsl create mode 100644 res/demo/shadowMappingShade-vs.glsl create mode 100644 src/tests/org/lwjgl/demo/opengl/DemoUtils.java create mode 100644 src/tests/org/lwjgl/demo/opengl/ShadowMappingDemo.java diff --git a/res/demo/shadowMapping-fs.glsl b/res/demo/shadowMapping-fs.glsl new file mode 100644 index 0000000000..d40fbc39f3 --- /dev/null +++ b/res/demo/shadowMapping-fs.glsl @@ -0,0 +1,9 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: http://lwjgl.org/license.php + */ +#version 130 + +void main(void) { + /* Nothing to do here... we are only rendering depth */ +} diff --git a/res/demo/shadowMapping-vs.glsl b/res/demo/shadowMapping-vs.glsl new file mode 100644 index 0000000000..488b57ddcd --- /dev/null +++ b/res/demo/shadowMapping-vs.glsl @@ -0,0 +1,13 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: http://lwjgl.org/license.php + */ +#version 130 + +uniform mat4 modelViewProjectionMatrix; + +in vec3 position; + +void main(void) { + gl_Position = modelViewProjectionMatrix * vec4(position, 1.0); +} diff --git a/res/demo/shadowMappingShade-fs.glsl b/res/demo/shadowMappingShade-fs.glsl new file mode 100644 index 0000000000..5e5b6fb2d8 --- /dev/null +++ b/res/demo/shadowMappingShade-fs.glsl @@ -0,0 +1,38 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: http://lwjgl.org/license.php + */ +#version 130 + +#define DEPTH_OFFSET 0.00005 +#define LIGHT_INTENSITY 0.9 +#define SHADOW_INTENSITY 0.1 + +uniform sampler2D depthTexture; +uniform mat4 lightModelViewMatrix; +uniform vec3 lightPosition; +uniform vec3 lightLookAt; + +in vec4 lightBiasedClipPosition; +in vec3 worldNormal; +out vec4 color; + +void main(void) { + /* Convert the linearly interpolated clip-space position to NDC */ + vec4 lightNDCPosition = lightBiasedClipPosition / lightBiasedClipPosition.w; + + /* Sample the depth from the depth texture */ + vec4 depth = texture2D(depthTexture, lightNDCPosition.xy); + + /* Additionally, do standard lambertian/diffuse lighting */ + float dot = max(0.0, dot(normalize(lightPosition - lightLookAt), worldNormal)); + + /* "in shadow" test... */ + if (depth.z >= lightNDCPosition.z - DEPTH_OFFSET) { + /* lit */ + color = vec4(LIGHT_INTENSITY, LIGHT_INTENSITY, LIGHT_INTENSITY, 1.0) * dot; + } else { + /* in shadow */ + color = vec4(SHADOW_INTENSITY, SHADOW_INTENSITY, SHADOW_INTENSITY, 1.0); + } +} diff --git a/res/demo/shadowMappingShade-vs.glsl b/res/demo/shadowMappingShade-vs.glsl new file mode 100644 index 0000000000..9e9cdd72c6 --- /dev/null +++ b/res/demo/shadowMappingShade-vs.glsl @@ -0,0 +1,28 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: http://lwjgl.org/license.php + */ +#version 130 + +uniform mat4 modelViewProjectionMatrix; +uniform mat4 lightModelViewProjectionMatrix; +uniform mat4 biasMatrix; + +in vec3 position; +in vec3 normal; +out vec4 lightBiasedClipPosition; +out vec3 worldNormal; + +void main(void) { + /* Pass the normal to the fragment shader (we do lighting computations in world coordinates) */ + worldNormal = normal; + + /* Compute vertex position as seen from + the light and use linear interpolation when passing it + to the fragment shader + */ + lightBiasedClipPosition = biasMatrix * lightModelViewProjectionMatrix * vec4(position, 1.0); + + /* Normally transform the vertex */ + gl_Position = modelViewProjectionMatrix * vec4(position, 1.0); +} diff --git a/src/tests/org/lwjgl/demo/opengl/DemoUtils.java b/src/tests/org/lwjgl/demo/opengl/DemoUtils.java new file mode 100644 index 0000000000..b681cfddc9 --- /dev/null +++ b/src/tests/org/lwjgl/demo/opengl/DemoUtils.java @@ -0,0 +1,101 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: http://lwjgl.org/license.php + */ +package org.lwjgl.demo.opengl; + +import static org.lwjgl.demo.util.IOUtil.ioResourceToByteBuffer; +import static org.lwjgl.opengl.GL20.GL_COMPILE_STATUS; +import static org.lwjgl.opengl.GL20.glCompileShader; +import static org.lwjgl.opengl.GL20.glCreateShader; +import static org.lwjgl.opengl.GL20.glGetShaderInfoLog; +import static org.lwjgl.opengl.GL20.glGetShaderi; +import static org.lwjgl.opengl.GL20.glShaderSource; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +import org.lwjgl.BufferUtils; +import org.lwjgl.PointerBuffer; +import org.lwjgl.demo.util.Vector3f; + +/** + * Utility methods for most of the ray tracing demos. + * + * @author Kai Burjack + * + */ +public class DemoUtils { + + /** + * Write the vertices (position and normal) of an axis-aligned unit box into + * the provided {@link FloatBuffer}. + * + * @param fv + * the {@link FloatBuffer} receiving the vertex position and + * normal + */ + public static void triangulateUnitBox(FloatBuffer fv) { + triangulateBox(new Vector3f(-1.0f, -1.0f, -1.0f), new Vector3f(1.0f, 1.0f, 1.0f), fv); + } + + /** + * Write the vertices (position and normal) of an axis-aligned box with the + * given corner coordinates into the provided {@link FloatBuffer}. + * + * @param min + * the min corner + * @param max + * the max corner + * @param fv + * the {@link FloatBuffer} receiving the vertex position and + * normal + */ + public static void triangulateBox(Vector3f min, Vector3f max, FloatBuffer fv) { + /* Front face */ + fv.put(min.x).put(min.y).put(max.z).put(0.0f).put(0.0f).put(1.0f); + fv.put(max.x).put(min.y).put(max.z).put(0.0f).put(0.0f).put(1.0f); + fv.put(max.x).put(max.y).put(max.z).put(0.0f).put(0.0f).put(1.0f); + fv.put(max.x).put(max.y).put(max.z).put(0.0f).put(0.0f).put(1.0f); + fv.put(min.x).put(max.y).put(max.z).put(0.0f).put(0.0f).put(1.0f); + fv.put(min.x).put(min.y).put(max.z).put(0.0f).put(0.0f).put(1.0f); + /* Back face */ + fv.put(max.x).put(min.y).put(min.z).put(0.0f).put(0.0f).put(-1.0f); + fv.put(min.x).put(min.y).put(min.z).put(0.0f).put(0.0f).put(-1.0f); + fv.put(min.x).put(max.y).put(min.z).put(0.0f).put(0.0f).put(-1.0f); + fv.put(min.x).put(max.y).put(min.z).put(0.0f).put(0.0f).put(-1.0f); + fv.put(max.x).put(max.y).put(min.z).put(0.0f).put(0.0f).put(-1.0f); + fv.put(max.x).put(min.y).put(min.z).put(0.0f).put(0.0f).put(-1.0f); + /* Left face */ + fv.put(min.x).put(min.y).put(min.z).put(-1.0f).put(0.0f).put(0.0f); + fv.put(min.x).put(min.y).put(max.z).put(-1.0f).put(0.0f).put(0.0f); + fv.put(min.x).put(max.y).put(max.z).put(-1.0f).put(0.0f).put(0.0f); + fv.put(min.x).put(max.y).put(max.z).put(-1.0f).put(0.0f).put(0.0f); + fv.put(min.x).put(max.y).put(min.z).put(-1.0f).put(0.0f).put(0.0f); + fv.put(min.x).put(min.y).put(min.z).put(-1.0f).put(0.0f).put(0.0f); + /* Right face */ + fv.put(max.x).put(min.y).put(max.z).put(1.0f).put(0.0f).put(0.0f); + fv.put(max.x).put(min.y).put(min.z).put(1.0f).put(0.0f).put(0.0f); + fv.put(max.x).put(max.y).put(min.z).put(1.0f).put(0.0f).put(0.0f); + fv.put(max.x).put(max.y).put(min.z).put(1.0f).put(0.0f).put(0.0f); + fv.put(max.x).put(max.y).put(max.z).put(1.0f).put(0.0f).put(0.0f); + fv.put(max.x).put(min.y).put(max.z).put(1.0f).put(0.0f).put(0.0f); + /* Top face */ + fv.put(min.x).put(max.y).put(max.z).put(0.0f).put(1.0f).put(0.0f); + fv.put(max.x).put(max.y).put(max.z).put(0.0f).put(1.0f).put(0.0f); + fv.put(max.x).put(max.y).put(min.z).put(0.0f).put(1.0f).put(0.0f); + fv.put(max.x).put(max.y).put(min.z).put(0.0f).put(1.0f).put(0.0f); + fv.put(min.x).put(max.y).put(min.z).put(0.0f).put(1.0f).put(0.0f); + fv.put(min.x).put(max.y).put(max.z).put(0.0f).put(1.0f).put(0.0f); + /* Bottom face */ + fv.put(min.x).put(min.y).put(min.z).put(0.0f).put(-1.0f).put(0.0f); + fv.put(max.x).put(min.y).put(min.z).put(0.0f).put(-1.0f).put(0.0f); + fv.put(max.x).put(min.y).put(max.z).put(0.0f).put(-1.0f).put(0.0f); + fv.put(max.x).put(min.y).put(max.z).put(0.0f).put(-1.0f).put(0.0f); + fv.put(min.x).put(min.y).put(max.z).put(0.0f).put(-1.0f).put(0.0f); + fv.put(min.x).put(min.y).put(min.z).put(0.0f).put(-1.0f).put(0.0f); + } + +} diff --git a/src/tests/org/lwjgl/demo/opengl/ShadowMappingDemo.java b/src/tests/org/lwjgl/demo/opengl/ShadowMappingDemo.java new file mode 100644 index 0000000000..37df9503be --- /dev/null +++ b/src/tests/org/lwjgl/demo/opengl/ShadowMappingDemo.java @@ -0,0 +1,397 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: http://lwjgl.org/license.php + */ +package org.lwjgl.demo.opengl; + +import static org.lwjgl.demo.util.IOUtil.ioResourceToByteBuffer; +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL15.*; +import static org.lwjgl.opengl.GL20.*; +import static org.lwjgl.opengl.GL30.*; +import static org.lwjgl.system.MemoryUtil.NULL; +import static org.lwjgl.system.MemoryUtil.memAddress; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +import org.lwjgl.BufferUtils; +import org.lwjgl.PointerBuffer; +import org.lwjgl.demo.opengl.raytracing.Scene; +import org.lwjgl.demo.util.Camera; +import org.lwjgl.demo.util.Matrix4f; +import org.lwjgl.demo.util.Vector3f; +import org.lwjgl.glfw.Callbacks; +import org.lwjgl.glfw.GLFWErrorCallback; +import org.lwjgl.glfw.GLFWFramebufferSizeCallback; +import org.lwjgl.glfw.GLFWKeyCallback; +import org.lwjgl.glfw.GLFWvidmode; +import org.lwjgl.opengl.GLContext; +import org.lwjgl.system.libffi.Closure; + +/** + * Simple demo to showcase shadow mapping with a custom shader doing perspective + * divide and depth test (i.e. no sampler2DShadow). + * + * @author Kai Burjack + */ +public class ShadowMappingDemo { + + private static Vector3f[] boxes = Scene.boxes2; + + static int shadowMapSize = 512; + static Vector3f lightPosition = new Vector3f(6.0f, 3.0f, 6.0f); + static Vector3f lightLookAt = new Vector3f(0.0f, 1.0f, 0.0f); + static Vector3f cameraPosition = new Vector3f(-3.0f, 3.0f, 6.0f); + static Vector3f cameraLookAt = new Vector3f(0.0f, 0.0f, 0.0f); + + long window; + int width = 1200; + int height = 800; + + int vao; + int vbo; + int shadowProgram; + int shadowProgramMVPUniform; + int normalProgram; + int normalProgramBiasUniform; + int normalProgramMVPUniform; + int normalProgramLMVPUniform; + int normalProgramLightPosition; + int normalProgramLightLookAt; + int fbo; + int depthTexture; + int samplerLocation; + + ByteBuffer matrixByteBuffer = BufferUtils.createByteBuffer(4 * 16); + FloatBuffer matrixByteBufferFloatView = matrixByteBuffer.asFloatBuffer(); + + Camera lightView = new Camera(); + Camera cameraView = new Camera(); + Matrix4f lightMatrix = new Matrix4f(); + Matrix4f biasMatrix = new Matrix4f(0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f, 0.5f, 0.0f, + 0.0f, 0.0f, 1.0f); + Matrix4f cameraMatrix = new Matrix4f(); + + GLContext ctx; + GLFWErrorCallback errCallback; + GLFWKeyCallback keyCallback; + GLFWFramebufferSizeCallback fbCallback; + Closure debugProc; + + void init() throws IOException { + glfwSetErrorCallback(errCallback = new GLFWErrorCallback() { + GLFWErrorCallback delegate = Callbacks.errorCallbackPrint(System.err); + + public void invoke(int error, long description) { + if (error == GLFW_VERSION_UNAVAILABLE) + System.err.println("This demo requires OpenGL 3.0 or higher."); + delegate.invoke(error, description); + } + + @Override + public void release() { + delegate.release(); + super.release(); + } + }); + + if (glfwInit() != GL_TRUE) + throw new IllegalStateException("Unable to initialize GLFW"); + + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_VISIBLE, GL_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); + + window = glfwCreateWindow(width, height, "Uniform array test", NULL, NULL); + if (window == NULL) { + throw new AssertionError("Failed to create the GLFW window"); + } + System.out.println("Press 'up' or 'down' to cycle through some colors."); + + glfwSetFramebufferSizeCallback(window, fbCallback = new GLFWFramebufferSizeCallback() { + public void invoke(long window, int width, int height) { + if (width > 0 && height > 0 + && (ShadowMappingDemo.this.width != width || ShadowMappingDemo.this.height != height)) { + ShadowMappingDemo.this.width = width; + ShadowMappingDemo.this.height = height; + } + } + }); + + ByteBuffer vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + glfwSetWindowPos(window, (GLFWvidmode.width(vidmode) - width) / 2, (GLFWvidmode.height(vidmode) - height) / 2); + glfwMakeContextCurrent(window); + glfwSwapInterval(0); + glfwShowWindow(window); + + IntBuffer framebufferSize = BufferUtils.createIntBuffer(2); + nglfwGetFramebufferSize(window, memAddress(framebufferSize), memAddress(framebufferSize) + 4); + width = framebufferSize.get(0); + height = framebufferSize.get(1); + + ctx = GLContext.createFromCurrent(); + debugProc = ctx.setupDebugMessageCallback(System.err); + + /* Set some GL states */ + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + glClearColor(0.6f, 0.7f, 0.8f, 1.0f); + + /* Create all needed GL resources */ + createVao(); + createShadowProgram(); + initShadowProgram(); + createNormalProgram(); + initNormalProgram(); + createDepthTexture(); + createFbo(); + } + + /** + * Create the texture storing the depth values of the light-render. + */ + void createDepthTexture() { + depthTexture = glGenTextures(); + glBindTexture(GL_TEXTURE_2D, depthTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, shadowMapSize, shadowMapSize, 0, GL_DEPTH_COMPONENT, + GL_UNSIGNED_BYTE, (ByteBuffer) null); + glBindTexture(GL_TEXTURE_2D, 0); + } + + /** + * Create the FBO to render the depth values of the light-render into the + * depth texture. + */ + void createFbo() { + fbo = glGenFramebuffers(); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindTexture(GL_TEXTURE_2D, depthTexture); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0); + int fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (fboStatus != GL_FRAMEBUFFER_COMPLETE) { + throw new AssertionError("Could not create FBO: " + fboStatus); + } + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + /** + * Creates a VAO for the scene with some boxes. + */ + private void createVao() { + vao = glGenVertexArrays(); + int vbo = glGenBuffers(); + glBindVertexArray(vao); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + ByteBuffer bb = BufferUtils.createByteBuffer(boxes.length * 4 * (3 + 3) * 6 * 6); + FloatBuffer fv = bb.asFloatBuffer(); + for (int i = 0; i < boxes.length; i += 2) { + DemoUtils.triangulateBox(boxes[i], boxes[i + 1], fv); + } + glBufferData(GL_ARRAY_BUFFER, bb, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, false, 4 * (3 + 3), 0L); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, false, 4 * (3 + 3), 4 * 3); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + } + + static int createShader(String resource, int type) throws IOException { + int shader = glCreateShader(type); + + ByteBuffer source = ioResourceToByteBuffer(resource, 8192); + + PointerBuffer strings = BufferUtils.createPointerBuffer(1); + IntBuffer lengths = BufferUtils.createIntBuffer(1); + + strings.put(0, source); + lengths.put(0, source.remaining()); + + glShaderSource(shader, strings, lengths); + glCompileShader(shader); + int compiled = glGetShaderi(shader, GL_COMPILE_STATUS); + String shaderLog = glGetShaderInfoLog(shader); + if (!shaderLog.trim().isEmpty()) { + System.err.println(shaderLog); + } + if (compiled == 0) { + throw new AssertionError("Could not compile shader"); + } + return shader; + } + + void createShadowProgram() throws IOException { + shadowProgram = glCreateProgram(); + int vshader = createShader("demo/shadowMapping-vs.glsl", GL_VERTEX_SHADER); + int fshader = createShader("demo/shadowMapping-fs.glsl", GL_FRAGMENT_SHADER); + glAttachShader(shadowProgram, vshader); + glAttachShader(shadowProgram, fshader); + glBindAttribLocation(shadowProgram, 0, "position"); + glLinkProgram(shadowProgram); + int linked = glGetProgrami(shadowProgram, GL_LINK_STATUS); + String programLog = glGetProgramInfoLog(shadowProgram); + if (!programLog.trim().isEmpty()) { + System.err.println(programLog); + } + if (linked == 0) { + throw new AssertionError("Could not link program"); + } + } + + void initShadowProgram() { + glUseProgram(shadowProgram); + shadowProgramMVPUniform = glGetUniformLocation(shadowProgram, "modelViewProjectionMatrix"); + glUseProgram(0); + } + + void createNormalProgram() throws IOException { + normalProgram = glCreateProgram(); + int vshader = createShader("demo/shadowMappingShade-vs.glsl", GL_VERTEX_SHADER); + int fshader = createShader("demo/shadowMappingShade-fs.glsl", GL_FRAGMENT_SHADER); + glAttachShader(normalProgram, vshader); + glAttachShader(normalProgram, fshader); + glBindAttribLocation(normalProgram, 0, "position"); + glBindAttribLocation(normalProgram, 1, "normal"); + glLinkProgram(normalProgram); + int linked = glGetProgrami(normalProgram, GL_LINK_STATUS); + String programLog = glGetProgramInfoLog(normalProgram); + if (!programLog.trim().isEmpty()) { + System.err.println(programLog); + } + if (linked == 0) { + throw new AssertionError("Could not link program"); + } + } + + void initNormalProgram() { + glUseProgram(normalProgram); + samplerLocation = glGetUniformLocation(normalProgram, "depthTexture"); + normalProgramBiasUniform = glGetUniformLocation(normalProgram, "biasMatrix"); + normalProgramMVPUniform = glGetUniformLocation(normalProgram, "modelViewProjectionMatrix"); + normalProgramLMVPUniform = glGetUniformLocation(normalProgram, "lightModelViewProjectionMatrix"); + normalProgramLightPosition = glGetUniformLocation(normalProgram, "lightPosition"); + normalProgramLightLookAt = glGetUniformLocation(normalProgram, "lightLookAt"); + glUniform1i(samplerLocation, 0); + glUseProgram(0); + } + + /** + * Update the camera and light matrices. + */ + void update() { + lightView.setFrustumPerspective(45.0f, 1.0f, 0.1f, 30.0f); + lightView.setLookAt(lightPosition, lightLookAt, new Vector3f(0.0f, 1.0f, 0.0f)); + lightMatrix.set(lightView.getProjectionMatrix()); + lightMatrix.mul(lightView.getViewMatrix()); + cameraView.setFrustumPerspective(45.0f, (float) width / height, 0.1f, 30.0f); + cameraView.setLookAt(cameraPosition, cameraLookAt, new Vector3f(0.0f, 1.0f, 0.0f)); + cameraMatrix.set(cameraView.getProjectionMatrix()); + cameraMatrix.mul(cameraView.getViewMatrix()); + } + + /** + * Render the shadow map into a depth texture. + */ + void renderShadowMap() { + glUseProgram(shadowProgram); + + /* Set MVP matrix of the "light camera" */ + matrixUniform(shadowProgramMVPUniform, lightMatrix, false); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glViewport(0, 0, shadowMapSize, shadowMapSize); + /* Only clear depth buffer, since we don't have a color draw buffer */ + glClear(GL_DEPTH_BUFFER_BIT); + glBindVertexArray(vao); + glDrawArrays(GL_TRIANGLES, 0, 6 * 6 * boxes.length); + glBindVertexArray(0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glUseProgram(0); + } + + /** + * Render the scene normally, with sampling the previously rendered depth + * texture. + */ + void renderNormal() { + glUseProgram(normalProgram); + + /* Set MVP matrix of camera */ + matrixUniform(normalProgramMVPUniform, cameraMatrix, false); + /* Set MVP matrix that was used when doing the light-render */ + matrixUniform(normalProgramLMVPUniform, lightMatrix, false); + /* The bias-matrix used to convert to NDC coordinates */ + matrixUniform(normalProgramBiasUniform, biasMatrix, false); + /* + * Light position and lookat for normal lambertian computation + */ + glUniform3f(normalProgramLightPosition, lightPosition.x, lightPosition.y, lightPosition.z); + glUniform3f(normalProgramLightLookAt, lightLookAt.x, lightLookAt.y, lightLookAt.z); + + glViewport(0, 0, width, height); + /* Must clear both color and depth, since we are re-rendering the scene */ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glBindTexture(GL_TEXTURE_2D, depthTexture); + glBindVertexArray(vao); + glDrawArrays(GL_TRIANGLES, 0, 6 * 6 * boxes.length); + glBindVertexArray(0); + glBindTexture(GL_TEXTURE_2D, 0); + + glUseProgram(0); + } + + private void matrixUniform(int location, Matrix4f value, boolean transpose) { + matrixByteBufferFloatView.put(value.m00).put(value.m10).put(value.m20).put(value.m30).put(value.m01) + .put(value.m11).put(value.m21).put(value.m31).put(value.m02).put(value.m12).put(value.m22) + .put(value.m32).put(value.m03).put(value.m13).put(value.m23).put(value.m33); + matrixByteBufferFloatView.rewind(); + glUniformMatrix4fv(location, 1, transpose, matrixByteBuffer); + } + + void loop() { + while (glfwWindowShouldClose(window) == GL_FALSE) { + glfwPollEvents(); + + update(); + renderShadowMap(); + renderNormal(); + + glfwSwapBuffers(window); + } + } + + void run() { + try { + init(); + loop(); + + if (debugProc != null) + debugProc.release(); + + errCallback.release(); + fbCallback.release(); + glfwDestroyWindow(window); + } catch (Throwable t) { + t.printStackTrace(); + } finally { + glfwTerminate(); + } + } + + public static void main(String[] args) { + new ShadowMappingDemo().run(); + } + +} \ No newline at end of file diff --git a/src/tests/org/lwjgl/demo/opengl/raytracing/Scene.java b/src/tests/org/lwjgl/demo/opengl/raytracing/Scene.java index 4189271092..6b6ada9845 100644 --- a/src/tests/org/lwjgl/demo/opengl/raytracing/Scene.java +++ b/src/tests/org/lwjgl/demo/opengl/raytracing/Scene.java @@ -17,4 +17,10 @@ public interface Scene { new Vector3f(-1.5f, 1.0f, 2.5f), new Vector3f(1.5f, 0.0f, 1.5f), new Vector3f(2.5f, 1.0f, 2.5f), new Vector3f(1.5f, 0.0f, -2.5f), new Vector3f(2.5f, 1.0f, -1.5f) }; + Vector3f[] boxes2 = { new Vector3f(-5.0f, -0.1f, -5.0f), new Vector3f(5.0f, 0.0f, 5.0f), + new Vector3f(-0.5f, 0.0f, -0.5f), new Vector3f(0.5f, 1.0f, 0.5f), new Vector3f(-2.5f, 0.0f, -1.5f), + new Vector3f(-1.5f, 1.0f, -0.5f), new Vector3f(-2.5f, 0.0f, 1.5f), new Vector3f(-1.5f, 1.0f, 2.5f), + new Vector3f(1.5f, 0.0f, 1.5f), new Vector3f(2.5f, 1.0f, 2.5f), new Vector3f(1.5f, 0.0f, -2.5f), + new Vector3f(2.5f, 1.0f, -1.5f) }; + } diff --git a/src/tests/org/lwjgl/demo/util/Matrix4f.java b/src/tests/org/lwjgl/demo/util/Matrix4f.java index a17258ff71..e8201e5eea 100644 --- a/src/tests/org/lwjgl/demo/util/Matrix4f.java +++ b/src/tests/org/lwjgl/demo/util/Matrix4f.java @@ -25,6 +25,28 @@ public class Matrix4f { public Matrix4f() { super(); + setIdentity(); + } + + public Matrix4f(float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13, float m20, + float m21, float m22, float m23, float m30, float m31, float m32, float m33) { + super(); + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m03 = m03; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m13 = m13; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + this.m23 = m23; + this.m30 = m30; + this.m31 = m31; + this.m32 = m32; + this.m33 = m33; } public Matrix4f(Matrix4f mat) { @@ -46,6 +68,13 @@ public Matrix4f(Matrix4f mat) { this.m33 = mat.m33; } + public void setIdentity() { + this.m00 = 1; + this.m11 = 1; + this.m22 = 1; + this.m33 = 1; + } + public void set(Matrix4f m1) { m00 = m1.m00; m01 = m1.m01;