Skip to content

Commit 20876cb

Browse files
authored
Merge pull request #2486 from capdevon/capdevon-SSAO
SSAOFilter: javadoc + fix approximateNormals serialization
2 parents 6c02ec9 + 7c835e1 commit 20876cb

File tree

2 files changed

+142
-69
lines changed

2 files changed

+142
-69
lines changed

jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java

Lines changed: 79 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2021 jMonkeyEngine
2+
* Copyright (c) 2009-2025 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -40,7 +40,6 @@
4040
import com.jme3.math.Vector2f;
4141
import com.jme3.math.Vector3f;
4242
import com.jme3.post.Filter;
43-
import com.jme3.post.Filter.Pass;
4443
import com.jme3.renderer.RenderManager;
4544
import com.jme3.renderer.Renderer;
4645
import com.jme3.renderer.ViewPort;
@@ -62,23 +61,22 @@
6261
public class SSAOFilter extends Filter {
6362

6463
private Pass normalPass;
65-
private Vector3f frustumCorner;
66-
private Vector2f frustumNearFar;
67-
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)};
64+
private final Vector2f[] samples = {
65+
new Vector2f(1.0f, 0.0f),
66+
new Vector2f(-1.0f, 0.0f),
67+
new Vector2f(0.0f, 1.0f),
68+
new Vector2f(0.0f, -1.0f)
69+
};
6870
private float sampleRadius = 5.1f;
6971
private float intensity = 1.5f;
7072
private float scale = 0.2f;
7173
private float bias = 0.1f;
74+
private boolean approximateNormals = false;
7275
private boolean useOnlyAo = false;
7376
private boolean useAo = true;
7477
private Material ssaoMat;
75-
private Pass ssaoPass;
76-
// private Material downSampleMat;
77-
// private Pass downSamplePass;
78-
private float downSampleFactor = 1f;
7978
private RenderManager renderManager;
8079
private ViewPort viewPort;
81-
private boolean approximateNormals = false;
8280

8381
/**
8482
* Create a Screen Space Ambient Occlusion Filter
@@ -89,10 +87,11 @@ public SSAOFilter() {
8987

9088
/**
9189
* Create a Screen Space Ambient Occlusion Filter
90+
*
9291
* @param sampleRadius The radius of the area where random samples will be picked. default 5.1f
93-
* @param intensity intensity of the resulting AO. default 1.2f
94-
* @param scale distance between occluders and occludee. default 0.2f
95-
* @param bias the width of the occlusion cone considered by the occludee. default 0.1f
92+
* @param intensity intensity of the resulting AO. default 1.5f
93+
* @param scale distance between occluders and occludee. default 0.2f
94+
* @param bias the width of the occlusion cone considered by the occludee. default 0.1f
9695
*/
9796
public SSAOFilter(float sampleRadius, float intensity, float scale, float bias) {
9897
this();
@@ -126,38 +125,32 @@ protected Material getMaterial() {
126125
}
127126

128127
@Override
129-
protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
128+
protected void initFilter(AssetManager assetManager, RenderManager renderManager, ViewPort vp, int w, int h) {
130129
this.renderManager = renderManager;
131130
this.viewPort = vp;
132131
int screenWidth = w;
133132
int screenHeight = h;
133+
float downSampleFactor = 1f;
134134
postRenderPasses = new ArrayList<Pass>();
135135

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

139-
140-
frustumNearFar = new Vector2f();
141-
139+
Vector2f frustumNearFar = new Vector2f();
142140
float farY = (vp.getCamera().getFrustumTop() / vp.getCamera().getFrustumNear()) * vp.getCamera().getFrustumFar();
143141
float farX = farY * (screenWidth / (float) screenHeight);
144-
frustumCorner = new Vector3f(farX, farY, vp.getCamera().getFrustumFar());
142+
Vector3f frustumCorner = new Vector3f(farX, farY, vp.getCamera().getFrustumFar());
145143
frustumNearFar.x = vp.getCamera().getFrustumNear();
146144
frustumNearFar.y = vp.getCamera().getFrustumFar();
147145

148-
149-
150-
151-
152146
//ssao Pass
153-
ssaoMat = new Material(manager, "Common/MatDefs/SSAO/ssao.j3md");
147+
ssaoMat = new Material(assetManager, "Common/MatDefs/SSAO/ssao.j3md");
154148
ssaoMat.setTexture("Normals", normalPass.getRenderedTexture());
155-
Texture random = manager.loadTexture("Common/MatDefs/SSAO/Textures/random.png");
149+
Texture random = assetManager.loadTexture("Common/MatDefs/SSAO/Textures/random.png");
156150
random.setWrap(Texture.WrapMode.Repeat);
157151
ssaoMat.setTexture("RandomMap", random);
158152

159-
ssaoPass = new Pass("SSAO pass") {
160-
153+
Pass ssaoPass = new Pass("SSAO pass") {
161154
@Override
162155
public boolean requiresDepthAsTexture() {
163156
return true;
@@ -168,18 +161,18 @@ public boolean requiresDepthAsTexture() {
168161
// ssaoPass.getRenderedTexture().setMinFilter(Texture.MinFilter.Trilinear);
169162
// ssaoPass.getRenderedTexture().setMagFilter(Texture.MagFilter.Bilinear);
170163
postRenderPasses.add(ssaoPass);
171-
material = new Material(manager, "Common/MatDefs/SSAO/ssaoBlur.j3md");
164+
material = new Material(assetManager, "Common/MatDefs/SSAO/ssaoBlur.j3md");
172165
material.setTexture("SSAOMap", ssaoPass.getRenderedTexture());
166+
material.setVector2("FrustumNearFar", frustumNearFar);
167+
material.setBoolean("UseAo", useAo);
168+
material.setBoolean("UseOnlyAo", useOnlyAo);
173169

174170
ssaoMat.setVector3("FrustumCorner", frustumCorner);
175171
ssaoMat.setFloat("SampleRadius", sampleRadius);
176172
ssaoMat.setFloat("Intensity", intensity);
177173
ssaoMat.setFloat("Scale", scale);
178174
ssaoMat.setFloat("Bias", bias);
179-
material.setBoolean("UseAo", useAo);
180-
material.setBoolean("UseOnlyAo", useOnlyAo);
181175
ssaoMat.setVector2("FrustumNearFar", frustumNearFar);
182-
material.setVector2("FrustumNearFar", frustumNearFar);
183176
ssaoMat.setParam("Samples", VarType.Vector2Array, samples);
184177
ssaoMat.setBoolean("ApproximateNormals", approximateNormals);
185178

@@ -189,7 +182,6 @@ public boolean requiresDepthAsTexture() {
189182
float blurScale = 2f;
190183
material.setFloat("XScale", blurScale * xScale);
191184
material.setFloat("YScale", blurScale * yScale);
192-
193185
}
194186

195187
@Override
@@ -198,18 +190,20 @@ protected void cleanUpFilter(Renderer r) {
198190
}
199191

200192
/**
201-
* Return the bias<br>
202-
* see {@link #setBias(float bias)}
203-
* @return bias
193+
* Returns the bias value used in the SSAO calculation.
194+
*
195+
* @return The bias value.
196+
* @see #setBias(float)
204197
*/
205198
public float getBias() {
206199
return bias;
207200
}
208201

209202
/**
210-
* Sets the width of the occlusion cone considered by the occludee default is 0.1f
203+
* Sets the width of the occlusion cone considered by the occludee.
204+
* A higher bias means a wider cone, resulting in less self-occlusion.
211205
*
212-
* @param bias the desired width (default=0.1)
206+
* @param bias The desired bias value (default: 0.1f).
213207
*/
214208
public void setBias(float bias) {
215209
this.bias = bias;
@@ -219,62 +213,65 @@ public void setBias(float bias) {
219213
}
220214

221215
/**
222-
* returns the ambient occlusion intensity
223-
* @return intensity
216+
* Returns the ambient occlusion intensity.
217+
*
218+
* @return The intensity value.
224219
*/
225220
public float getIntensity() {
226221
return intensity;
227222
}
228223

229224
/**
230-
* Sets the Ambient occlusion intensity default is 1.5
225+
* Sets the ambient occlusion intensity. A higher intensity makes the ambient
226+
* occlusion effect more pronounced.
231227
*
232-
* @param intensity the desired intensity (default=1.5)
228+
* @param intensity The desired intensity (default: 1.5f).
233229
*/
234230
public void setIntensity(float intensity) {
235231
this.intensity = intensity;
236232
if (ssaoMat != null) {
237233
ssaoMat.setFloat("Intensity", intensity);
238234
}
239-
240235
}
241236

242237
/**
243-
* returns the sample radius<br>
244-
* see {link setSampleRadius(float sampleRadius)}
245-
* @return the sample radius
238+
* Returns the sample radius used in the SSAO calculation.
239+
*
240+
* @return The sample radius.
241+
* @see #setSampleRadius(float)
246242
*/
247243
public float getSampleRadius() {
248244
return sampleRadius;
249245
}
250246

251247
/**
252-
* Sets the radius of the area where random samples will be picked default 5.1f
248+
* Sets the radius of the area where random samples will be picked for SSAO.
249+
* A larger radius considers more distant occluders.
253250
*
254-
* @param sampleRadius the desired radius (default=5.1)
251+
* @param sampleRadius The desired radius (default: 5.1f).
255252
*/
256253
public void setSampleRadius(float sampleRadius) {
257254
this.sampleRadius = sampleRadius;
258255
if (ssaoMat != null) {
259256
ssaoMat.setFloat("SampleRadius", sampleRadius);
260257
}
261-
262258
}
263259

264260
/**
265-
* returns the scale<br>
266-
* see {@link #setScale(float scale)}
267-
* @return scale
261+
* Returns the scale value used in the SSAO calculation.
262+
*
263+
* @return The scale value.
264+
* @see #setScale(float)
268265
*/
269266
public float getScale() {
270267
return scale;
271268
}
272269

273270
/**
274-
*
275-
* Returns the distance between occluders and occludee. default 0.2f
271+
* Sets the distance between occluders and occludee for SSAO.
272+
* This essentially controls the "thickness" of the ambient occlusion.
276273
*
277-
* @param scale the desired distance (default=0.2)
274+
* @param scale The desired distance (default: 0.2f).
278275
*/
279276
public void setScale(float scale) {
280277
this.scale = scale;
@@ -284,15 +281,38 @@ public void setScale(float scale) {
284281
}
285282

286283
/**
287-
* debugging only , will be removed
284+
* Sets whether to use approximate normals for the SSAO calculation.
285+
* If `true`, normals are derived from the depth buffer. If `false`, a separate
286+
* normal pass is rendered.
287+
*
288+
* @param approximateNormals `true` to use approximate normals, `false` to use a normal pass.
289+
*/
290+
public void setApproximateNormals(boolean approximateNormals) {
291+
this.approximateNormals = approximateNormals;
292+
if (ssaoMat != null) {
293+
ssaoMat.setBoolean("ApproximateNormals", approximateNormals);
294+
}
295+
}
296+
297+
/**
298+
* Checks if approximate normals are being used for SSAO calculation.
299+
*
300+
* @return `true` if approximate normals are used, `false` otherwise.
301+
*/
302+
public boolean isApproximateNormals() {
303+
return approximateNormals;
304+
}
305+
306+
/**
307+
* debugging only, will be removed
288308
* @return true if using ambient occlusion
289309
*/
290310
public boolean isUseAo() {
291311
return useAo;
292312
}
293313

294314
/**
295-
* debugging only , will be removed
315+
* debugging only, will be removed
296316
*
297317
* @param useAo true to enable, false to disable (default=true)
298318
*/
@@ -301,30 +321,18 @@ public void setUseAo(boolean useAo) {
301321
if (material != null) {
302322
material.setBoolean("UseAo", useAo);
303323
}
304-
305-
}
306-
307-
public void setApproximateNormals(boolean approximateNormals) {
308-
this.approximateNormals = approximateNormals;
309-
if (ssaoMat != null) {
310-
ssaoMat.setBoolean("ApproximateNormals", approximateNormals);
311-
}
312-
}
313-
314-
public boolean isApproximateNormals() {
315-
return approximateNormals;
316324
}
317325

318326
/**
319-
* debugging only , will be removed
327+
* debugging only, will be removed
320328
* @return useOnlyAo
321329
*/
322330
public boolean isUseOnlyAo() {
323331
return useOnlyAo;
324332
}
325333

326334
/**
327-
* debugging only , will be removed
335+
* debugging only, will be removed
328336
*
329337
* @param useOnlyAo true to enable, false to disable (default=false)
330338
*/
@@ -343,6 +351,7 @@ public void write(JmeExporter ex) throws IOException {
343351
oc.write(intensity, "intensity", 1.5f);
344352
oc.write(scale, "scale", 0.2f);
345353
oc.write(bias, "bias", 0.1f);
354+
oc.write(approximateNormals, "approximateNormals", false);
346355
}
347356

348357
@Override
@@ -353,5 +362,6 @@ public void read(JmeImporter im) throws IOException {
353362
intensity = ic.readFloat("intensity", 1.5f);
354363
scale = ic.readFloat("scale", 0.2f);
355364
bias = ic.readFloat("bias", 0.1f);
365+
approximateNormals = ic.readBoolean("approximateNormals", false);
356366
}
357367
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.jme3.post.filters;
2+
3+
import com.jme3.asset.AssetManager;
4+
import com.jme3.asset.DesktopAssetManager;
5+
import com.jme3.export.binary.BinaryExporter;
6+
import com.jme3.post.ssao.SSAOFilter;
7+
import org.junit.Assert;
8+
import org.junit.Test;
9+
10+
/**
11+
* Automated tests for the {@code SSAOFilter} class.
12+
*
13+
* @author capdevon
14+
*/
15+
public class SSAOFilterTest {
16+
17+
/**
18+
* Tests serialization and de-serialization of an {@code SSAOFilter}.
19+
*/
20+
@Test
21+
public void testSaveAndLoad() {
22+
SSAOFilter filter = new SSAOFilter();
23+
24+
// Verify the default parameter values:
25+
verifyDefaults(filter);
26+
27+
// Set parameters to new values:
28+
filter.setEnabled(false);
29+
filter.setSampleRadius(4.5f);
30+
filter.setIntensity(1.8f);
31+
filter.setScale(0.4f);
32+
filter.setBias(0.5f);
33+
filter.setApproximateNormals(true);
34+
35+
// Create a duplicate filter using serialization:
36+
AssetManager assetManager = new DesktopAssetManager();
37+
SSAOFilter copy = BinaryExporter.saveAndLoad(assetManager, filter);
38+
39+
// Verify the parameter values of the copy:
40+
Assert.assertEquals("SSAOFilter", copy.getName());
41+
Assert.assertEquals(4.5f, copy.getSampleRadius(), 0f);
42+
Assert.assertEquals(1.8f, copy.getIntensity(), 0f);
43+
Assert.assertEquals(0.4f, copy.getScale(), 0f);
44+
Assert.assertEquals(0.5f, copy.getBias(), 0f);
45+
Assert.assertTrue(copy.isApproximateNormals());
46+
Assert.assertFalse(copy.isEnabled());
47+
}
48+
49+
/**
50+
* Verify some default values of a newly instantiated {@code SSAOFilter}.
51+
*
52+
* @param filter (not null, unaffected)
53+
*/
54+
private void verifyDefaults(SSAOFilter filter) {
55+
Assert.assertEquals("SSAOFilter", filter.getName());
56+
Assert.assertEquals(5.1f, filter.getSampleRadius(), 0f);
57+
Assert.assertEquals(1.5f, filter.getIntensity(), 0f);
58+
Assert.assertEquals(0.2f, filter.getScale(), 0f);
59+
Assert.assertEquals(0.1f, filter.getBias(), 0f);
60+
Assert.assertFalse(filter.isApproximateNormals());
61+
Assert.assertTrue(filter.isEnabled());
62+
}
63+
}

0 commit comments

Comments
 (0)