Dropped the use of the irradiance Map for LightProbes, only the spherical harmonics coeffs are stored and used to recompose indirect diffuse color in the shader.
Also added a tweak to get the specular dominant direction when sampling the prefiltered env maps. It gives better result on low roughness materials.
This commit is contained in:
parent
0da2bfe0ba
commit
e6a55e9d3a
@ -31,12 +31,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.environment;
|
package com.jme3.environment;
|
||||||
|
|
||||||
|
import com.jme3.environment.generation.*;
|
||||||
import com.jme3.light.LightProbe;
|
import com.jme3.light.LightProbe;
|
||||||
import com.jme3.environment.generation.JobProgressListener;
|
|
||||||
import com.jme3.environment.generation.PrefilteredEnvMapFaceGenerator;
|
|
||||||
import com.jme3.environment.generation.IrradianceMapGenerator;
|
|
||||||
import com.jme3.environment.util.EnvMapUtils;
|
import com.jme3.environment.util.EnvMapUtils;
|
||||||
import com.jme3.environment.generation.JobProgressAdapter;
|
|
||||||
import com.jme3.app.Application;
|
import com.jme3.app.Application;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
@ -115,7 +112,6 @@ public class LightProbeFactory {
|
|||||||
public static LightProbe makeProbe(final EnvironmentCamera envCam, Spatial scene, final JobProgressListener<LightProbe> listener) {
|
public static LightProbe makeProbe(final EnvironmentCamera envCam, Spatial scene, final JobProgressListener<LightProbe> listener) {
|
||||||
final LightProbe probe = new LightProbe();
|
final LightProbe probe = new LightProbe();
|
||||||
probe.setPosition(envCam.getPosition());
|
probe.setPosition(envCam.getPosition());
|
||||||
probe.setIrradianceMap(EnvMapUtils.createIrradianceMap(envCam.getSize(), envCam.getImageFormat()));
|
|
||||||
probe.setPrefilteredMap(EnvMapUtils.createPrefilteredEnvMap(envCam.getSize(), envCam.getImageFormat()));
|
probe.setPrefilteredMap(EnvMapUtils.createPrefilteredEnvMap(envCam.getSize(), envCam.getImageFormat()));
|
||||||
envCam.snapshot(scene, new JobProgressAdapter<TextureCubeMap>() {
|
envCam.snapshot(scene, new JobProgressAdapter<TextureCubeMap>() {
|
||||||
|
|
||||||
@ -153,15 +149,12 @@ public class LightProbeFactory {
|
|||||||
|
|
||||||
probe.setReady(false);
|
probe.setReady(false);
|
||||||
|
|
||||||
if(probe.getIrradianceMap() != null) {
|
if (probe.getPrefilteredEnvMap() != null) {
|
||||||
probe.getIrradianceMap().getImage().dispose();
|
|
||||||
probe.getPrefilteredEnvMap().getImage().dispose();
|
probe.getPrefilteredEnvMap().getImage().dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
probe.setIrradianceMap(EnvMapUtils.createIrradianceMap(envCam.getSize(), envCam.getImageFormat()));
|
|
||||||
probe.setPrefilteredMap(EnvMapUtils.createPrefilteredEnvMap(envCam.getSize(), envCam.getImageFormat()));
|
probe.setPrefilteredMap(EnvMapUtils.createPrefilteredEnvMap(envCam.getSize(), envCam.getImageFormat()));
|
||||||
|
|
||||||
|
|
||||||
envCam.snapshot(scene, new JobProgressAdapter<TextureCubeMap>() {
|
envCam.snapshot(scene, new JobProgressAdapter<TextureCubeMap>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -174,7 +167,7 @@ public class LightProbeFactory {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Internally called to generate the maps.
|
* Internally called to generate the maps.
|
||||||
* This method will spawn 7 thread (one for the IrradianceMap, and one for each face of the prefiltered env map).
|
* This method will spawn 7 thread (one for the Irradiance spherical harmonics generator, and one for each face of the prefiltered env map).
|
||||||
* Those threads will be executed in a ScheduledThreadPoolExecutor that will be shutdown when the genration is done.
|
* Those threads will be executed in a ScheduledThreadPoolExecutor that will be shutdown when the genration is done.
|
||||||
*
|
*
|
||||||
* @param envMap the raw env map rendered by the env camera
|
* @param envMap the raw env map rendered by the env camera
|
||||||
@ -183,20 +176,20 @@ public class LightProbeFactory {
|
|||||||
* @param listener a progress listener. (can be null if no progress reporting is needed)
|
* @param listener a progress listener. (can be null if no progress reporting is needed)
|
||||||
*/
|
*/
|
||||||
private static void generatePbrMaps(TextureCubeMap envMap, final LightProbe probe, Application app, final JobProgressListener<LightProbe> listener) {
|
private static void generatePbrMaps(TextureCubeMap envMap, final LightProbe probe, Application app, final JobProgressListener<LightProbe> listener) {
|
||||||
IrradianceMapGenerator irrMapGenerator;
|
IrradianceSphericalHarmonicsGenerator irrShGenerator;
|
||||||
PrefilteredEnvMapFaceGenerator[] pemGenerators = new PrefilteredEnvMapFaceGenerator[6];
|
PrefilteredEnvMapFaceGenerator[] pemGenerators = new PrefilteredEnvMapFaceGenerator[6];
|
||||||
|
|
||||||
final JobState jobState = new JobState(new ScheduledThreadPoolExecutor(7));
|
final JobState jobState = new JobState(new ScheduledThreadPoolExecutor(7));
|
||||||
|
|
||||||
irrMapGenerator = new IrradianceMapGenerator(app, new JobListener(listener, jobState, probe, 6));
|
irrShGenerator = new IrradianceSphericalHarmonicsGenerator(app, new JobListener(listener, jobState, probe, 6));
|
||||||
int size = envMap.getImage().getWidth();
|
int size = envMap.getImage().getWidth();
|
||||||
irrMapGenerator.setGenerationParam(EnvMapUtils.duplicateCubeMap(envMap), size, EnvMapUtils.FixSeamsMethod.Wrap, probe.getIrradianceMap());
|
irrShGenerator.setGenerationParam(EnvMapUtils.duplicateCubeMap(envMap), probe);
|
||||||
|
|
||||||
jobState.executor.execute(irrMapGenerator);
|
jobState.executor.execute(irrShGenerator);
|
||||||
|
|
||||||
for (int i = 0; i < pemGenerators.length; i++) {
|
for (int i = 0; i < pemGenerators.length; i++) {
|
||||||
pemGenerators[i] = new PrefilteredEnvMapFaceGenerator(app, i, new JobListener(listener, jobState, probe, i));
|
pemGenerators[i] = new PrefilteredEnvMapFaceGenerator(app, i, new JobListener(listener, jobState, probe, i));
|
||||||
pemGenerators[i].setGenerationParam(EnvMapUtils.duplicateCubeMap(envMap), size, EnvMapUtils.FixSeamsMethod.Wrap, probe.getPrefilteredEnvMap());
|
pemGenerators[i].setGenerationParam(EnvMapUtils.duplicateCubeMap(envMap), size, EnvMapUtils.FixSeamsMethod.None, probe.getPrefilteredEnvMap());
|
||||||
jobState.executor.execute(pemGenerators[i]);
|
jobState.executor.execute(pemGenerators[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,176 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2015 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.environment.generation;
|
|
||||||
|
|
||||||
import com.jme3.environment.util.CubeMapWrapper;
|
|
||||||
import com.jme3.environment.util.EnvMapUtils;
|
|
||||||
import com.jme3.app.Application;
|
|
||||||
import com.jme3.math.ColorRGBA;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.texture.TextureCubeMap;
|
|
||||||
import static com.jme3.environment.util.EnvMapUtils.shBandFactor;
|
|
||||||
import com.jme3.util.BufferUtils;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Generates the Irradiance map for PBR. This job can be lauched from a separate
|
|
||||||
* thread.
|
|
||||||
*
|
|
||||||
* TODO there is a lot of duplicate code here with the EnvMapUtils.
|
|
||||||
*
|
|
||||||
* @author Nehon
|
|
||||||
*/
|
|
||||||
//TODO there is a lot of duplicate code here with the EnvMapUtils. We should,
|
|
||||||
//either leverage the code from the util class either remove it and only allow
|
|
||||||
//parallel generation using this runnable.
|
|
||||||
public class IrradianceMapGenerator extends RunnableWithProgress {
|
|
||||||
|
|
||||||
private int targetMapSize;
|
|
||||||
private EnvMapUtils.FixSeamsMethod fixSeamsMethod;
|
|
||||||
private TextureCubeMap sourceMap;
|
|
||||||
private TextureCubeMap store;
|
|
||||||
private final Application app;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an Irradiance map generator. The app is needed to enqueue the
|
|
||||||
* call to the EnvironmentCamera when the generation is done, so that this
|
|
||||||
* process is thread safe.
|
|
||||||
*
|
|
||||||
* @param app the Application
|
|
||||||
* @param listener
|
|
||||||
*/
|
|
||||||
public IrradianceMapGenerator(Application app, JobProgressListener<Integer> listener) {
|
|
||||||
super(listener);
|
|
||||||
this.app = app;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fills all the genration parameters
|
|
||||||
*
|
|
||||||
* @param sourceMap the source cube map
|
|
||||||
* @param targetMapSize the size of the generated map (width or height in
|
|
||||||
* pixel)
|
|
||||||
* @param fixSeamsMethod the method used to fix seams as described here
|
|
||||||
* {@link EnvMapUtils.FixSeamsMethod}
|
|
||||||
*
|
|
||||||
* @param store The cube map to store the result in.
|
|
||||||
*/
|
|
||||||
public void setGenerationParam(TextureCubeMap sourceMap, int targetMapSize, EnvMapUtils.FixSeamsMethod fixSeamsMethod, TextureCubeMap store) {
|
|
||||||
this.sourceMap = sourceMap;
|
|
||||||
this.targetMapSize = targetMapSize;
|
|
||||||
this.fixSeamsMethod = fixSeamsMethod;
|
|
||||||
this.store = store;
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
app.enqueue(new Callable<Void>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void call() throws Exception {
|
|
||||||
listener.start();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
Vector3f[] shCoeffs = EnvMapUtils.getSphericalHarmonicsCoefficents(sourceMap);
|
|
||||||
store = generateIrradianceMap(shCoeffs, targetMapSize, fixSeamsMethod, store);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
app.enqueue(new Callable<Void>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void call() throws Exception {
|
|
||||||
listener.done(6);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the Irradiance map (used for image based difuse lighting) from
|
|
||||||
* Spherical Harmonics coefficients previously computed with
|
|
||||||
* {@link EnvMapUtils#getSphericalHarmonicsCoefficents(com.jme3.texture.TextureCubeMap)}
|
|
||||||
*
|
|
||||||
* @param shCoeffs the SH coeffs
|
|
||||||
* @param targetMapSize the size of the irradiance map to generate
|
|
||||||
* @param fixSeamsMethod the method to fix seams
|
|
||||||
* @param store
|
|
||||||
* @return The irradiance cube map for the given coefficients
|
|
||||||
*/
|
|
||||||
public TextureCubeMap generateIrradianceMap(Vector3f[] shCoeffs, int targetMapSize, EnvMapUtils.FixSeamsMethod fixSeamsMethod, TextureCubeMap store) {
|
|
||||||
TextureCubeMap irrCubeMap = store;
|
|
||||||
|
|
||||||
setEnd(6 + 6);
|
|
||||||
for (int i = 0; i < 6; i++) {
|
|
||||||
ByteBuffer buf = BufferUtils.createByteBuffer(targetMapSize * targetMapSize * store.getImage().getFormat().getBitsPerPixel() / 8);
|
|
||||||
irrCubeMap.getImage().setData(i, buf);
|
|
||||||
progress();
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3f texelVect = new Vector3f();
|
|
||||||
ColorRGBA color = new ColorRGBA(ColorRGBA.Black);
|
|
||||||
float[] shDir = new float[9];
|
|
||||||
CubeMapWrapper envMapWriter = new CubeMapWrapper(irrCubeMap);
|
|
||||||
for (int face = 0; face < 6; face++) {
|
|
||||||
|
|
||||||
for (int y = 0; y < targetMapSize; y++) {
|
|
||||||
for (int x = 0; x < targetMapSize; x++) {
|
|
||||||
EnvMapUtils.getVectorFromCubemapFaceTexCoord(x, y, targetMapSize, face, texelVect, fixSeamsMethod);
|
|
||||||
EnvMapUtils.evalShBasis(texelVect, shDir);
|
|
||||||
color.set(0, 0, 0, 0);
|
|
||||||
for (int i = 0; i < EnvMapUtils.NUM_SH_COEFFICIENT; i++) {
|
|
||||||
color.set(color.r + shCoeffs[i].x * shDir[i] * shBandFactor[i],
|
|
||||||
color.g + shCoeffs[i].y * shDir[i] * shBandFactor[i],
|
|
||||||
color.b + shCoeffs[i].z * shDir[i] * shBandFactor[i],
|
|
||||||
1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
//clamping the color because very low value close to zero produce artifacts
|
|
||||||
color.r = Math.max(0.0001f, color.r);
|
|
||||||
color.g = Math.max(0.0001f, color.g);
|
|
||||||
color.b = Math.max(0.0001f, color.b);
|
|
||||||
|
|
||||||
envMapWriter.setPixel(x, y, face, color);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
progress();
|
|
||||||
}
|
|
||||||
return irrCubeMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2015 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.environment.generation;
|
||||||
|
|
||||||
|
import com.jme3.app.Application;
|
||||||
|
import com.jme3.environment.util.CubeMapWrapper;
|
||||||
|
import com.jme3.environment.util.EnvMapUtils;
|
||||||
|
import com.jme3.light.LightProbe;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.texture.TextureCubeMap;
|
||||||
|
import com.jme3.util.BufferUtils;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import static com.jme3.environment.util.EnvMapUtils.shBandFactor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the Irradiance map for PBR. This job can be launched from a separate
|
||||||
|
* thread.
|
||||||
|
* <p>
|
||||||
|
* This is not used as we use spherical harmonics directly now, but we may need this code again at some point
|
||||||
|
*
|
||||||
|
* @author Nehon
|
||||||
|
*/
|
||||||
|
public class IrradianceSphericalHarmonicsGenerator extends RunnableWithProgress {
|
||||||
|
|
||||||
|
private TextureCubeMap sourceMap;
|
||||||
|
private LightProbe store;
|
||||||
|
private final Application app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an Irradiance map generator. The app is needed to enqueue the
|
||||||
|
* call to the EnvironmentCamera when the generation is done, so that this
|
||||||
|
* process is thread safe.
|
||||||
|
*
|
||||||
|
* @param app the Application
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
public IrradianceSphericalHarmonicsGenerator(Application app, JobProgressListener<Integer> listener) {
|
||||||
|
super(listener);
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills all the genration parameters
|
||||||
|
*
|
||||||
|
* @param sourceMap the source cube map
|
||||||
|
* {@link EnvMapUtils.FixSeamsMethod}
|
||||||
|
* @param store The cube map to store the result in.
|
||||||
|
*/
|
||||||
|
public void setGenerationParam(TextureCubeMap sourceMap, LightProbe store) {
|
||||||
|
this.sourceMap = sourceMap;
|
||||||
|
|
||||||
|
this.store = store;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
app.enqueue(new Callable<Void>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void call() throws Exception {
|
||||||
|
listener.start();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
Vector3f[] shCoeffs = EnvMapUtils.getSphericalHarmonicsCoefficents(sourceMap);
|
||||||
|
EnvMapUtils.prepareShCoefs(shCoeffs);
|
||||||
|
store.setShCoeffs(shCoeffs);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
app.enqueue(new Callable<Void>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void call() throws Exception {
|
||||||
|
listener.done(6);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -42,19 +42,20 @@ import static com.jme3.math.FastMath.pow;
|
|||||||
import static com.jme3.math.FastMath.sqrt;
|
import static com.jme3.math.FastMath.sqrt;
|
||||||
|
|
||||||
import com.jme3.texture.TextureCubeMap;
|
import com.jme3.texture.TextureCubeMap;
|
||||||
|
|
||||||
import static com.jme3.environment.util.EnvMapUtils.getHammersleyPoint;
|
import static com.jme3.environment.util.EnvMapUtils.getHammersleyPoint;
|
||||||
import static com.jme3.environment.util.EnvMapUtils.getRoughnessFromMip;
|
import static com.jme3.environment.util.EnvMapUtils.getRoughnessFromMip;
|
||||||
import static com.jme3.environment.util.EnvMapUtils.getSampleFromMip;
|
import static com.jme3.environment.util.EnvMapUtils.getSampleFromMip;
|
||||||
import static com.jme3.environment.util.EnvMapUtils.getVectorFromCubemapFaceTexCoord;
|
import static com.jme3.environment.util.EnvMapUtils.getVectorFromCubemapFaceTexCoord;
|
||||||
|
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Generates one face of the prefiltered environnement map for PBR. This job can
|
* Generates one face of the prefiltered environnement map for PBR. This job can
|
||||||
* be lauched from a separate thread.
|
* be lauched from a separate thread.
|
||||||
*
|
* <p>
|
||||||
* TODO there is a lot of duplicate code here with the EnvMapUtils.
|
* TODO there is a lot of duplicate code here with the EnvMapUtils.
|
||||||
*
|
*
|
||||||
* @author Nehon
|
* @author Nehon
|
||||||
@ -96,7 +97,6 @@ public class PrefilteredEnvMapFaceGenerator extends RunnableWithProgress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fills all the genration parameters
|
* Fills all the genration parameters
|
||||||
*
|
*
|
||||||
@ -105,7 +105,6 @@ public class PrefilteredEnvMapFaceGenerator extends RunnableWithProgress {
|
|||||||
* pixel)
|
* pixel)
|
||||||
* @param fixSeamsMethod the method used to fix seams as described here
|
* @param fixSeamsMethod the method used to fix seams as described here
|
||||||
* {@link EnvMapUtils.FixSeamsMethod}
|
* {@link EnvMapUtils.FixSeamsMethod}
|
||||||
*
|
|
||||||
* @param store The cube map to store the result in.
|
* @param store The cube map to store the result in.
|
||||||
*/
|
*/
|
||||||
public void setGenerationParam(TextureCubeMap sourceMap, int targetMapSize, EnvMapUtils.FixSeamsMethod fixSeamsMethod, TextureCubeMap store) {
|
public void setGenerationParam(TextureCubeMap sourceMap, int targetMapSize, EnvMapUtils.FixSeamsMethod fixSeamsMethod, TextureCubeMap store) {
|
||||||
@ -116,7 +115,7 @@ public class PrefilteredEnvMapFaceGenerator extends RunnableWithProgress {
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init(){
|
private void init() {
|
||||||
Xi.set(0, 0, 0, 0);
|
Xi.set(0, 0, 0, 0);
|
||||||
H.set(0, 0, 0);
|
H.set(0, 0, 0);
|
||||||
tmp.set(0, 0, 0);
|
tmp.set(0, 0, 0);
|
||||||
@ -184,10 +183,10 @@ public class PrefilteredEnvMapFaceGenerator extends RunnableWithProgress {
|
|||||||
for (int y = 0; y < targetMipMapSize; y++) {
|
for (int y = 0; y < targetMipMapSize; y++) {
|
||||||
for (int x = 0; x < targetMipMapSize; x++) {
|
for (int x = 0; x < targetMipMapSize; x++) {
|
||||||
color.set(0, 0, 0);
|
color.set(0, 0, 0);
|
||||||
getVectorFromCubemapFaceTexCoord(x, y, targetMipMapSize, face, texelVect, EnvMapUtils.FixSeamsMethod.None);
|
getVectorFromCubemapFaceTexCoord(x, y, targetMipMapSize, face, texelVect, fixSeamsMethod);
|
||||||
prefilterEnvMapTexel(sourceWrapper, roughness, texelVect, nbSamples, color);
|
prefilterEnvMapTexel(sourceWrapper, roughness, texelVect, nbSamples, color);
|
||||||
|
|
||||||
outColor.set(Math.max(color.x, 0.0001f), Math.max(color.y,0.0001f), Math.max(color.z, 0.0001f), 1);
|
outColor.set(Math.max(color.x, 0.0001f), Math.max(color.y, 0.0001f), Math.max(color.z, 0.0001f), 1);
|
||||||
log.log(Level.FINE, "coords {0},{1}", new Object[]{x, y});
|
log.log(Level.FINE, "coords {0},{1}", new Object[]{x, y});
|
||||||
targetWrapper.setPixel(x, y, face, mipLevel, outColor);
|
targetWrapper.setPixel(x, y, face, mipLevel, outColor);
|
||||||
|
|
||||||
|
@ -59,6 +59,12 @@ import com.jme3.util.TempVars;
|
|||||||
*/
|
*/
|
||||||
public class EnvMapUtils {
|
public class EnvMapUtils {
|
||||||
|
|
||||||
|
|
||||||
|
private static final float sqrtPi = sqrt(PI);
|
||||||
|
private static final float sqrt3Pi = sqrt(3f / PI);
|
||||||
|
private static final float sqrt5Pi = sqrt(5f / PI);
|
||||||
|
private static final float sqrt15Pi = sqrt(15f / PI);
|
||||||
|
|
||||||
public final static int NUM_SH_COEFFICIENT = 9;
|
public final static int NUM_SH_COEFFICIENT = 9;
|
||||||
// See Peter-Pike Sloan paper for these coefficients
|
// See Peter-Pike Sloan paper for these coefficients
|
||||||
//http://www.ppsloan.org/publications/StupidSH36.pdf
|
//http://www.ppsloan.org/publications/StupidSH36.pdf
|
||||||
@ -79,7 +85,7 @@ public class EnvMapUtils {
|
|||||||
/**
|
/**
|
||||||
* No seams fix
|
* No seams fix
|
||||||
*/
|
*/
|
||||||
None;
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,18 +117,6 @@ public class EnvMapUtils {
|
|||||||
cubeImage.addData(backImg.getData(0));
|
cubeImage.addData(backImg.getData(0));
|
||||||
cubeImage.addData(frontImg.getData(0));
|
cubeImage.addData(frontImg.getData(0));
|
||||||
|
|
||||||
if (leftImg.getEfficentData() != null) {
|
|
||||||
// also consilidate efficient data
|
|
||||||
ArrayList<Object> efficientData = new ArrayList<Object>(6);
|
|
||||||
efficientData.add(rightImg.getEfficentData());
|
|
||||||
efficientData.add(leftImg.getEfficentData());
|
|
||||||
efficientData.add(upImg.getEfficentData());
|
|
||||||
efficientData.add(downImg.getEfficentData());
|
|
||||||
efficientData.add(backImg.getEfficentData());
|
|
||||||
efficientData.add(frontImg.getEfficentData());
|
|
||||||
cubeImage.setEfficentData(efficientData);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureCubeMap cubeMap = new TextureCubeMap(cubeImage);
|
TextureCubeMap cubeMap = new TextureCubeMap(cubeImage);
|
||||||
cubeMap.setAnisotropicFilter(0);
|
cubeMap.setAnisotropicFilter(0);
|
||||||
cubeMap.setMagFilter(Texture.MagFilter.Bilinear);
|
cubeMap.setMagFilter(Texture.MagFilter.Bilinear);
|
||||||
@ -154,13 +148,6 @@ public class EnvMapUtils {
|
|||||||
cubeImage.addData(d.duplicate());
|
cubeImage.addData(d.duplicate());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (srcImg.getEfficentData() != null) {
|
|
||||||
// also consilidate efficient data
|
|
||||||
ArrayList<Object> efficientData = new ArrayList<Object>(6);
|
|
||||||
efficientData.add(srcImg.getEfficentData());
|
|
||||||
cubeImage.setEfficentData(efficientData);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureCubeMap cubeMap = new TextureCubeMap(cubeImage);
|
TextureCubeMap cubeMap = new TextureCubeMap(cubeImage);
|
||||||
cubeMap.setAnisotropicFilter(sourceMap.getAnisotropicFilter());
|
cubeMap.setAnisotropicFilter(sourceMap.getAnisotropicFilter());
|
||||||
cubeMap.setMagFilter(sourceMap.getMagFilter());
|
cubeMap.setMagFilter(sourceMap.getMagFilter());
|
||||||
@ -196,7 +183,7 @@ public class EnvMapUtils {
|
|||||||
static float getSolidAngleAndVector(int x, int y, int mapSize, int face, Vector3f store, FixSeamsMethod fixSeamsMethod) {
|
static float getSolidAngleAndVector(int x, int y, int mapSize, int face, Vector3f store, FixSeamsMethod fixSeamsMethod) {
|
||||||
|
|
||||||
if (store == null) {
|
if (store == null) {
|
||||||
throw new IllegalArgumentException("the store parameter ust not be null");
|
throw new IllegalArgumentException("the store parameter must not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* transform from [0..res - 1] to [- (1 - 1 / res) .. (1 - 1 / res)]
|
/* transform from [0..res - 1] to [- (1 - 1 / res) .. (1 - 1 / res)]
|
||||||
@ -438,7 +425,7 @@ public class EnvMapUtils {
|
|||||||
float weight;
|
float weight;
|
||||||
|
|
||||||
if (cubeMap.getImage().getData(0) == null) {
|
if (cubeMap.getImage().getData(0) == null) {
|
||||||
throw new IllegalStateException("The cube map must contain Efficient data, if you rendered the cube map on the GPU plase use renderer.readFrameBuffer, to create a CPU image");
|
throw new IllegalStateException("The cube map must contain Efficient data, if you rendered the cube map on the GPU please use renderer.readFrameBuffer, to create a CPU image");
|
||||||
}
|
}
|
||||||
|
|
||||||
int width = cubeMap.getImage().getWidth();
|
int width = cubeMap.getImage().getWidth();
|
||||||
@ -495,12 +482,6 @@ public class EnvMapUtils {
|
|||||||
float yV = texelVect.y;
|
float yV = texelVect.y;
|
||||||
float zV = texelVect.z;
|
float zV = texelVect.z;
|
||||||
|
|
||||||
float pi = PI;
|
|
||||||
float sqrtPi = sqrt(pi);
|
|
||||||
float sqrt3Pi = sqrt(3f / pi);
|
|
||||||
float sqrt5Pi = sqrt(5f / pi);
|
|
||||||
float sqrt15Pi = sqrt(15f / pi);
|
|
||||||
|
|
||||||
float x2 = xV * xV;
|
float x2 = xV * xV;
|
||||||
float y2 = yV * yV;
|
float y2 = yV * yV;
|
||||||
float z2 = zV * zV;
|
float z2 = zV * zV;
|
||||||
@ -514,9 +495,32 @@ public class EnvMapUtils {
|
|||||||
shDir[6] = (sqrt5Pi * (-1f + 3f * z2)) / 4f;
|
shDir[6] = (sqrt5Pi * (-1f + 3f * z2)) / 4f;
|
||||||
shDir[7] = -(sqrt15Pi * xV * zV) / 2f;
|
shDir[7] = -(sqrt15Pi * xV * zV) / 2f;
|
||||||
shDir[8] = sqrt15Pi * (x2 - y2) / 4f;
|
shDir[8] = sqrt15Pi * (x2 - y2) / 4f;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void prepareShCoefs(Vector3f[] shCoefs) {
|
||||||
|
|
||||||
|
float coef0 = (1f / (2f * sqrtPi));
|
||||||
|
float coef1 = -sqrt3Pi / 2f;
|
||||||
|
float coef2 = -coef1;
|
||||||
|
float coef3 = coef1;
|
||||||
|
float coef4 = sqrt15Pi / 2f;
|
||||||
|
float coef5 = -coef4;
|
||||||
|
float coef6 = sqrt5Pi / 4f;
|
||||||
|
float coef7 = coef5;
|
||||||
|
float coef8 = sqrt15Pi / 4f;
|
||||||
|
|
||||||
|
shCoefs[0].multLocal(coef0);
|
||||||
|
shCoefs[1].multLocal(coef1);
|
||||||
|
shCoefs[2].multLocal(coef2);
|
||||||
|
shCoefs[3].multLocal(coef3);
|
||||||
|
shCoefs[4].multLocal(coef4);
|
||||||
|
shCoefs[5].multLocal(coef5);
|
||||||
|
shCoefs[6].multLocal(coef6);
|
||||||
|
shCoefs[7].multLocal(coef7);
|
||||||
|
shCoefs[8].multLocal(coef8);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Vector4f getHammersleyPoint(int i, final int nbrSample, Vector4f store) {
|
public static Vector4f getHammersleyPoint(int i, final int nbrSample, Vector4f store) {
|
||||||
if (store == null) {
|
if (store == null) {
|
||||||
store = new Vector4f();
|
store = new Vector4f();
|
||||||
@ -734,3 +738,4 @@ public class EnvMapUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,26 +61,10 @@ public class LightsDebugState extends BaseAppState {
|
|||||||
private Geometry debugGeom;
|
private Geometry debugGeom;
|
||||||
private Geometry debugBounds;
|
private Geometry debugBounds;
|
||||||
private Material debugMaterial;
|
private Material debugMaterial;
|
||||||
private DebugMode debugMode = DebugMode.PrefilteredEnvMap;
|
|
||||||
private float probeScale = 1.0f;
|
private float probeScale = 1.0f;
|
||||||
private Spatial scene = null;
|
private Spatial scene = null;
|
||||||
private final List<LightProbe> probes = new ArrayList<LightProbe>();
|
private final List<LightProbe> probes = new ArrayList<LightProbe>();
|
||||||
|
|
||||||
/**
|
|
||||||
* Debug mode for light probes
|
|
||||||
*/
|
|
||||||
public enum DebugMode {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays the prefiltered env maps on the debug sphere
|
|
||||||
*/
|
|
||||||
PrefilteredEnvMap,
|
|
||||||
/**
|
|
||||||
* displays the Irradiance map on the debug sphere
|
|
||||||
*/
|
|
||||||
IrradianceMap
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initialize(Application app) {
|
protected void initialize(Application app) {
|
||||||
debugNode = new Node("Environment debug Node");
|
debugNode = new Node("Environment debug Node");
|
||||||
@ -114,12 +98,8 @@ public class LightsDebugState extends BaseAppState {
|
|||||||
Material m = probeGeom.getMaterial();
|
Material m = probeGeom.getMaterial();
|
||||||
probeGeom.setLocalScale(probeScale);
|
probeGeom.setLocalScale(probeScale);
|
||||||
if (probe.isReady()) {
|
if (probe.isReady()) {
|
||||||
if (debugMode == DebugMode.IrradianceMap) {
|
|
||||||
m.setTexture("CubeMap", probe.getIrradianceMap());
|
|
||||||
} else {
|
|
||||||
m.setTexture("CubeMap", probe.getPrefilteredEnvMap());
|
m.setTexture("CubeMap", probe.getPrefilteredEnvMap());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
n.setLocalTranslation(probe.getPosition());
|
n.setLocalTranslation(probe.getPosition());
|
||||||
n.getChild(1).setLocalScale(((BoundingSphere) probe.getBounds()).getRadius());
|
n.getChild(1).setLocalScale(((BoundingSphere) probe.getBounds()).getRadius());
|
||||||
break;
|
break;
|
||||||
@ -161,24 +141,6 @@ public class LightsDebugState extends BaseAppState {
|
|||||||
rm.renderScene(debugNode, getApplication().getViewPort());
|
rm.renderScene(debugNode, getApplication().getViewPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see DebugMode
|
|
||||||
* @return the debug mode
|
|
||||||
*/
|
|
||||||
public DebugMode getDebugMode() {
|
|
||||||
return debugMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sets the debug mode
|
|
||||||
* @see DebugMode
|
|
||||||
* @param debugMode the debug mode
|
|
||||||
*/
|
|
||||||
public void setDebugMode(DebugMode debugMode) {
|
|
||||||
this.debugMode = debugMode;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the scale of the probe's debug sphere
|
* returns the scale of the probe's debug sphere
|
||||||
|
@ -50,17 +50,19 @@ import com.jme3.scene.Spatial;
|
|||||||
import com.jme3.texture.TextureCubeMap;
|
import com.jme3.texture.TextureCubeMap;
|
||||||
import com.jme3.util.TempVars;
|
import com.jme3.util.TempVars;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A LightProbe is not exactly a light. It holds environment map information used for Image Based Lighting.
|
* A LightProbe is not exactly a light. It holds environment map information used for Image Based Lighting.
|
||||||
* This is used for indirect lighting in the Physically Based Rendering pipeline.
|
* This is used for indirect lighting in the Physically Based Rendering pipeline.
|
||||||
*
|
*
|
||||||
* A light probe has a position in world space. This is the position from where the Environment Map are rendered.
|
* A light probe has a position in world space. This is the position from where the Environment Map are rendered.
|
||||||
* There are two environment maps held by the LightProbe :
|
* There are two environment data structure held by the LightProbe :
|
||||||
* - The irradiance map (used for indirect diffuse lighting in the PBR pipeline).
|
* - The irradiance spherical harmonics factors (used for indirect diffuse lighting in the PBR pipeline).
|
||||||
* - The prefiltered environment map (used for indirect specular lighting and reflection in the PBE pipeline).
|
* - The prefiltered environment map (used for indirect specular lighting and reflection in the PBE pipeline).
|
||||||
* Note that when instanciating the LightProbe, both those maps are null.
|
* Note that when instantiating the LightProbe, both of those structures are null.
|
||||||
* To render them see {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node)}
|
* To compute them see {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node)}
|
||||||
* and {@link EnvironmentCamera}.
|
* and {@link EnvironmentCamera}.
|
||||||
*
|
*
|
||||||
* The light probe has an area of effect that is a bounding volume centered on its position. (for now only Bounding spheres are supported).
|
* The light probe has an area of effect that is a bounding volume centered on its position. (for now only Bounding spheres are supported).
|
||||||
@ -75,7 +77,9 @@ import java.io.IOException;
|
|||||||
*/
|
*/
|
||||||
public class LightProbe extends Light implements Savable {
|
public class LightProbe extends Light implements Savable {
|
||||||
|
|
||||||
private TextureCubeMap irradianceMap;
|
private static final Logger logger = Logger.getLogger(LightProbe.class.getName());
|
||||||
|
|
||||||
|
private Vector3f[] shCoeffs;
|
||||||
private TextureCubeMap prefilteredEnvMap;
|
private TextureCubeMap prefilteredEnvMap;
|
||||||
private BoundingVolume bounds = new BoundingSphere(1.0f, Vector3f.ZERO);
|
private BoundingVolume bounds = new BoundingSphere(1.0f, Vector3f.ZERO);
|
||||||
private boolean ready = false;
|
private boolean ready = false;
|
||||||
@ -90,23 +94,6 @@ public class LightProbe extends Light implements Savable {
|
|||||||
public LightProbe() {
|
public LightProbe() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the irradiance map texture of this Light probe.
|
|
||||||
* Note that this Texture may not have image data yet if the LightProbe is not ready
|
|
||||||
* @return the irradiance map
|
|
||||||
*/
|
|
||||||
public TextureCubeMap getIrradianceMap() {
|
|
||||||
return irradianceMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the irradiance map
|
|
||||||
* @param irradianceMap the irradiance map
|
|
||||||
*/
|
|
||||||
public void setIrradianceMap(TextureCubeMap irradianceMap) {
|
|
||||||
this.irradianceMap = irradianceMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the prefiltered environment map texture of this light probe
|
* returns the prefiltered environment map texture of this light probe
|
||||||
* Note that this Texture may not have image data yet if the LightProbe is not ready
|
* Note that this Texture may not have image data yet if the LightProbe is not ready
|
||||||
@ -128,7 +115,7 @@ public class LightProbe extends Light implements Savable {
|
|||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
super.write(ex);
|
super.write(ex);
|
||||||
OutputCapsule oc = ex.getCapsule(this);
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
oc.write(irradianceMap, "irradianceMap", null);
|
oc.write(shCoeffs, "shCoeffs", null);
|
||||||
oc.write(prefilteredEnvMap, "prefilteredEnvMap", null);
|
oc.write(prefilteredEnvMap, "prefilteredEnvMap", null);
|
||||||
oc.write(position, "position", null);
|
oc.write(position, "position", null);
|
||||||
oc.write(bounds, "bounds", new BoundingSphere(1.0f, Vector3f.ZERO));
|
oc.write(bounds, "bounds", new BoundingSphere(1.0f, Vector3f.ZERO));
|
||||||
@ -139,11 +126,15 @@ public class LightProbe extends Light implements Savable {
|
|||||||
public void read(JmeImporter im) throws IOException {
|
public void read(JmeImporter im) throws IOException {
|
||||||
super.read(im);
|
super.read(im);
|
||||||
InputCapsule ic = im.getCapsule(this);
|
InputCapsule ic = im.getCapsule(this);
|
||||||
irradianceMap = (TextureCubeMap) ic.readSavable("irradianceMap", null);
|
shCoeffs = (Vector3f[]) ic.readSavableArray("shCoeffs", null);
|
||||||
prefilteredEnvMap = (TextureCubeMap) ic.readSavable("prefilteredEnvMap", null);
|
prefilteredEnvMap = (TextureCubeMap) ic.readSavable("prefilteredEnvMap", null);
|
||||||
position = (Vector3f) ic.readSavable("position", this);
|
position = (Vector3f) ic.readSavable("position", this);
|
||||||
bounds = (BoundingVolume) ic.readSavable("bounds", new BoundingSphere(1.0f, Vector3f.ZERO));
|
bounds = (BoundingVolume) ic.readSavable("bounds", new BoundingSphere(1.0f, Vector3f.ZERO));
|
||||||
ready = ic.readBoolean("ready", false);
|
ready = ic.readBoolean("ready", false);
|
||||||
|
if (shCoeffs == null) {
|
||||||
|
ready = false;
|
||||||
|
logger.log(Level.WARNING, "LightProbe is missing parameters, it should be recomputed. Please use lightProbeFactory.updateProbe()");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -195,14 +186,11 @@ public class LightProbe extends Light implements Savable {
|
|||||||
*/
|
*/
|
||||||
public Node getDebugGui(AssetManager manager) {
|
public Node getDebugGui(AssetManager manager) {
|
||||||
if (!ready) {
|
if (!ready) {
|
||||||
throw new UnsupportedOperationException("This EnvProbeis not ready yet, try to test isReady()");
|
throw new UnsupportedOperationException("This EnvProbe is not ready yet, try to test isReady()");
|
||||||
}
|
}
|
||||||
if (debugNode == null) {
|
if (debugNode == null) {
|
||||||
debugNode = new Node("debug gui probe");
|
debugNode = new Node("debug gui probe");
|
||||||
Node debugPfemCm = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(getPrefilteredEnvMap(), manager);
|
Node debugPfemCm = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(getPrefilteredEnvMap(), manager);
|
||||||
Node debugIrrCm = EnvMapUtils.getCubeMapCrossDebugView(getIrradianceMap(), manager);
|
|
||||||
|
|
||||||
debugNode.attachChild(debugIrrCm);
|
|
||||||
debugNode.attachChild(debugPfemCm);
|
debugNode.attachChild(debugPfemCm);
|
||||||
debugPfemCm.setLocalTranslation(520, 0, 0);
|
debugPfemCm.setLocalTranslation(520, 0, 0);
|
||||||
}
|
}
|
||||||
@ -210,6 +198,14 @@ public class LightProbe extends Light implements Savable {
|
|||||||
return debugNode;
|
return debugNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vector3f[] getShCoeffs() {
|
||||||
|
return shCoeffs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShCoeffs(Vector3f[] shCoeffs) {
|
||||||
|
this.shCoeffs = shCoeffs;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the position of the LightProbe in world space
|
* Returns the position of the LightProbe in world space
|
||||||
* @return the wolrd space position
|
* @return the wolrd space position
|
||||||
|
@ -115,7 +115,9 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
|
|||||||
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
|
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
|
||||||
Uniform lightProbeData = shader.getUniform("g_LightProbeData");
|
Uniform lightProbeData = shader.getUniform("g_LightProbeData");
|
||||||
lightProbeData.setVector4Length(1);
|
lightProbeData.setVector4Length(1);
|
||||||
Uniform lightProbeIrrMap = shader.getUniform("g_IrradianceMap");
|
|
||||||
|
//TODO These 2 uniforms should be packed in an array, to ba able to have several probes and blend between them.
|
||||||
|
Uniform shCoeffs = shader.getUniform("g_ShCoeffs");
|
||||||
Uniform lightProbePemMap = shader.getUniform("g_PrefEnvMap");
|
Uniform lightProbePemMap = shader.getUniform("g_PrefEnvMap");
|
||||||
|
|
||||||
lightProbe = null;
|
lightProbe = null;
|
||||||
@ -132,12 +134,9 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
|
|||||||
if(lightProbe != null){
|
if(lightProbe != null){
|
||||||
BoundingSphere s = (BoundingSphere)lightProbe.getBounds();
|
BoundingSphere s = (BoundingSphere)lightProbe.getBounds();
|
||||||
lightProbeData.setVector4InArray(lightProbe.getPosition().x, lightProbe.getPosition().y, lightProbe.getPosition().z, 1f / s.getRadius() + lightProbe.getNbMipMaps(), 0);
|
lightProbeData.setVector4InArray(lightProbe.getPosition().x, lightProbe.getPosition().y, lightProbe.getPosition().z, 1f / s.getRadius() + lightProbe.getNbMipMaps(), 0);
|
||||||
|
shCoeffs.setValue(VarType.Vector3Array, lightProbe.getShCoeffs());
|
||||||
//assigning new texture indexes
|
//assigning new texture indexes
|
||||||
int irrUnit = lastTexUnit++;
|
|
||||||
int pemUnit = lastTexUnit++;
|
int pemUnit = lastTexUnit++;
|
||||||
|
|
||||||
rm.getRenderer().setTexture(irrUnit, lightProbe.getIrradianceMap());
|
|
||||||
lightProbeIrrMap.setValue(VarType.Int, irrUnit);
|
|
||||||
rm.getRenderer().setTexture(pemUnit, lightProbe.getPrefilteredEnvMap());
|
rm.getRenderer().setTexture(pemUnit, lightProbe.getPrefilteredEnvMap());
|
||||||
lightProbePemMap.setValue(VarType.Int, pemUnit);
|
lightProbePemMap.setValue(VarType.Int, pemUnit);
|
||||||
} else {
|
} else {
|
||||||
|
@ -24,7 +24,7 @@ varying vec3 wPosition;
|
|||||||
#ifdef INDIRECT_LIGHTING
|
#ifdef INDIRECT_LIGHTING
|
||||||
// uniform sampler2D m_IntegrateBRDF;
|
// uniform sampler2D m_IntegrateBRDF;
|
||||||
uniform samplerCube g_PrefEnvMap;
|
uniform samplerCube g_PrefEnvMap;
|
||||||
uniform samplerCube g_IrradianceMap;
|
uniform vec3 g_ShCoeffs[9];
|
||||||
uniform vec4 g_LightProbeData;
|
uniform vec4 g_LightProbeData;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -257,9 +257,9 @@ void main(){
|
|||||||
|
|
||||||
vec3 indirectDiffuse = vec3(0.0);
|
vec3 indirectDiffuse = vec3(0.0);
|
||||||
vec3 indirectSpecular = vec3(0.0);
|
vec3 indirectSpecular = vec3(0.0);
|
||||||
indirectDiffuse = textureCube(g_IrradianceMap, normal.xyz).rgb * diffuseColor.rgb;
|
indirectDiffuse = sphericalHarmonics(normal.xyz, g_ShCoeffs) * diffuseColor.rgb;
|
||||||
|
vec3 dominantR = getSpecularDominantDir( normal, rv.xyz, Roughness*Roughness );
|
||||||
indirectSpecular = ApproximateSpecularIBLPolynomial(g_PrefEnvMap, specularColor.rgb, Roughness, ndotv, rv.xyz, nbMipMaps);
|
indirectSpecular = ApproximateSpecularIBLPolynomial(g_PrefEnvMap, specularColor.rgb, Roughness, ndotv, dominantR, nbMipMaps);
|
||||||
indirectSpecular *= vec3(horiz);
|
indirectSpecular *= vec3(horiz);
|
||||||
|
|
||||||
vec3 indirectLighting = indirectDiffuse + indirectSpecular;
|
vec3 indirectLighting = indirectDiffuse + indirectSpecular;
|
||||||
|
@ -53,6 +53,29 @@ void PBR_ComputeDirectLightSpecWF(vec3 normal, vec3 lightDir, vec3 viewDir,
|
|||||||
outSpecular = fresnel * vec3(specular) * lightColor;
|
outSpecular = fresnel * vec3(specular) * lightColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec3 sphericalHarmonics( const in vec3 normal, const vec3 sph[9] ){
|
||||||
|
float x = normal.x;
|
||||||
|
float y = normal.y;
|
||||||
|
float z = normal.z;
|
||||||
|
|
||||||
|
vec3 result = (
|
||||||
|
sph[0] +
|
||||||
|
|
||||||
|
sph[1] * y +
|
||||||
|
sph[2] * z +
|
||||||
|
sph[3] * x +
|
||||||
|
|
||||||
|
sph[4] * y * x +
|
||||||
|
sph[5] * y * z +
|
||||||
|
sph[6] * (3.0 * z * z - 1.0) +
|
||||||
|
sph[7] * (z * x) +
|
||||||
|
sph[8] * (x*x - y*y)
|
||||||
|
);
|
||||||
|
|
||||||
|
return max(result, vec3(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void PBR_ComputeDirectLight(vec3 normal, vec3 lightDir, vec3 viewDir,
|
void PBR_ComputeDirectLight(vec3 normal, vec3 lightDir, vec3 viewDir,
|
||||||
vec3 lightColor, float fZero, float roughness, float ndotv,
|
vec3 lightColor, float fZero, float roughness, float ndotv,
|
||||||
out vec3 outDiffuse, out vec3 outSpecular){
|
out vec3 outDiffuse, out vec3 outSpecular){
|
||||||
@ -115,6 +138,18 @@ vec3 integrateBRDFApprox( const in vec3 specular, float roughness, float NoV ){
|
|||||||
return specular * AB.x + AB.y;
|
return specular * AB.x + AB.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// from Sebastien Lagarde https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf page 69
|
||||||
|
vec3 getSpecularDominantDir(const in vec3 N, const in vec3 R, const in float realRoughness){
|
||||||
|
vec3 dominant;
|
||||||
|
|
||||||
|
float smoothness = 1.0 - realRoughness;
|
||||||
|
float lerpFactor = smoothness * (sqrt(smoothness) + realRoughness);
|
||||||
|
// The result is not normalized as we fetch in a cubemap
|
||||||
|
dominant = mix(N, R, lerpFactor);
|
||||||
|
|
||||||
|
return dominant;
|
||||||
|
}
|
||||||
|
|
||||||
vec3 ApproximateSpecularIBL(samplerCube envMap,sampler2D integrateBRDF, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec, float nbMipMaps){
|
vec3 ApproximateSpecularIBL(samplerCube envMap,sampler2D integrateBRDF, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec, float nbMipMaps){
|
||||||
float Lod = sqrt( Roughness ) * (nbMipMaps - 1.0);
|
float Lod = sqrt( Roughness ) * (nbMipMaps - 1.0);
|
||||||
vec3 PrefilteredColor = textureCubeLod(envMap, refVec.xyz,Lod).rgb;
|
vec3 PrefilteredColor = textureCubeLod(envMap, refVec.xyz,Lod).rgb;
|
||||||
|
@ -73,7 +73,6 @@ public class TestPBRLighting extends SimpleApplication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Node tex;
|
private Node tex;
|
||||||
private Node tex2;
|
|
||||||
|
|
||||||
private Geometry model;
|
private Geometry model;
|
||||||
private DirectionalLight dl;
|
private DirectionalLight dl;
|
||||||
@ -94,7 +93,7 @@ public class TestPBRLighting extends SimpleApplication {
|
|||||||
|
|
||||||
dl = new DirectionalLight();
|
dl = new DirectionalLight();
|
||||||
dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
|
dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
|
||||||
// rootNode.addLight(dl);
|
rootNode.addLight(dl);
|
||||||
dl.setColor(ColorRGBA.White);
|
dl.setColor(ColorRGBA.White);
|
||||||
rootNode.attachChild(modelNode);
|
rootNode.attachChild(modelNode);
|
||||||
|
|
||||||
@ -103,7 +102,7 @@ public class TestPBRLighting extends SimpleApplication {
|
|||||||
// fpp.addFilter(new FXAAFilter());
|
// fpp.addFilter(new FXAAFilter());
|
||||||
fpp.addFilter(new ToneMapFilter(Vector3f.UNIT_XYZ.mult(4.0f)));
|
fpp.addFilter(new ToneMapFilter(Vector3f.UNIT_XYZ.mult(4.0f)));
|
||||||
// fpp.addFilter(new SSAOFilter(0.5f, 3, 0.2f, 0.2f));
|
// fpp.addFilter(new SSAOFilter(0.5f, 3, 0.2f, 0.2f));
|
||||||
// viewPort.addProcessor(fpp);
|
viewPort.addProcessor(fpp);
|
||||||
|
|
||||||
//Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Sky_Cloudy.hdr", SkyFactory.EnvMapType.EquirectMap);
|
//Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Sky_Cloudy.hdr", SkyFactory.EnvMapType.EquirectMap);
|
||||||
Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap);
|
Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap);
|
||||||
@ -143,13 +142,10 @@ public class TestPBRLighting extends SimpleApplication {
|
|||||||
if (tex == null) {
|
if (tex == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (tex.getParent() == null && tex2.getParent() == null) {
|
if (tex.getParent() == null) {
|
||||||
guiNode.attachChild(tex);
|
guiNode.attachChild(tex);
|
||||||
} else if (tex2.getParent() == null) {
|
|
||||||
tex.removeFromParent();
|
|
||||||
guiNode.attachChild(tex2);
|
|
||||||
} else {
|
} else {
|
||||||
tex2.removeFromParent();
|
tex.removeFromParent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,6 +191,7 @@ public class TestPBRLighting extends SimpleApplication {
|
|||||||
|
|
||||||
MaterialDebugAppState debug = new MaterialDebugAppState();
|
MaterialDebugAppState debug = new MaterialDebugAppState();
|
||||||
debug.registerBinding("Common/MatDefs/Light/PBRLighting.frag", rootNode);
|
debug.registerBinding("Common/MatDefs/Light/PBRLighting.frag", rootNode);
|
||||||
|
debug.registerBinding("Common/ShaderLib/PBR.glsllib", rootNode);
|
||||||
getStateManager().attach(debug);
|
getStateManager().attach(debug);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -211,7 +208,6 @@ public class TestPBRLighting extends SimpleApplication {
|
|||||||
public void done(LightProbe result) {
|
public void done(LightProbe result) {
|
||||||
System.err.println("Done rendering env maps");
|
System.err.println("Done rendering env maps");
|
||||||
tex = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(result.getPrefilteredEnvMap(), assetManager);
|
tex = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(result.getPrefilteredEnvMap(), assetManager);
|
||||||
tex2 = EnvMapUtils.getCubeMapCrossDebugView(result.getIrradianceMap(), assetManager);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
((BoundingSphere) probe.getBounds()).setRadius(100);
|
((BoundingSphere) probe.getBounds()).setRadius(100);
|
||||||
|
@ -306,16 +306,7 @@ public class TestPbrEnv extends SimpleApplication implements ActionListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (name.equals("debugProbe") && keyPressed) {
|
if (name.equals("debugProbe") && keyPressed) {
|
||||||
//getStateManager().getState(EnvironmentCamera.class).toggleDebug();
|
debugState.setEnabled(!debugState.isEnabled());
|
||||||
if (!debugState.isEnabled()) {
|
|
||||||
debugState.setEnabled(true);
|
|
||||||
debugState.setDebugMode(LightsDebugState.DebugMode.IrradianceMap);
|
|
||||||
} else if (debugState.getDebugMode() == LightsDebugState.DebugMode.IrradianceMap) {
|
|
||||||
debugState.setDebugMode(LightsDebugState.DebugMode.PrefilteredEnvMap);
|
|
||||||
} else if (debugState.getDebugMode() == LightsDebugState.DebugMode.PrefilteredEnvMap) {
|
|
||||||
debugState.setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.equals("debugTex") && keyPressed) {
|
if (name.equals("debugTex") && keyPressed) {
|
||||||
|
@ -54,7 +54,7 @@ public class TestGltfLoading extends SimpleApplication {
|
|||||||
Node probeNode;
|
Node probeNode;
|
||||||
float time = 0;
|
float time = 0;
|
||||||
int assetIndex = 0;
|
int assetIndex = 0;
|
||||||
boolean useAutoRotate = true;
|
boolean useAutoRotate = false;
|
||||||
private final static String indentString = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
|
private final static String indentString = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
|
||||||
int duration = 2;
|
int duration = 2;
|
||||||
boolean playAnim = true;
|
boolean playAnim = true;
|
||||||
@ -105,25 +105,27 @@ public class TestGltfLoading extends SimpleApplication {
|
|||||||
// PointLight pl1 = new PointLight(new Vector3f(-5.0f, -5.0f, -5.0f), ColorRGBA.White.mult(0.5f), 50);
|
// PointLight pl1 = new PointLight(new Vector3f(-5.0f, -5.0f, -5.0f), ColorRGBA.White.mult(0.5f), 50);
|
||||||
// rootNode.addLight(pl1);
|
// rootNode.addLight(pl1);
|
||||||
|
|
||||||
loadModel("Models/gltf/box/box.gltf", Vector3f.ZERO, 1);
|
// loadModel("Models/gltf/box/box.gltf", Vector3f.ZERO, 1);
|
||||||
loadModel("Models/gltf/duck/Duck.gltf", new Vector3f(0, -1, 0), 1);
|
// loadModel("Models/gltf/duck/Duck.gltf", new Vector3f(0, -1, 0), 1);
|
||||||
loadModel("Models/gltf/damagedHelmet/damagedHelmet.gltf", Vector3f.ZERO, 1);
|
// loadModel("Models/gltf/damagedHelmet/damagedHelmet.gltf", Vector3f.ZERO, 1);
|
||||||
loadModel("Models/gltf/hornet/scene.gltf", new Vector3f(0, -0.5f, 0), 0.4f);
|
// loadModel("Models/gltf/hornet/scene.gltf", new Vector3f(0, -0.5f, 0), 0.4f);
|
||||||
// loadModel("Models/gltf/adamHead/adamHead.gltf", Vector3f.ZERO, 0.6f);
|
//// loadModel("Models/gltf/adamHead/adamHead.gltf", Vector3f.ZERO, 0.6f);
|
||||||
loadModel("Models/gltf/busterDrone/busterDrone.gltf", new Vector3f(0, 0f, 0), 0.8f);
|
// loadModel("Models/gltf/busterDrone/busterDrone.gltf", new Vector3f(0, 0f, 0), 0.8f);
|
||||||
loadModel("Models/gltf/animatedCube/AnimatedCube.gltf", Vector3f.ZERO, 0.5f);
|
// loadModel("Models/gltf/animatedCube/AnimatedCube.gltf", Vector3f.ZERO, 0.5f);
|
||||||
|
//
|
||||||
//TODO need to pad tracks that doesn't have the same length than the animation.
|
// //loadModel("Models/gltf/BoxAnimated/BoxAnimated.gltf", new Vector3f(0, 0f, 0), 0.8f);
|
||||||
//loadModel("Models/gltf/BoxAnimated/BoxAnimated.gltf", new Vector3f(0, 0f, 0), 0.8f);
|
//
|
||||||
|
// loadModel("Models/gltf/RiggedFigure/RiggedSimple.gltf", new Vector3f(0, -0.3f, 0), 0.2f);
|
||||||
loadModel("Models/gltf/RiggedFigure/RiggedSimple.gltf", new Vector3f(0, -0.3f, 0), 0.2f);
|
//loadModel("Models/gltf/RiggedFigure/RiggedFigure.gltf", new Vector3f(0, -1f, 0), 1f);
|
||||||
loadModel("Models/gltf/RiggedFigure/RiggedFigure.gltf", new Vector3f(0, -1f, 0), 1f);
|
//loadModel("Models/gltf/CesiumMan/CesiumMan.gltf", new Vector3f(0, -1, 0), 1f);
|
||||||
loadModel("Models/gltf/CesiumMan/CesiumMan.gltf", new Vector3f(0, -1, 0), 1f);
|
//loadModel("Models/gltf/BrainStem/BrainStem.gltf", new Vector3f(0, -1, 0), 1f);
|
||||||
loadModel("Models/gltf/BrainStem/BrainStem.gltf", new Vector3f(0, -1, 0), 1f);
|
|
||||||
//loadModel("Models/gltf/Jaime/Jaime.gltf", new Vector3f(0, -1, 0), 1f);
|
//loadModel("Models/gltf/Jaime/Jaime.gltf", new Vector3f(0, -1, 0), 1f);
|
||||||
//loadModel("Models/gltf/GiantWorm/GiantWorm.gltf", new Vector3f(0, -1, 0), 1f);
|
// loadModel("Models/gltf/GiantWorm/GiantWorm.gltf", new Vector3f(0, -1, 0), 1f);
|
||||||
//loadModel("Models/gltf/RiggedFigure/WalkingLady.gltf", new Vector3f(0, -0.f, 0), 1f);
|
// //loadModel("Models/gltf/RiggedFigure/WalkingLady.gltf", new Vector3f(0, -0.f, 0), 1f);
|
||||||
loadModel("Models/gltf/Monster/Monster.gltf", Vector3f.ZERO, 0.03f);
|
// loadModel("Models/gltf/Monster/Monster.gltf", Vector3f.ZERO, 0.03f);
|
||||||
|
|
||||||
|
// loadModel("Models/gltf/corset/Corset.gltf", new Vector3f(0, -1, 0), 20f);
|
||||||
|
loadModel("Models/gltf/boxInter/BoxInterleaved.gltf", new Vector3f(0, 0, 0), 1f);
|
||||||
|
|
||||||
|
|
||||||
probeNode.attachChild(assets.get(0));
|
probeNode.attachChild(assets.get(0));
|
||||||
@ -193,12 +195,14 @@ public class TestGltfLoading extends SimpleApplication {
|
|||||||
playFirstAnim(s);
|
playFirstAnim(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SkeletonControl ctrl = findControl(s, SkeletonControl.class);
|
SkeletonControl ctrl = findControl(s, SkeletonControl.class);
|
||||||
|
|
||||||
// //ctrl.getSpatial().removeControl(ctrl);
|
// //ctrl.getSpatial().removeControl(ctrl);
|
||||||
// if (ctrl == null) {
|
if (ctrl == null) {
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
// getStateManager().getState(SkeletonDebugAppState.class).addSkeleton(ctrl, true);
|
ctrl.setHardwareSkinningPreferred(false);
|
||||||
|
getStateManager().getState(SkeletonDebugAppState.class).addSkeleton(ctrl, true);
|
||||||
// AnimControl aCtrl = findControl(s, AnimControl.class);
|
// AnimControl aCtrl = findControl(s, AnimControl.class);
|
||||||
// //ctrl.getSpatial().removeControl(ctrl);
|
// //ctrl.getSpatial().removeControl(ctrl);
|
||||||
// if (aCtrl == null) {
|
// if (aCtrl == null) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user