blob: cb0c037b7a00f887e4551be47f7d2a80c0139ad7 [file] [log] [blame]
/*
* 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.ssao;
import com.jme3.asset.AssetManager;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.material.Material;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.Filter;
import com.jme3.post.Filter.Pass;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.renderer.ViewPort;
import com.jme3.shader.VarType;
import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture;
import java.io.IOException;
import java.util.ArrayList;
/**
* SSAO stands for screen space ambient occlusion
* It's a technique that fake ambient lighting by computing shadows that near by objects would casts on each others
* under the effect of an ambient light
* more info on this in this blog post <a href="http://jmonkeyengine.org/2010/08/16/screen-space-ambient-occlusion-for-jmonkeyengine-3-0/">http://jmonkeyengine.org/2010/08/16/screen-space-ambient-occlusion-for-jmonkeyengine-3-0/</a>
*
* @author Rémy Bouquet aka Nehon
*/
public class SSAOFilter extends Filter {
private Pass normalPass;
private Vector3f frustumCorner;
private Vector2f frustumNearFar;
private Vector2f[] samples = {new Vector2f(1.0f, 0.0f), new Vector2f(-1.0f, 0.0f), new Vector2f(0.0f, 1.0f), new Vector2f(0.0f, -1.0f)};
private float sampleRadius = 5.1f;
private float intensity = 1.5f;
private float scale = 0.2f;
private float bias = 0.1f;
private boolean useOnlyAo = false;
private boolean useAo = true;
private Material ssaoMat;
private Pass ssaoPass;
// private Material downSampleMat;
// private Pass downSamplePass;
private float downSampleFactor = 1f;
/**
* Create a Screen Space Ambient Occlusion Filter
*/
public SSAOFilter() {
super("SSAOFilter");
}
/**
* Create a Screen Space Ambient Occlusion Filter
* @param sampleRadius The radius of the area where random samples will be picked. default 5.1f
* @param intensity intensity of the resulting AO. default 1.2f
* @param scale distance between occluders and occludee. default 0.2f
* @param bias the width of the occlusion cone considered by the occludee. default 0.1f
*/
public SSAOFilter(float sampleRadius, float intensity, float scale, float bias) {
this();
this.sampleRadius = sampleRadius;
this.intensity = intensity;
this.scale = scale;
this.bias = bias;
}
@Override
protected boolean isRequiresDepthTexture() {
return true;
}
@Override
protected void postQueue(RenderManager renderManager, ViewPort viewPort) {
Renderer r = renderManager.getRenderer();
r.setFrameBuffer(normalPass.getRenderFrameBuffer());
renderManager.getRenderer().clearBuffers(true, true, true);
renderManager.setForcedTechnique("PreNormalPass");
renderManager.renderViewPortQueues(viewPort, false);
renderManager.setForcedTechnique(null);
renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer());
}
@Override
protected Material getMaterial() {
return material;
}
@Override
protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
int screenWidth = w;
int screenHeight = h;
postRenderPasses = new ArrayList<Pass>();
normalPass = new Pass();
normalPass.init(renderManager.getRenderer(), (int) (screenWidth / downSampleFactor), (int) (screenHeight / downSampleFactor), Format.RGBA8, Format.Depth);
frustumNearFar = new Vector2f();
float farY = (vp.getCamera().getFrustumTop() / vp.getCamera().getFrustumNear()) * vp.getCamera().getFrustumFar();
float farX = farY * ((float) screenWidth / (float) screenHeight);
frustumCorner = new Vector3f(farX, farY, vp.getCamera().getFrustumFar());
frustumNearFar.x = vp.getCamera().getFrustumNear();
frustumNearFar.y = vp.getCamera().getFrustumFar();
//ssao Pass
ssaoMat = new Material(manager, "Common/MatDefs/SSAO/ssao.j3md");
ssaoMat.setTexture("Normals", normalPass.getRenderedTexture());
Texture random = manager.loadTexture("Common/MatDefs/SSAO/Textures/random.png");
random.setWrap(Texture.WrapMode.Repeat);
ssaoMat.setTexture("RandomMap", random);
ssaoPass = new Pass() {
@Override
public boolean requiresDepthAsTexture() {
return true;
}
};
ssaoPass.init(renderManager.getRenderer(), (int) (screenWidth / downSampleFactor), (int) (screenHeight / downSampleFactor), Format.RGBA8, Format.Depth, 1, ssaoMat);
ssaoPass.getRenderedTexture().setMinFilter(Texture.MinFilter.Trilinear);
ssaoPass.getRenderedTexture().setMagFilter(Texture.MagFilter.Bilinear);
postRenderPasses.add(ssaoPass);
material = new Material(manager, "Common/MatDefs/SSAO/ssaoBlur.j3md");
material.setTexture("SSAOMap", ssaoPass.getRenderedTexture());
ssaoMat.setVector3("FrustumCorner", frustumCorner);
ssaoMat.setFloat("SampleRadius", sampleRadius);
ssaoMat.setFloat("Intensity", intensity);
ssaoMat.setFloat("Scale", scale);
ssaoMat.setFloat("Bias", bias);
material.setBoolean("UseAo", useAo);
material.setBoolean("UseOnlyAo", useOnlyAo);
ssaoMat.setVector2("FrustumNearFar", frustumNearFar);
material.setVector2("FrustumNearFar", frustumNearFar);
ssaoMat.setParam("Samples", VarType.Vector2Array, samples);
float xScale = 1.0f / w;
float yScale = 1.0f / h;
float blurScale = 2f;
material.setFloat("XScale", blurScale * xScale);
material.setFloat("YScale", blurScale * yScale);
}
/**
* Return the bias<br>
* see {@link #setBias(float bias)}
* @return
*/
public float getBias() {
return bias;
}
/**
* Sets the the width of the occlusion cone considered by the occludee default is 0.1f
* @param bias
*/
public void setBias(float bias) {
this.bias = bias;
if (ssaoMat != null) {
ssaoMat.setFloat("Bias", bias);
}
}
/**
* returns the ambient occlusion intensity
* @return
*/
public float getIntensity() {
return intensity;
}
/**
* Sets the Ambient occlusion intensity default is 1.2f
* @param intensity
*/
public void setIntensity(float intensity) {
this.intensity = intensity;
if (ssaoMat != null) {
ssaoMat.setFloat("Intensity", intensity);
}
}
/**
* returns the sample radius<br>
* see {link setSampleRadius(float sampleRadius)}
* @return
*/
public float getSampleRadius() {
return sampleRadius;
}
/**
* Sets the radius of the area where random samples will be picked dafault 5.1f
* @param sampleRadius
*/
public void setSampleRadius(float sampleRadius) {
this.sampleRadius = sampleRadius;
if (ssaoMat != null) {
ssaoMat.setFloat("SampleRadius", sampleRadius);
}
}
/**
* returns the scale<br>
* see {@link #setScale(float scale)}
* @return
*/
public float getScale() {
return scale;
}
/**
*
* Returns the distance between occluders and occludee. default 0.2f
* @param scale
*/
public void setScale(float scale) {
this.scale = scale;
if (ssaoMat != null) {
ssaoMat.setFloat("Scale", scale);
}
}
/**
* debugging only , will be removed
* @return
*/
public boolean isUseAo() {
return useAo;
}
/**
* debugging only , will be removed
*/
public void setUseAo(boolean useAo) {
this.useAo = useAo;
if (material != null) {
material.setBoolean("UseAo", useAo);
}
}
/**
* debugging only , will be removed
* @return
*/
public boolean isUseOnlyAo() {
return useOnlyAo;
}
/**
* debugging only , will be removed
*/
public void setUseOnlyAo(boolean useOnlyAo) {
this.useOnlyAo = useOnlyAo;
if (material != null) {
material.setBoolean("UseOnlyAo", useOnlyAo);
}
}
@Override
public void write(JmeExporter ex) throws IOException {
super.write(ex);
OutputCapsule oc = ex.getCapsule(this);
oc.write(sampleRadius, "sampleRadius", 5.1f);
oc.write(intensity, "intensity", 1.5f);
oc.write(scale, "scale", 0.2f);
oc.write(bias, "bias", 0.1f);
}
@Override
public void read(JmeImporter im) throws IOException {
super.read(im);
InputCapsule ic = im.getCapsule(this);
sampleRadius = ic.readFloat("sampleRadius", 5.1f);
intensity = ic.readFloat("intensity", 1.5f);
scale = ic.readFloat("scale", 0.2f);
bias = ic.readFloat("bias", 0.1f);
}
}