/* * 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.nodes; import org.terasology.engine.ComponentSystemManager; import org.terasology.entitySystem.systems.RenderSystem; import org.terasology.monitoring.PerformanceMonitor; import org.terasology.registry.In; import org.terasology.rendering.cameras.Camera; import org.terasology.rendering.dag.AbstractNode; import org.terasology.rendering.dag.stateChanges.BindFBO; import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA; import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA; import org.terasology.rendering.dag.stateChanges.DisableDepthWriting; import org.terasology.rendering.dag.stateChanges.EnableBlending; import static org.terasology.rendering.opengl.DefaultDynamicFBOs.READ_ONLY_GBUFFER; import org.terasology.rendering.dag.stateChanges.LookThrough; import org.terasology.rendering.dag.stateChanges.SetBlendFunction; import org.terasology.rendering.dag.stateChanges.SetViewportToSizeOf; import org.terasology.rendering.opengl.fbms.DisplayResolutionDependentFBOs; import org.terasology.rendering.world.WorldRenderer; /** * An instance of this class renders and blends semi-transparent objects into the content of the existing g-buffer. * * Notice that this is handled in the process() method by calling the renderAlphaBlend() method of registered * instances implementing the RenderSystem interface. * * Theoretically the same results could be achieved by rendering all meshes in one go, keeping blending * always enabled and relying on the alpha channel of the textures associated with a given mesh. In practice * blending is an expensive operation and it wouldn't be good performance-wise to keep it always enabled. * * Also, a number of previous nodes rely on unambiguous meaning for the depth values in the gbuffers, * but this node temporarily disable writing to the depth buffer - what value should be written to it, * the distance to the semi-transparent surface or what's already stored in the depth buffer? As such * semi-transparent objects are handled here, after nodes relying on the depth buffer have done their job. */ public class SimpleBlendMaterialsNode extends AbstractNode { @In private WorldRenderer worldRenderer; @In private ComponentSystemManager componentSystemManager; @In private DisplayResolutionDependentFBOs displayResolutionDependentFBOs; /** * This method must be called once shortly after instantiation to fully initialize the node * and make it ready for rendering. */ @Override public void initialise() { Camera playerCamera = worldRenderer.getActiveCamera(); addDesiredStateChange(new LookThrough(playerCamera)); addDesiredStateChange(new BindFBO(READ_ONLY_GBUFFER.getName(), displayResolutionDependentFBOs)); addDesiredStateChange(new SetViewportToSizeOf(READ_ONLY_GBUFFER.getName(), displayResolutionDependentFBOs)); // Sets the state for the rendering of objects or portions of objects having some degree of transparency. // Generally speaking objects drawn with this state will have their color blended with the background // color, depending on their opacity. I.e. a 25% opaque foreground object will provide 25% of its // color while the background will provide the remaining 75%. The sum of the two RGBA vectors gets // written onto the output buffer. addDesiredStateChange(new EnableBlending()); // (*) In this context SRC is Foreground. This effectively says: // Resulting RGB = ForegroundRGB * ForegroundAlpha + BackgroundRGB * (1 - ForegroundAlpha) // Which might still look complicated, but it's actually the most typical alpha-driven composite. // A neat tool to play with this settings can be found here: http://www.andersriggelsen.dk/glblendfunc.php addDesiredStateChange(new SetBlendFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); // Important note: the following disables writing to the Depth Buffer. This is why filters relying on // depth information (i.e. DoF) have problems with transparent objects: the depth of their pixels is // found to be that of the background rather than that of the transparent's object surface. // This is an unresolved (unresolv-able?) issue that would only be reversed, not eliminated, // by re-enabling writing to the Depth Buffer. addDesiredStateChange(new DisableDepthWriting()); } /** * Iterates over registered RenderSystem instances and call their renderAlphaBlend() method. * * This leaves great freedom to RenderSystem implementations, but also the responsibility to * leave the OpenGL state in the way they found it - otherwise the next system or the next * render node might not be able to function properly. */ @Override public void process() { PerformanceMonitor.startActivity("rendering/simpleBlendMaterials"); for (RenderSystem renderer : componentSystemManager.iterateRenderSubscribers()) { renderer.renderAlphaBlend(); } PerformanceMonitor.endActivity(); } }