Added a way to approximate the normals for the SSAO filter instead of rendering an additional geometry pass.

define_list_fix
Nehon 9 years ago
parent 46891b32c6
commit e89e0e7c12
  1. 29
      jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java
  2. 47
      jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.frag
  3. 9
      jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.j3md
  4. 43
      jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao15.frag
  5. 8
      jme3-examples/src/main/java/jme3test/post/SSAOUI.java
  6. 3
      jme3-examples/src/main/java/jme3test/post/TestSSAO.java
  7. 108
      jme3-examples/src/main/java/jme3test/post/TestSSAO2.java

@ -78,6 +78,7 @@ public class SSAOFilter extends Filter {
private float downSampleFactor = 1f;
private RenderManager renderManager;
private ViewPort viewPort;
private boolean approximateNormals = false;
/**
* Create a Screen Space Ambient Occlusion Filter
@ -108,13 +109,15 @@ public class SSAOFilter extends Filter {
@Override
protected void postQueue(RenderQueue queue) {
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());
if(!approximateNormals) {
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
@ -178,6 +181,7 @@ public class SSAOFilter extends Filter {
ssaoMat.setVector2("FrustumNearFar", frustumNearFar);
material.setVector2("FrustumNearFar", frustumNearFar);
ssaoMat.setParam("Samples", VarType.Vector2Array, samples);
ssaoMat.setBoolean("ApproximateNormals", approximateNormals);
float xScale = 1.0f / w;
float yScale = 1.0f / h;
@ -294,6 +298,17 @@ public class SSAOFilter extends Filter {
}
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
* @return useOnlyAo

@ -1,4 +1,5 @@
uniform vec2 g_Resolution;
uniform vec2 g_ResolutionInverse;
uniform vec2 m_FrustumNearFar;
uniform sampler2D m_Texture;
uniform sampler2D m_Normals;
@ -15,12 +16,9 @@ uniform vec2[4] m_Samples;
varying vec2 texCoord;
float depthv;
vec3 getPosition(in vec2 uv){
vec3 getPosition(float depthv, in vec2 uv){
//Reconstruction from depth
depthv =texture2D(m_DepthTexture,uv).r;
float depth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - depthv* (m_FrustumNearFar.y-m_FrustumNearFar.x));
float depth = (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - depthv * (m_FrustumNearFar.y-m_FrustumNearFar.x));
//one frustum corner method
float x = mix(-m_FrustumCorner.x, m_FrustumCorner.x, uv.x);
@ -29,6 +27,19 @@ vec3 getPosition(in vec2 uv){
return depth* vec3(x, y, m_FrustumCorner.z);
}
vec3 approximateNormal(in vec3 pos,in vec2 texCoord){
float step = g_ResolutionInverse.x ;
float stepy = g_ResolutionInverse.y ;
float depth2 = texture2D(m_DepthTexture,texCoord + vec2(step,-stepy)).r;
float depth3 = texture2D(m_DepthTexture,texCoord + vec2(-step,-stepy)).r;
vec3 pos2 = vec3(getPosition(depth2,texCoord + vec2(step,-stepy)));
vec3 pos3 = vec3(getPosition(depth3,texCoord + vec2(-step,-stepy)));
vec3 v1 = (pos - pos2).xyz;
vec3 v2 = (pos3 - pos2).xyz;
return normalize(cross(-v1, v2));
}
vec3 getNormal(in vec2 uv){
return normalize(texture2D(m_Normals, uv).xyz * 2.0 - 1.0);
}
@ -39,7 +50,8 @@ vec2 getRandom(in vec2 uv){
}
float doAmbientOcclusion(in vec2 tc, in vec3 pos, in vec3 norm){
vec3 diff = getPosition(tc)- pos;
float depthv = texture2D(m_DepthTexture, tc).r;
vec3 diff = getPosition(depthv, tc)- pos;
vec3 v = normalize(diff);
float d = length(diff) * m_Scale;
@ -58,14 +70,21 @@ void main(){
float result;
//vec2 vec[4] = { vec2(1.0, 0.0), vec2(-1.0, 0.0), vec2(0.0, 1.0), vec2(0.0, -1.0) };
vec3 position = getPosition(texCoord);
//optimization, do not calculate AO if depth is 1
if(depthv==1.0){
gl_FragColor=vec4(1.0);
return;
float depthv = texture2D(m_DepthTexture, texCoord).r;
//optimization, do not calculate AO if depth is 1
if(depthv == 1.0){
gl_FragColor = vec4(1.0);
return;
}
vec3 normal = getNormal(texCoord);
vec3 position = getPosition(depthv, texCoord);
#ifdef APPROXIMATE_NORMALS
vec3 normal = approximateNormal(position, texCoord);
#else
vec3 normal = getNormal(texCoord);
#endif
vec2 rand = getRandom(texCoord);
float ao = 0.0;
@ -85,5 +104,5 @@ void main(){
ao /= float(iterations) * 4.0;
result = 1.0 - ao;
gl_FragColor=vec4(result);
gl_FragColor = vec4(result);
}

@ -14,20 +14,23 @@ MaterialDef SSAO {
Float Bias
Vector2 FrustumNearFar
Vector2Array Samples
Boolean ApproximateNormals
}
Technique {
VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
FragmentShader GLSL150: Common/MatDefs/SSAO/ssao15.frag
WorldParameters {
WorldParameters {
WorldViewMatrix
Resolution
ResolutionInverse
}
Defines {
RESOLVE_MS : NumSamples
RESOLVE_DEPTH_MS : NumSamplesDepth
APPROXIMATE_NORMALS : ApproximateNormals
}
}
@ -38,6 +41,10 @@ MaterialDef SSAO {
WorldParameters {
WorldViewMatrix
Resolution
ResolutionInverse
}
Defines {
APPROXIMATE_NORMALS : ApproximateNormals
}
}
}

@ -3,6 +3,7 @@
uniform COLORTEXTURE m_Texture;
uniform DEPTHTEXTURE m_DepthTexture;
uniform vec2 g_ResolutionInverse;
uniform vec2 g_Resolution;
uniform vec2 m_FrustumNearFar;
uniform sampler2D m_Normals;
@ -18,12 +19,11 @@ in vec2 texCoord;
out vec4 fragColor;
float depthv;
vec3 getPosition(in vec2 uv){
vec3 getPosition(float depthv, in vec2 uv){
//Reconstruction from depth
depthv =getDepth(m_DepthTexture,uv).r;
float depth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - depthv* (m_FrustumNearFar.y-m_FrustumNearFar.x));
float depth = (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - depthv * (m_FrustumNearFar.y-m_FrustumNearFar.x));
//one frustum corner method
float x = mix(-m_FrustumCorner.x, m_FrustumCorner.x, uv.x);
@ -36,6 +36,19 @@ vec3 getNormal(in vec2 uv){
return normalize(texture2D(m_Normals, uv).xyz * 2.0 - 1.0);
}
vec3 approximateNormal(in vec3 pos,in vec2 texCoord){
float step = g_ResolutionInverse.x ;
float stepy = g_ResolutionInverse.y ;
float depth2 = getDepth(m_DepthTexture,texCoord + vec2(step,-stepy)).r;
float depth3 = getDepth(m_DepthTexture,texCoord + vec2(-step,-stepy)).r;
vec3 pos2 = vec3(getPosition(depth2,texCoord + vec2(step,-stepy)));
vec3 pos3 = vec3(getPosition(depth3,texCoord + vec2(-step,-stepy)));
vec3 v1 = (pos - pos2).xyz;
vec3 v2 = (pos3 - pos2).xyz;
return normalize(cross(-v1, v2));
}
vec2 getRandom(in vec2 uv){
//float rand=(fract(uv.x*(g_Resolution.x/2.0))*0.25)+(fract(uv.y*(g_Resolution.y/2.0))*0.5);
vec4 rand=texture2D(m_RandomMap, g_Resolution * uv / 128.0 * 3.0)*2.0 -1.0;
@ -44,7 +57,8 @@ vec2 getRandom(in vec2 uv){
}
float doAmbientOcclusion(in vec2 tc, in vec3 pos, in vec3 norm){
vec3 diff = getPosition(tc)- pos;
float depthv = getDepth(m_DepthTexture, tc).r;
vec3 diff = getPosition(depthv, tc)- pos;
vec3 v = normalize(diff);
float d = length(diff) * m_Scale;
@ -63,13 +77,20 @@ void main(){
float result;
vec3 position = getPosition(texCoord);
//optimization, do not calculate AO if depth is 1
if(depthv==1.0){
fragColor = vec4(1.0);
return;
float depthv = getDepth(m_DepthTexture, texCoord).r;
//optimization, do not calculate AO if depth is 1
if(depthv == 1.0){
fragColor = vec4(1.0);
return;
}
vec3 normal = getNormal(texCoord);
vec3 position = getPosition(depthv, texCoord);
#ifdef APPROXIMATE_NORMALS
vec3 normal = approximateNormal(position, texCoord);
#else
vec3 normal = getNormal(texCoord);
#endif
vec2 rand = getRandom(texCoord);
float ao = 0.0;

@ -73,6 +73,7 @@ public class SSAOUI {
inputManager.addMapping("outputConfig", new KeyTrigger(KeyInput.KEY_P));
inputManager.addMapping("toggleUseAO", new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addMapping("toggleUseOnlyAo", new KeyTrigger(KeyInput.KEY_NUMPAD0));
inputManager.addMapping("toggleApprox", new KeyTrigger(KeyInput.KEY_NUMPAD5));
ActionListener acl = new ActionListener() {
@ -83,6 +84,11 @@ public class SSAOUI {
// filter.setUseAo(!filter.isUseAo());
System.out.println("use AO : " + filter.isEnabled());
}
if (name.equals("toggleApprox") && keyPressed) {
filter.setApproximateNormals(!filter.isApproximateNormals());
System.out.println("Approximate Normals : " + filter.isApproximateNormals());
}
if (name.equals("toggleUseOnlyAo") && keyPressed) {
filter.setUseOnlyAo(!filter.isUseOnlyAo());
System.out.println("use Only AO : " + filter.isUseOnlyAo());
@ -132,7 +138,7 @@ public class SSAOUI {
}
};
inputManager.addListener(acl, "toggleUseAO", "toggleUseOnlyAo", "outputConfig");
inputManager.addListener(acl, "toggleUseAO", "toggleApprox", "toggleUseOnlyAo", "outputConfig");
inputManager.addListener(anl, "sampleRadiusUp", "sampleRadiusDown", "intensityUp", "intensityDown", "scaleUp", "scaleDown",
"biasUp", "biasDown");

@ -83,7 +83,8 @@ public class TestSSAO extends SimpleApplication {
rootNode.attachChild(model);
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
SSAOFilter ssaoFilter = new SSAOFilter(12.940201f, 43.928635f, 0.32999992f, 0.6059958f);
SSAOFilter ssaoFilter = new SSAOFilter(2.9299974f,32.920483f,5.8100376f,0.091000035f);;
ssaoFilter.setApproximateNormals(true);
fpp.addFilter(ssaoFilter);
SSAOUI ui = new SSAOUI(inputManager, ssaoFilter);

@ -0,0 +1,108 @@
/*
* 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 jme3test.post;
import com.jme3.app.SimpleApplication;
import com.jme3.light.*;
import com.jme3.material.Material;
import com.jme3.math.*;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.ssao.SSAOFilter;
import com.jme3.scene.*;
import com.jme3.scene.control.LodControl;
import com.jme3.scene.shape.Box;
import com.jme3.texture.Texture;
public class TestSSAO2 extends SimpleApplication {
Geometry model;
public static void main(String[] args) {
TestSSAO2 app = new TestSSAO2();
app.start();
}
@Override
public void simpleInitApp() {
DirectionalLight dl = new DirectionalLight();
dl.setDirection(new Vector3f(-1,-1,-1).normalizeLocal());
rootNode.addLight(dl);
Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
mat.setFloat("Shininess", 16f);
//mat.setBoolean("VertexLighting", true);
Geometry floor = new Geometry("floor", new Box(1000,0.1f,1000));
floor.setMaterial(mat);
rootNode.attachChild(floor);
Node teapotNode = (Node) assetManager.loadModel("Models/Teapot/Teapot.mesh.xml");
Geometry teapot = (Geometry) teapotNode.getChild(0);
teapot.setMaterial(mat);
// Sphere sph = new Sphere(16, 16, 4);
// Geometry teapot = new Geometry("teapot", sph);
// show normals as material
//Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
for (int f = 10; f > 3; f--) {
for (int y = -f; y < f; y++) {
for (int x = -f; x < f; x++) {
Geometry clonePot = teapot.clone();
//clonePot.setMaterial(mat);
clonePot.setLocalTranslation(x * .5f, 10 - f, y * .5f);
clonePot.setLocalScale(.15f);
rootNode.attachChild(clonePot);
}
}
}
cam.setLocation(new Vector3f(10.247649f, 8.275992f, 10.405156f));
cam.setRotation(new Quaternion(-0.083419204f, 0.90370524f, -0.20599906f, -0.36595422f));
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
SSAOFilter ssaoFilter = new SSAOFilter(2.9299974f,25f,5.8100376f,0.091000035f);
fpp.addFilter(ssaoFilter);
SSAOUI ui = new SSAOUI(inputManager, ssaoFilter);
viewPort.addProcessor(fpp);
}
@Override
public void simpleUpdate(float tpf) {
}
}
Loading…
Cancel
Save