/* * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'jMonkeyEngine' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.jme3.post; import com.jme3.asset.AssetManager; import com.jme3.material.Material; import com.jme3.math.Vector2f; import com.jme3.profile.AppProfiler; import com.jme3.renderer.*; import com.jme3.renderer.queue.RenderQueue; import com.jme3.texture.FrameBuffer; import com.jme3.texture.Image; import com.jme3.texture.Image.Format; import com.jme3.texture.Texture; import com.jme3.texture.Texture.MagFilter; import com.jme3.texture.Texture.MinFilter; import com.jme3.texture.Texture2D; import com.jme3.ui.Picture; import java.util.Collection; import java.util.logging.Logger; /** * @deprecated use the ToneMappingFilter. */ @Deprecated public class HDRRenderer implements SceneProcessor { private static final int LUMMODE_NONE = 0x1, LUMMODE_ENCODE_LUM = 0x2, LUMMODE_DECODE_LUM = 0x3; private Renderer renderer; private RenderManager renderManager; private ViewPort viewPort; private static final Logger logger = Logger.getLogger(HDRRenderer.class.getName()); private AppProfiler prof; private Camera fbCam = new Camera(1, 1); private FrameBuffer msFB; private FrameBuffer mainSceneFB; private Texture2D mainScene; private FrameBuffer scene64FB; private Texture2D scene64; private FrameBuffer scene8FB; private Texture2D scene8; private FrameBuffer scene1FB[] = new FrameBuffer[2]; private Texture2D scene1[] = new Texture2D[2]; private Material hdr64; private Material hdr8; private Material hdr1; private Material tone; private Picture fsQuad; private float time = 0; private int curSrc = -1; private int oppSrc = -1; private float blendFactor = 0; private int numSamples = 0; private float exposure = 0.18f; private float whiteLevel = 100f; private float throttle = -1; private int maxIterations = -1; private Image.Format bufFormat = Format.RGB16F; private MinFilter fbMinFilter = MinFilter.BilinearNoMipMaps; private MagFilter fbMagFilter = MagFilter.Bilinear; private AssetManager manager; private boolean enabled = true; public HDRRenderer(AssetManager manager, Renderer renderer){ this.manager = manager; this.renderer = renderer; Collection<Caps> caps = renderer.getCaps(); if (caps.contains(Caps.PackedFloatColorBuffer)) bufFormat = Format.RGB111110F; else if (caps.contains(Caps.FloatColorBuffer)) bufFormat = Format.RGB16F; else{ enabled = false; return; } } public boolean isEnabled() { return enabled; } public void setSamples(int samples){ this.numSamples = samples; } public void setExposure(float exp){ this.exposure = exp; } public void setWhiteLevel(float whiteLevel){ this.whiteLevel = whiteLevel; } public void setMaxIterations(int maxIterations){ this.maxIterations = maxIterations; // regenerate shaders if needed if (hdr64 != null) createLumShaders(); } public void setThrottle(float throttle){ this.throttle = throttle; } public void setUseFastFilter(boolean fastFilter){ if (fastFilter){ fbMagFilter = MagFilter.Nearest; fbMinFilter = MinFilter.NearestNoMipMaps; }else{ fbMagFilter = MagFilter.Bilinear; fbMinFilter = MinFilter.BilinearNoMipMaps; } } public Picture createDisplayQuad(/*int mode, Texture tex*/){ if (scene64 == null) return null; Material mat = new Material(manager, "Common/MatDefs/Hdr/LogLum.j3md"); // if (mode == LUMMODE_ENCODE_LUM) // mat.setBoolean("EncodeLum", true); // else if (mode == LUMMODE_DECODE_LUM) mat.setBoolean("DecodeLum", true); mat.setTexture("Texture", scene64); // mat.setTexture("Texture", tex); Picture dispQuad = new Picture("Luminance Display"); dispQuad.setMaterial(mat); return dispQuad; } private Material createLumShader(int srcW, int srcH, int bufW, int bufH, int mode, int iters, Texture tex){ Material mat = new Material(manager, "Common/MatDefs/Hdr/LogLum.j3md"); Vector2f blockSize = new Vector2f(1f / bufW, 1f / bufH); Vector2f pixelSize = new Vector2f(1f / srcW, 1f / srcH); Vector2f blocks = new Vector2f(); float numPixels = Float.POSITIVE_INFINITY; if (iters != -1){ do { pixelSize.multLocal(2); blocks.set(blockSize.x / pixelSize.x, blockSize.y / pixelSize.y); numPixels = blocks.x * blocks.y; } while (numPixels > iters); }else{ blocks.set(blockSize.x / pixelSize.x, blockSize.y / pixelSize.y); numPixels = blocks.x * blocks.y; } mat.setBoolean("Blocks", true); if (mode == LUMMODE_ENCODE_LUM) mat.setBoolean("EncodeLum", true); else if (mode == LUMMODE_DECODE_LUM) mat.setBoolean("DecodeLum", true); mat.setTexture("Texture", tex); mat.setVector2("BlockSize", blockSize); mat.setVector2("PixelSize", pixelSize); mat.setFloat("NumPixels", numPixels); return mat; } private void createLumShaders(){ int w = mainSceneFB.getWidth(); int h = mainSceneFB.getHeight(); hdr64 = createLumShader(w, h, 64, 64, LUMMODE_ENCODE_LUM, maxIterations, mainScene); hdr8 = createLumShader(64, 64, 8, 8, LUMMODE_NONE, maxIterations, scene64); hdr1 = createLumShader(8, 8, 1, 1, LUMMODE_NONE, maxIterations, scene8); } private int opposite(int i){ return i == 1 ? 0 : 1; } private void renderProcessing(Renderer r, FrameBuffer dst, Material mat){ if (dst == null){ fsQuad.setWidth(mainSceneFB.getWidth()); fsQuad.setHeight(mainSceneFB.getHeight()); fbCam.resize(mainSceneFB.getWidth(), mainSceneFB.getHeight(), true); }else{ fsQuad.setWidth(dst.getWidth()); fsQuad.setHeight(dst.getHeight()); fbCam.resize(dst.getWidth(), dst.getHeight(), true); } fsQuad.setMaterial(mat); fsQuad.updateGeometricState(); renderManager.setCamera(fbCam, true); r.setFrameBuffer(dst); r.clearBuffers(true, true, true); renderManager.renderGeometry(fsQuad); } private void renderToneMap(Renderer r, FrameBuffer out){ tone.setFloat("A", exposure); tone.setFloat("White", whiteLevel); tone.setTexture("Lum", scene1[oppSrc]); tone.setTexture("Lum2", scene1[curSrc]); tone.setFloat("BlendFactor", blendFactor); renderProcessing(r, out, tone); } private void updateAverageLuminance(Renderer r){ renderProcessing(r, scene64FB, hdr64); renderProcessing(r, scene8FB, hdr8); renderProcessing(r, scene1FB[curSrc], hdr1); } public boolean isInitialized(){ return viewPort != null; } public void reshape(ViewPort vp, int w, int h){ if (mainSceneFB != null){ renderer.deleteFrameBuffer(mainSceneFB); } mainSceneFB = new FrameBuffer(w, h, 1); mainScene = new Texture2D(w, h, bufFormat); mainSceneFB.setDepthBuffer(Format.Depth); mainSceneFB.setColorTexture(mainScene); mainScene.setMagFilter(fbMagFilter); mainScene.setMinFilter(fbMinFilter); if (msFB != null){ renderer.deleteFrameBuffer(msFB); } tone.setTexture("Texture", mainScene); Collection<Caps> caps = renderer.getCaps(); if (numSamples > 1 && caps.contains(Caps.FrameBufferMultisample)){ msFB = new FrameBuffer(w, h, numSamples); msFB.setDepthBuffer(Format.Depth); msFB.setColorBuffer(bufFormat); vp.setOutputFrameBuffer(msFB); }else{ if (numSamples > 1) logger.warning("FBO multisampling not supported on this GPU, request ignored."); vp.setOutputFrameBuffer(mainSceneFB); } createLumShaders(); } public void initialize(RenderManager rm, ViewPort vp){ if (!enabled) return; renderer = rm.getRenderer(); renderManager = rm; viewPort = vp; // loadInitial() fsQuad = new Picture("HDR Fullscreen Quad"); Format lumFmt = Format.RGB8; scene64FB = new FrameBuffer(64, 64, 1); scene64 = new Texture2D(64, 64, lumFmt); scene64FB.setColorTexture(scene64); scene64.setMagFilter(fbMagFilter); scene64.setMinFilter(fbMinFilter); scene8FB = new FrameBuffer(8, 8, 1); scene8 = new Texture2D(8, 8, lumFmt); scene8FB.setColorTexture(scene8); scene8.setMagFilter(fbMagFilter); scene8.setMinFilter(fbMinFilter); scene1FB[0] = new FrameBuffer(1, 1, 1); scene1[0] = new Texture2D(1, 1, lumFmt); scene1FB[0].setColorTexture(scene1[0]); scene1FB[1] = new FrameBuffer(1, 1, 1); scene1[1] = new Texture2D(1, 1, lumFmt); scene1FB[1].setColorTexture(scene1[1]); // prepare tonemap shader tone = new Material(manager, "Common/MatDefs/Hdr/ToneMap.j3md"); tone.setFloat("A", 0.18f); tone.setFloat("White", 100); // load(); int w = vp.getCamera().getWidth(); int h = vp.getCamera().getHeight(); reshape(vp, w, h); } public void preFrame(float tpf) { if (!enabled) return; time += tpf; blendFactor = (time / throttle); } public void postQueue(RenderQueue rq) { } public void postFrame(FrameBuffer out) { if (!enabled) return; if (msFB != null){ // first render to multisampled FB // renderer.setFrameBuffer(msFB); // renderer.clearBuffers(true,true,true); // // renderManager.renderViewPortRaw(viewPort); // render back to non-multisampled FB renderer.copyFrameBuffer(msFB, mainSceneFB, true); }else{ // renderer.setFrameBuffer(mainSceneFB); // renderer.clearBuffers(true,true,false); // // renderManager.renderViewPortRaw(viewPort); } // should we update avg lum? if (throttle == -1){ // update every frame curSrc = 0; oppSrc = 0; blendFactor = 0; time = 0; updateAverageLuminance(renderer); }else{ if (curSrc == -1){ curSrc = 0; oppSrc = 0; // initial update updateAverageLuminance(renderer); blendFactor = 0; time = 0; }else if (time > throttle){ // time to switch oppSrc = curSrc; curSrc = opposite(curSrc); updateAverageLuminance(renderer); blendFactor = 0; time = 0; } } // since out == mainSceneFB, tonemap into the main screen instead //renderToneMap(renderer, out); renderToneMap(renderer, null); renderManager.setCamera(viewPort.getCamera(), false); } public void cleanup() { if (!enabled) return; if (msFB != null) renderer.deleteFrameBuffer(msFB); if (mainSceneFB != null) renderer.deleteFrameBuffer(mainSceneFB); if (scene64FB != null){ renderer.deleteFrameBuffer(scene64FB); renderer.deleteFrameBuffer(scene8FB); renderer.deleteFrameBuffer(scene1FB[0]); renderer.deleteFrameBuffer(scene1FB[1]); } } @Override public void setProfiler(AppProfiler profiler) { this.prof = profiler; } }