Added a way to approximate the normals for the SSAO filter instead of rendering an additional geometry pass.
This commit is contained in:
parent
46891b32c6
commit
e89e0e7c12
@ -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);
|
||||
|
||||
|
108
jme3-examples/src/main/java/jme3test/post/TestSSAO2.java
Normal file
108
jme3-examples/src/main/java/jme3test/post/TestSSAO2.java
Normal file
@ -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…
x
Reference in New Issue
Block a user