-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wrapping ShadowMap and WorldReflection into a "Node" #2325
Changes from all commits
972238e
f3ccd65
0b050bd
a2ddfd3
d3d6ca0
afda34c
a47d551
d12878e
a32583d
a01d90e
2b76cc3
122fb58
98687b4
3971d29
aebc4c5
fba3ccf
a1e0099
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* | ||
* Copyright 2016 MovingBlocks | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.terasology.rendering.dag; | ||
|
||
|
||
public interface Node { | ||
|
||
void initialise(); | ||
|
||
void process(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
/* | ||
* Copyright 2016 MovingBlocks | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.terasology.rendering.dag; | ||
|
||
import org.lwjgl.opengl.GL11; | ||
import org.terasology.config.Config; | ||
import org.terasology.config.RenderingConfig; | ||
import org.terasology.math.TeraMath; | ||
import org.terasology.math.geom.Vector3f; | ||
import org.terasology.monitoring.PerformanceMonitor; | ||
import org.terasology.registry.In; | ||
import org.terasology.rendering.assets.material.Material; | ||
import org.terasology.rendering.backdrop.BackdropProvider; | ||
import org.terasology.rendering.cameras.Camera; | ||
import org.terasology.rendering.cameras.OrthographicCamera; | ||
import org.terasology.rendering.opengl.FBO; | ||
import org.terasology.rendering.opengl.FrameBuffersManager; | ||
import org.terasology.rendering.primitives.ChunkMesh; | ||
import org.terasology.rendering.world.RenderQueuesHelper; | ||
import org.terasology.rendering.world.RenderableWorld; | ||
import org.terasology.rendering.world.WorldRenderer; | ||
import org.terasology.rendering.world.WorldRendererImpl; | ||
import static org.lwjgl.opengl.GL11.*; | ||
import static org.terasology.rendering.opengl.OpenGLUtils.*; | ||
|
||
|
||
/** | ||
* Diagram of this node can be viewed from: | ||
* TODO: move diagram to the wiki when this part of the code is stable | ||
* - https://docs.google.com/drawings/d/13I0GM9jDFlZv1vNrUPlQuBbaF86RPRNpVfn5q8Wj2lc/edit?usp=sharing | ||
*/ | ||
public class ShadowMapNode implements Node { | ||
private static final int SHADOW_FRUSTUM_BOUNDS = 500; | ||
public Camera shadowMapCamera = new OrthographicCamera(-SHADOW_FRUSTUM_BOUNDS, SHADOW_FRUSTUM_BOUNDS, SHADOW_FRUSTUM_BOUNDS, -SHADOW_FRUSTUM_BOUNDS); | ||
|
||
@In | ||
private RenderableWorld renderableWorld; | ||
|
||
@In | ||
private RenderQueuesHelper renderQueues; | ||
|
||
@In | ||
private Config config; | ||
|
||
@In | ||
private FrameBuffersManager frameBuffersManager; | ||
|
||
@In | ||
private WorldRenderer worldRenderer; | ||
|
||
@In | ||
private BackdropProvider backdropProvider; | ||
|
||
private Material shadowMapShader; | ||
private FBO shadowMap; | ||
private RenderingConfig renderingConfig; | ||
private Camera playerCamera; | ||
|
||
|
||
@Override | ||
public void initialise() { | ||
this.playerCamera = worldRenderer.getActiveCamera(); | ||
this.shadowMap = frameBuffersManager.getFBO("sceneShadowMap"); | ||
this.shadowMapShader = worldRenderer.getMaterial("engine:prog.shadowMap"); | ||
this.renderingConfig = config.getRendering(); | ||
renderableWorld.setShadowMapCamera(shadowMapCamera); | ||
} | ||
|
||
@Override | ||
public void process() { | ||
this.shadowMap = frameBuffersManager.getFBO("sceneShadowMap"); | ||
positionShadowMapCamera(); | ||
|
||
// TODO: find an elegant way to fetch isFirstRenderingStageForCurrentFrame | ||
boolean processCondition = renderingConfig.isDynamicShadows() && worldRenderer.isFirstRenderingStageForCurrentFrame(); | ||
if (!processCondition) { | ||
return; | ||
} | ||
|
||
PerformanceMonitor.startActivity("Render World (Shadow Map)"); | ||
// preRenderSetupSceneShadowMap | ||
shadowMap.bind(); | ||
setViewportToSizeOf(shadowMap); | ||
// TODO: verify the need to clear color buffer | ||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | ||
GL11.glDisable(GL_CULL_FACE); | ||
|
||
// render | ||
shadowMapCamera.lookThrough(); | ||
|
||
shadowMapShader.enable(); | ||
// FIXME: storing chuncksOpaqueShadow or a mechanism for requesting a chunk queue for nodes which calls renderChunks method? | ||
worldRenderer.renderChunks(renderQueues.chunksOpaqueShadow, ChunkMesh.RenderPhase.OPAQUE, shadowMapCamera, WorldRendererImpl.ChunkRenderMode.SHADOW_MAP); | ||
playerCamera.lookThrough(); //FIXME: not strictly needed: just defensive programming here. | ||
|
||
// postRenderCleanupSceneShadowMap | ||
GL11.glEnable(GL_CULL_FACE); | ||
bindDisplay(); | ||
|
||
PerformanceMonitor.endActivity(); | ||
} | ||
|
||
private void positionShadowMapCamera() { | ||
// Shadows are rendered around the player so... | ||
Vector3f lightPosition = new Vector3f(playerCamera.getPosition().x, 0.0f, playerCamera.getPosition().z); | ||
|
||
// Project the shadowMapCamera position to light space and make sure it is only moved in texel steps (avoids flickering when moving the shadowMapCamera) | ||
float texelSize = 1.0f / renderingConfig.getShadowMapResolution(); | ||
texelSize *= 2.0f; | ||
|
||
shadowMapCamera.getViewProjectionMatrix().transformPoint(lightPosition); | ||
lightPosition.set(TeraMath.fastFloor(lightPosition.x / texelSize) * texelSize, 0.0f, TeraMath.fastFloor(lightPosition.z / texelSize) * texelSize); | ||
shadowMapCamera.getInverseViewProjectionMatrix().transformPoint(lightPosition); | ||
|
||
// ... we position our new shadowMapCamera at the position of the player and move it | ||
// quite a bit into the direction of the sun (our main light). | ||
|
||
// Make sure the sun does not move too often since it causes massive shadow flickering (from hell to the max)! | ||
float stepSize = 50f; | ||
Vector3f sunDirection = backdropProvider.getQuantizedSunDirection(stepSize); | ||
|
||
Vector3f sunPosition = new Vector3f(sunDirection); | ||
sunPosition.scale(256.0f + 64.0f); | ||
lightPosition.add(sunPosition); | ||
shadowMapCamera.getPosition().set(lightPosition); | ||
|
||
// and adjust it to look from the sun direction into the direction of our player | ||
Vector3f negSunDirection = new Vector3f(sunDirection); | ||
negSunDirection.scale(-1.0f); | ||
|
||
shadowMapCamera.getViewingDirection().set(negSunDirection); | ||
shadowMapCamera.update(worldRenderer.getSecondsSinceLastFrame()); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* | ||
* Copyright 2016 MovingBlocks | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.terasology.rendering.dag; | ||
|
||
import org.lwjgl.opengl.GL11; | ||
import org.terasology.config.Config; | ||
import org.terasology.config.RenderingConfig; | ||
import org.terasology.monitoring.PerformanceMonitor; | ||
import org.terasology.registry.In; | ||
import org.terasology.rendering.assets.material.Material; | ||
import org.terasology.rendering.assets.shader.ShaderProgramFeature; | ||
import org.terasology.rendering.backdrop.BackdropRenderer; | ||
import org.terasology.rendering.cameras.Camera; | ||
import org.terasology.rendering.opengl.FBO; | ||
import org.terasology.rendering.opengl.FrameBuffersManager; | ||
import org.terasology.rendering.primitives.ChunkMesh; | ||
import org.terasology.rendering.world.RenderQueuesHelper; | ||
import org.terasology.rendering.world.WorldRenderer; | ||
import org.terasology.rendering.world.WorldRendererImpl; | ||
|
||
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; | ||
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; | ||
import static org.lwjgl.opengl.GL11.glClear; | ||
import static org.terasology.rendering.opengl.OpenGLUtils.*; | ||
|
||
/** | ||
* Diagram of this node can be viewed from: | ||
* TODO: move diagram to the wiki when this part of the code is stable | ||
* - https://docs.google.com/drawings/d/1Iz7MA8Y5q7yjxxcgZW-0antv5kgx6NYkvoInielbwGU/edit?usp=sharing | ||
*/ | ||
public class WorldReflectionNode implements Node { | ||
|
||
@In | ||
private FrameBuffersManager frameBuffersManager; | ||
|
||
@In | ||
private RenderQueuesHelper renderQueues; | ||
|
||
@In | ||
private Config config; | ||
|
||
@In | ||
private WorldRenderer worldRenderer; | ||
|
||
@In | ||
private BackdropRenderer backdropRenderer; | ||
|
||
private Camera playerCamera; | ||
private Material chunkShader; | ||
private RenderingConfig renderingConfig; | ||
|
||
@Override | ||
public void initialise() { | ||
this.renderingConfig = config.getRendering(); | ||
this.chunkShader = worldRenderer.getMaterial("engine:prog.chunk"); | ||
this.playerCamera = worldRenderer.getActiveCamera(); | ||
} | ||
|
||
@Override | ||
public void process() { | ||
PerformanceMonitor.startActivity("Render World (Reflection)"); | ||
FBO sceneReflected = frameBuffersManager.getFBO("sceneReflected"); | ||
FBO sceneOpaque = frameBuffersManager.getFBO("sceneOpaque"); | ||
|
||
sceneReflected.bind(); | ||
setViewportToSizeOf(sceneReflected); | ||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | ||
GL11.glCullFace(GL11.GL_FRONT); | ||
playerCamera.setReflected(true); | ||
|
||
playerCamera.lookThroughNormalized(); // we don't want the reflected scene to be bobbing or moving with the player | ||
// TODO: convert backdropRenderer into a BackdropNode. | ||
backdropRenderer.render(playerCamera); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's add:
Note for @tdgunes: this will be an interesting use case for GL state management and order of execution of nodes: both the the BackdropNode and the WorldReflectionNode need similar/identical states, so the nodes should be processed one after another and the first node to be processed should not be allowed to reset to default the states the second node needs. Also interesting, the BackdropNode will need to be used twice, one here, to render the reflection, and then later, for the proper renderer. Something to put in the back of our minds. |
||
playerCamera.lookThrough(); | ||
|
||
if (renderingConfig.isReflectiveWater()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmmm.... let's put a TODO here:
|
||
// TODO: the isReflectiveWater() block should include content of the whole process() method. | ||
// TODO: Eventually the check will be removed, when node insertion will signify that the feature is enabled. | ||
chunkShader.activateFeature(ShaderProgramFeature.FEATURE_USE_FORWARD_LIGHTING); | ||
worldRenderer.renderChunks(renderQueues.chunksOpaqueReflection, ChunkMesh.RenderPhase.OPAQUE, playerCamera, WorldRendererImpl.ChunkRenderMode.REFLECTION); | ||
chunkShader.deactivateFeature(ShaderProgramFeature.FEATURE_USE_FORWARD_LIGHTING); | ||
} | ||
|
||
playerCamera.setReflected(false); | ||
|
||
GL11.glCullFace(GL11.GL_BACK); | ||
bindDisplay(); | ||
setViewportToSizeOf(sceneOpaque); | ||
|
||
PerformanceMonitor.endActivity(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My only suggestion here would be that you can get the playerCamera via WorldRenderer.getActiveCamera().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would use the InjectionHelper here. This enables the
@In
annotation for the fields that are known to the context.