ShadowMapMasterRenderer.java 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. package eu.tankernn.gameEngine.renderEngine.shadows;
  2. import java.util.List;
  3. import java.util.Map;
  4. import org.lwjgl.opengl.GL11;
  5. import org.lwjgl.util.vector.Matrix4f;
  6. import org.lwjgl.util.vector.Vector2f;
  7. import org.lwjgl.util.vector.Vector3f;
  8. import eu.tankernn.gameEngine.entities.Camera;
  9. import eu.tankernn.gameEngine.entities.Entity;
  10. import eu.tankernn.gameEngine.entities.Light;
  11. import eu.tankernn.gameEngine.loader.models.TexturedModel;
  12. import eu.tankernn.gameEngine.loader.textures.Texture;
  13. /**
  14. * This class is in charge of using all of the classes in the shadows package to
  15. * carry out the shadow render pass, i.e. rendering the scene to the shadow map
  16. * texture. This is the only class in the shadows package which needs to be
  17. * referenced from outside the shadows package.
  18. *
  19. * @author Karl
  20. *
  21. */
  22. public class ShadowMapMasterRenderer {
  23. public static final int SHADOW_MAP_SIZE = 4096;
  24. private ShadowFrameBuffer shadowFbo;
  25. private ShadowShader shader;
  26. private ShadowBox shadowBox;
  27. private Matrix4f projectionMatrix = new Matrix4f();
  28. private Matrix4f lightViewMatrix = new Matrix4f();
  29. private Matrix4f projectionViewMatrix = new Matrix4f();
  30. private Matrix4f offset = createOffset();
  31. private ShadowMapEntityRenderer entityRenderer;
  32. /**
  33. * Creates instances of the important objects needed for rendering the scene
  34. * to the shadow map. This includes the {@link ShadowBox} which calculates
  35. * the position and size of the "view cuboid", the simple renderer and
  36. * shader program that are used to render objects to the shadow map, and the
  37. * {@link ShadowFrameBuffer} to which the scene is rendered. The size of the
  38. * shadow map is determined here.
  39. *
  40. * @param camera
  41. * - the camera being used in the scene.
  42. */
  43. public ShadowMapMasterRenderer(Camera camera) {
  44. shader = new ShadowShader();
  45. shadowBox = new ShadowBox(lightViewMatrix, camera);
  46. shadowFbo = new ShadowFrameBuffer(SHADOW_MAP_SIZE, SHADOW_MAP_SIZE);
  47. entityRenderer = new ShadowMapEntityRenderer(shader, projectionViewMatrix);
  48. }
  49. /**
  50. * Carries out the shadow render pass. This renders the entities to the
  51. * shadow map. First the shadow box is updated to calculate the size and
  52. * position of the "view cuboid". The light direction is assumed to be
  53. * "-lightPosition" which will be fairly accurate assuming that the light is
  54. * very far from the scene. It then prepares to render, renders the entities
  55. * to the shadow map, and finishes rendering.
  56. *
  57. * @param entities
  58. * - the lists of entities to be rendered. Each list is
  59. * associated with the {@link TexturedModel} that all of the
  60. * entities in that list use.
  61. * @param sun
  62. * - the light acting as the sun in the scene.
  63. */
  64. public void render(Map<TexturedModel, List<Entity>> entities, Light sun) {
  65. shadowBox.update();
  66. Vector3f sunPosition = sun.getPosition();
  67. Vector3f lightDirection = new Vector3f(-sunPosition.x, -sunPosition.y, -sunPosition.z);
  68. prepare(lightDirection, shadowBox);
  69. entityRenderer.render(entities);
  70. finish();
  71. }
  72. /**
  73. * This biased projection-view matrix is used to convert fragments into
  74. * "shadow map space" when rendering the main render pass. It converts a
  75. * world space position into a 2D coordinate on the shadow map. This is
  76. * needed for the second part of shadow mapping.
  77. *
  78. * @return The to-shadow-map-space matrix.
  79. */
  80. public Matrix4f getToShadowMapSpaceMatrix() {
  81. return Matrix4f.mul(offset, projectionViewMatrix, null);
  82. }
  83. /**
  84. * Clean up the shader and FBO on closing.
  85. */
  86. public void cleanUp() {
  87. shader.cleanUp();
  88. shadowFbo.cleanUp();
  89. }
  90. /**
  91. * @return The ID of the shadow map texture. The ID will always stay the
  92. * same, even when the contents of the shadow map texture change
  93. * each frame.
  94. */
  95. public Texture getShadowMap() {
  96. return shadowFbo.getShadowMap();
  97. }
  98. /**
  99. * @return The light's "view" matrix.
  100. */
  101. protected Matrix4f getLightSpaceTransform() {
  102. return lightViewMatrix;
  103. }
  104. /**
  105. * Prepare for the shadow render pass. This first updates the dimensions of
  106. * the orthographic "view cuboid" based on the information that was
  107. * calculated in the {@link SHadowBox} class. The light's "view" matrix is
  108. * also calculated based on the light's direction and the center position of
  109. * the "view cuboid" which was also calculated in the {@link ShadowBox}
  110. * class. These two matrices are multiplied together to create the
  111. * projection-view matrix. This matrix determines the size, position, and
  112. * orientation of the "view cuboid" in the world. This method also binds the
  113. * shadows FBO so that everything rendered after this gets rendered to the
  114. * FBO. It also enables depth testing, and clears any data that is in the
  115. * FBOs depth attachment from last frame. The simple shader program is also
  116. * started.
  117. *
  118. * @param lightDirection
  119. * - the direction of the light rays coming from the sun.
  120. * @param box
  121. * - the shadow box, which contains all the info about the
  122. * "view cuboid".
  123. */
  124. private void prepare(Vector3f lightDirection, ShadowBox box) {
  125. updateOrthoProjectionMatrix(box.getWidth(), box.getHeight(), box.getLength());
  126. updateLightViewMatrix(lightDirection, box.getCenter());
  127. Matrix4f.mul(projectionMatrix, lightViewMatrix, projectionViewMatrix);
  128. shadowFbo.bindFrameBuffer();
  129. GL11.glEnable(GL11.GL_DEPTH_TEST);
  130. GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
  131. shader.start();
  132. }
  133. /**
  134. * Finish the shadow render pass. Stops the shader and unbinds the shadow
  135. * FBO, so everything rendered after this point is rendered to the screen,
  136. * rather than to the shadow FBO.
  137. */
  138. private void finish() {
  139. shader.stop();
  140. shadowFbo.unbindFrameBuffer();
  141. }
  142. /**
  143. * Updates the "view" matrix of the light. This creates a view matrix which
  144. * will line up the direction of the "view cuboid" with the direction of the
  145. * light. The light itself has no position, so the "view" matrix is centered
  146. * at the center of the "view cuboid". The created view matrix determines
  147. * where and how the "view cuboid" is positioned in the world. The size of
  148. * the view cuboid, however, is determined by the projection matrix.
  149. *
  150. * @param direction
  151. * - the light direction, and therefore the direction that the
  152. * "view cuboid" should be pointing.
  153. * @param center
  154. * - the center of the "view cuboid" in world space.
  155. */
  156. private void updateLightViewMatrix(Vector3f direction, Vector3f center) {
  157. direction.normalise();
  158. center.negate();
  159. lightViewMatrix.setIdentity();
  160. float pitch = (float) Math.acos(new Vector2f(direction.x, direction.z).length());
  161. Matrix4f.rotate(pitch, new Vector3f(1, 0, 0), lightViewMatrix, lightViewMatrix);
  162. float yaw = (float) Math.toDegrees(((float) Math.atan(direction.x / direction.z)));
  163. yaw = direction.z > 0 ? yaw - 180 : yaw;
  164. Matrix4f.rotate((float) -Math.toRadians(yaw), new Vector3f(0, 1, 0), lightViewMatrix,
  165. lightViewMatrix);
  166. Matrix4f.translate(center, lightViewMatrix, lightViewMatrix);
  167. }
  168. /**
  169. * Creates the orthographic projection matrix. This projection matrix
  170. * basically sets the width, length and height of the "view cuboid", based
  171. * on the values that were calculated in the {@link ShadowBox} class.
  172. *
  173. * @param width
  174. * - shadow box width.
  175. * @param height
  176. * - shadow box height.
  177. * @param length
  178. * - shadow box length.
  179. */
  180. private void updateOrthoProjectionMatrix(float width, float height, float length) {
  181. projectionMatrix.setIdentity();
  182. projectionMatrix.m00 = 2f / width;
  183. projectionMatrix.m11 = 2f / height;
  184. projectionMatrix.m22 = -2f / length;
  185. projectionMatrix.m33 = 1;
  186. }
  187. /**
  188. * Create the offset for part of the conversion to shadow map space. This
  189. * conversion is necessary to convert from one coordinate system to the
  190. * coordinate system that we can use to sample to shadow map.
  191. *
  192. * @return The offset as a matrix (so that it's easy to apply to other matrices).
  193. */
  194. private static Matrix4f createOffset() {
  195. Matrix4f offset = new Matrix4f();
  196. offset.translate(new Vector3f(0.5f, 0.5f, 0.5f));
  197. offset.scale(new Vector3f(0.5f, 0.5f, 0.5f));
  198. return offset;
  199. }
  200. }