Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 79 additions & 69 deletions jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2021 jMonkeyEngine
* Copyright (c) 2009-2025 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -40,7 +40,6 @@
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;
Expand All @@ -62,23 +61,22 @@
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 final 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 approximateNormals = false;
private boolean useOnlyAo = false;
private boolean useAo = true;
private Material ssaoMat;
private Pass ssaoPass;
// private Material downSampleMat;
// private Pass downSamplePass;
private float downSampleFactor = 1f;
private RenderManager renderManager;
private ViewPort viewPort;
private boolean approximateNormals = false;

/**
* Create a Screen Space Ambient Occlusion Filter
Expand All @@ -89,10 +87,11 @@ public 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
* @param intensity intensity of the resulting AO. default 1.5f
* @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();
Expand Down Expand Up @@ -126,38 +125,32 @@ protected Material getMaterial() {
}

@Override
protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
protected void initFilter(AssetManager assetManager, RenderManager renderManager, ViewPort vp, int w, int h) {
this.renderManager = renderManager;
this.viewPort = vp;
int screenWidth = w;
int screenHeight = h;
float downSampleFactor = 1f;
postRenderPasses = new ArrayList<Pass>();

normalPass = new Pass();
normalPass.init(renderManager.getRenderer(), (int) (screenWidth / downSampleFactor), (int) (screenHeight / downSampleFactor), Format.RGBA8, Format.Depth);


frustumNearFar = new Vector2f();

Vector2f frustumNearFar = new Vector2f();
float farY = (vp.getCamera().getFrustumTop() / vp.getCamera().getFrustumNear()) * vp.getCamera().getFrustumFar();
float farX = farY * (screenWidth / (float) screenHeight);
frustumCorner = new Vector3f(farX, farY, vp.getCamera().getFrustumFar());
Vector3f 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 = new Material(assetManager, "Common/MatDefs/SSAO/ssao.j3md");
ssaoMat.setTexture("Normals", normalPass.getRenderedTexture());
Texture random = manager.loadTexture("Common/MatDefs/SSAO/Textures/random.png");
Texture random = assetManager.loadTexture("Common/MatDefs/SSAO/Textures/random.png");
random.setWrap(Texture.WrapMode.Repeat);
ssaoMat.setTexture("RandomMap", random);

ssaoPass = new Pass("SSAO pass") {

Pass ssaoPass = new Pass("SSAO pass") {
@Override
public boolean requiresDepthAsTexture() {
return true;
Expand All @@ -168,18 +161,18 @@ public boolean requiresDepthAsTexture() {
// 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 = new Material(assetManager, "Common/MatDefs/SSAO/ssaoBlur.j3md");
material.setTexture("SSAOMap", ssaoPass.getRenderedTexture());
material.setVector2("FrustumNearFar", frustumNearFar);
material.setBoolean("UseAo", useAo);
material.setBoolean("UseOnlyAo", useOnlyAo);

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);
ssaoMat.setBoolean("ApproximateNormals", approximateNormals);

Expand All @@ -189,7 +182,6 @@ public boolean requiresDepthAsTexture() {
float blurScale = 2f;
material.setFloat("XScale", blurScale * xScale);
material.setFloat("YScale", blurScale * yScale);

}

@Override
Expand All @@ -198,18 +190,20 @@ protected void cleanUpFilter(Renderer r) {
}

/**
* Return the bias<br>
* see {@link #setBias(float bias)}
* @return bias
* Returns the bias value used in the SSAO calculation.
*
* @return The bias value.
* @see #setBias(float)
*/
public float getBias() {
return bias;
}

/**
* Sets the width of the occlusion cone considered by the occludee default is 0.1f
* Sets the width of the occlusion cone considered by the occludee.
* A higher bias means a wider cone, resulting in less self-occlusion.
*
* @param bias the desired width (default=0.1)
* @param bias The desired bias value (default: 0.1f).
*/
public void setBias(float bias) {
this.bias = bias;
Expand All @@ -219,62 +213,65 @@ public void setBias(float bias) {
}

/**
* returns the ambient occlusion intensity
* @return intensity
* Returns the ambient occlusion intensity.
*
* @return The intensity value.
*/
public float getIntensity() {
return intensity;
}

/**
* Sets the Ambient occlusion intensity default is 1.5
* Sets the ambient occlusion intensity. A higher intensity makes the ambient
* occlusion effect more pronounced.
*
* @param intensity the desired intensity (default=1.5)
* @param intensity The desired intensity (default: 1.5f).
*/
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 the sample radius
* Returns the sample radius used in the SSAO calculation.
*
* @return The sample radius.
* @see #setSampleRadius(float)
*/
public float getSampleRadius() {
return sampleRadius;
}

/**
* Sets the radius of the area where random samples will be picked default 5.1f
* Sets the radius of the area where random samples will be picked for SSAO.
* A larger radius considers more distant occluders.
*
* @param sampleRadius the desired radius (default=5.1)
* @param sampleRadius The desired radius (default: 5.1f).
*/
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 scale
* Returns the scale value used in the SSAO calculation.
*
* @return The scale value.
* @see #setScale(float)
*/
public float getScale() {
return scale;
}

/**
*
* Returns the distance between occluders and occludee. default 0.2f
* Sets the distance between occluders and occludee for SSAO.
* This essentially controls the "thickness" of the ambient occlusion.
*
* @param scale the desired distance (default=0.2)
* @param scale The desired distance (default: 0.2f).
*/
public void setScale(float scale) {
this.scale = scale;
Expand All @@ -284,15 +281,38 @@ public void setScale(float scale) {
}

/**
* debugging only , will be removed
* Sets whether to use approximate normals for the SSAO calculation.
* If `true`, normals are derived from the depth buffer. If `false`, a separate
* normal pass is rendered.
*
* @param approximateNormals `true` to use approximate normals, `false` to use a normal pass.
*/
public void setApproximateNormals(boolean approximateNormals) {
this.approximateNormals = approximateNormals;
if (ssaoMat != null) {
ssaoMat.setBoolean("ApproximateNormals", approximateNormals);
}
}

/**
* Checks if approximate normals are being used for SSAO calculation.
*
* @return `true` if approximate normals are used, `false` otherwise.
*/
public boolean isApproximateNormals() {
return approximateNormals;
}

/**
* debugging only, will be removed
* @return true if using ambient occlusion
*/
public boolean isUseAo() {
return useAo;
}

/**
* debugging only , will be removed
* debugging only, will be removed
*
* @param useAo true to enable, false to disable (default=true)
*/
Expand All @@ -301,30 +321,18 @@ public void setUseAo(boolean useAo) {
if (material != null) {
material.setBoolean("UseAo", useAo);
}

}

public void setApproximateNormals(boolean approximateNormals) {
this.approximateNormals = approximateNormals;
if (ssaoMat != null) {
ssaoMat.setBoolean("ApproximateNormals", approximateNormals);
}
}

public boolean isApproximateNormals() {
return approximateNormals;
}

/**
* debugging only , will be removed
* debugging only, will be removed
* @return useOnlyAo
*/
public boolean isUseOnlyAo() {
return useOnlyAo;
}

/**
* debugging only , will be removed
* debugging only, will be removed
*
* @param useOnlyAo true to enable, false to disable (default=false)
*/
Expand All @@ -343,6 +351,7 @@ public void write(JmeExporter ex) throws IOException {
oc.write(intensity, "intensity", 1.5f);
oc.write(scale, "scale", 0.2f);
oc.write(bias, "bias", 0.1f);
oc.write(approximateNormals, "approximateNormals", false);
}

@Override
Expand All @@ -353,5 +362,6 @@ public void read(JmeImporter im) throws IOException {
intensity = ic.readFloat("intensity", 1.5f);
scale = ic.readFloat("scale", 0.2f);
bias = ic.readFloat("bias", 0.1f);
approximateNormals = ic.readBoolean("approximateNormals", false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.jme3.post.filters;

import com.jme3.asset.AssetManager;
import com.jme3.asset.DesktopAssetManager;
import com.jme3.export.binary.BinaryExporter;
import com.jme3.post.ssao.SSAOFilter;
import org.junit.Assert;
import org.junit.Test;

/**
* Automated tests for the {@code SSAOFilter} class.
*
* @author capdevon
*/
public class SSAOFilterTest {

/**
* Tests serialization and de-serialization of an {@code SSAOFilter}.
*/
@Test
public void testSaveAndLoad() {
SSAOFilter filter = new SSAOFilter();

// Verify the default parameter values:
verifyDefaults(filter);

// Set parameters to new values:
filter.setEnabled(false);
filter.setSampleRadius(4.5f);
filter.setIntensity(1.8f);
filter.setScale(0.4f);
filter.setBias(0.5f);
filter.setApproximateNormals(true);

// Create a duplicate filter using serialization:
AssetManager assetManager = new DesktopAssetManager();
SSAOFilter copy = BinaryExporter.saveAndLoad(assetManager, filter);

// Verify the parameter values of the copy:
Assert.assertEquals("SSAOFilter", copy.getName());
Assert.assertEquals(4.5f, copy.getSampleRadius(), 0f);
Assert.assertEquals(1.8f, copy.getIntensity(), 0f);
Assert.assertEquals(0.4f, copy.getScale(), 0f);
Assert.assertEquals(0.5f, copy.getBias(), 0f);
Assert.assertTrue(copy.isApproximateNormals());
Assert.assertFalse(copy.isEnabled());
}

/**
* Verify some default values of a newly instantiated {@code SSAOFilter}.
*
* @param filter (not null, unaffected)
*/
private void verifyDefaults(SSAOFilter filter) {
Assert.assertEquals("SSAOFilter", filter.getName());
Assert.assertEquals(5.1f, filter.getSampleRadius(), 0f);
Assert.assertEquals(1.5f, filter.getIntensity(), 0f);
Assert.assertEquals(0.2f, filter.getScale(), 0f);
Assert.assertEquals(0.1f, filter.getBias(), 0f);
Assert.assertFalse(filter.isApproximateNormals());
Assert.assertTrue(filter.isEnabled());
}
}