diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentCamera.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentCamera.java index 4773b9639..1fba5d51d 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentCamera.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentCamera.java @@ -111,7 +111,7 @@ public class EnvironmentCamera extends BaseAppState { /** * The size of environment cameras. */ - protected int size = 128; + protected int size = 256; private final List jobs = new ArrayList(); diff --git a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java index 0c03b7659..81086b46b 100644 --- a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java +++ b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java @@ -280,6 +280,8 @@ public class LightProbeFactory { jobState.done[index] = true; if (jobState.isDone()) { + int nbMipMap = (int) (Math.log(probe.getPrefilteredEnvMap().getImage().getWidth()) / Math.log(2) - 1); + probe.setNbMipMaps(nbMipMap); probe.setReady(true); if (globalListener != null) { globalListener.done(probe); diff --git a/jme3-core/src/main/java/com/jme3/environment/generation/IrradianceMapGenerator.java b/jme3-core/src/main/java/com/jme3/environment/generation/IrradianceMapGenerator.java index 356d53b03..5504630ec 100644 --- a/jme3-core/src/main/java/com/jme3/environment/generation/IrradianceMapGenerator.java +++ b/jme3-core/src/main/java/com/jme3/environment/generation/IrradianceMapGenerator.java @@ -44,7 +44,7 @@ import java.util.concurrent.Callable; /** * - * Generates the Irrafiance map for PBR. This job can be lauched from a separate + * 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. diff --git a/jme3-core/src/main/java/com/jme3/environment/generation/PrefilteredEnvMapFaceGenerator.java b/jme3-core/src/main/java/com/jme3/environment/generation/PrefilteredEnvMapFaceGenerator.java index c5501a329..11b710b46 100644 --- a/jme3-core/src/main/java/com/jme3/environment/generation/PrefilteredEnvMapFaceGenerator.java +++ b/jme3-core/src/main/java/com/jme3/environment/generation/PrefilteredEnvMapFaceGenerator.java @@ -34,13 +34,13 @@ 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.*; + import static com.jme3.math.FastMath.abs; import static com.jme3.math.FastMath.clamp; import static com.jme3.math.FastMath.pow; import static com.jme3.math.FastMath.sqrt; -import com.jme3.math.Vector3f; -import com.jme3.math.Vector4f; + import com.jme3.texture.TextureCubeMap; import static com.jme3.environment.util.EnvMapUtils.getHammersleyPoint; import static com.jme3.environment.util.EnvMapUtils.getRoughnessFromMip; @@ -184,7 +184,7 @@ public class PrefilteredEnvMapFaceGenerator extends RunnableWithProgress { for (int y = 0; y < targetMipMapSize; y++) { for (int x = 0; x < targetMipMapSize; x++) { color.set(0, 0, 0); - getVectorFromCubemapFaceTexCoord(x, y, targetMipMapSize, face, texelVect, EnvMapUtils.FixSeamsMethod.Wrap); + getVectorFromCubemapFaceTexCoord(x, y, targetMipMapSize, face, texelVect, EnvMapUtils.FixSeamsMethod.None); 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); @@ -207,7 +207,6 @@ public class PrefilteredEnvMapFaceGenerator extends RunnableWithProgress { // a = roughness² and a2 = a² float a2 = roughness * roughness; a2 *= a2; - a2 *= 10; for (int i = 0; i < numSamples; i++) { Xi = getHammersleyPoint(i, numSamples, Xi); H = importanceSampleGGX(Xi, a2, N, H); @@ -227,8 +226,11 @@ public class PrefilteredEnvMapFaceGenerator extends RunnableWithProgress { totalWeight += NoL; } } + if (totalWeight > 0) { + prefilteredColor.divideLocal(totalWeight); + } - return prefilteredColor.divideLocal(totalWeight); + return prefilteredColor; } public Vector3f importanceSampleGGX(Vector4f xi, float a2, Vector3f normal, Vector3f store) { diff --git a/jme3-core/src/main/java/com/jme3/environment/util/EnvMapUtils.java b/jme3-core/src/main/java/com/jme3/environment/util/EnvMapUtils.java index 9474cb257..78ce73f3b 100644 --- a/jme3-core/src/main/java/com/jme3/environment/util/EnvMapUtils.java +++ b/jme3-core/src/main/java/com/jme3/environment/util/EnvMapUtils.java @@ -33,9 +33,7 @@ package com.jme3.environment.util; import com.jme3.asset.AssetManager; import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Vector3f; -import com.jme3.math.Vector4f; +import com.jme3.math.*; import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.shape.Quad; @@ -49,8 +47,7 @@ import com.jme3.util.BufferUtils; import java.nio.ByteBuffer; import java.util.ArrayList; import static com.jme3.math.FastMath.*; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector2f; + import com.jme3.util.TempVars; /** @@ -254,7 +251,7 @@ public class EnvMapUtils { float v; if (fixSeamsMethod == FixSeamsMethod.Stretch) { - /* Code from Nvtt : http://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvtt/CubeSurface.cpp + /* Code from Nvtt : https://github.com/castano/nvidia-texture-tools/blob/master/src/nvtt/CubeSurface.cpp#L77 * transform from [0..res - 1] to [-1 .. 1], match up edges exactly. */ u = (2.0f * (float) x / ((float) mapSize - 1.0f)) - 1.0f; v = (2.0f * (float) y / ((float) mapSize - 1.0f)) - 1.0f; @@ -274,7 +271,7 @@ public class EnvMapUtils { } //compute vector depending on the face - // Code from Nvtt : http://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvtt/CubeSurface.cpp + // Code from Nvtt : https://github.com/castano/nvidia-texture-tools/blob/master/src/nvtt/CubeSurface.cpp#L101 switch (face) { case 0: store.set(1f, -v, -u); @@ -387,62 +384,21 @@ public class EnvMapUtils { return face; } - /* - public static void main(String... argv) { - -// for (int givenFace = 0; givenFace < 6; givenFace++) { -// -// //int givenFace = 1; -// for (int x = 0; x < 128; x++) { -// for (int y = 0; y < 128; y++) { -// Vector3f v = EnvMapUtils.getVectorFromCubemapFaceTexCoord(x, y, 128, givenFace, null, FixSeamsMethod.None); -// Vector2f uvs = new Vector2f(); -// int face = EnvMapUtils.getCubemapFaceTexCoordFromVector(v, 128, uvs, FixSeamsMethod.None); -// -// if ((int) uvs.x != x || (int) uvs.y != y) { -// System.err.println("error " + uvs + " should be " + x + "," + y + " vect was " + v); -// } -// if (givenFace != face) { -// System.err.println("error face: " + face + " should be " + givenFace); -// } -// } -// } -// } -// System.err.println("done "); - int total = 0; - for (int i = 0; i < 6; i++) { - int size = (int) pow(2, 7 - i); - int samples = EnvMapUtils.getSampleFromMip(i, 6); - int iterations = (samples * size * size); - total += iterations; - float roughness = EnvMapUtils.getRoughnessFromMip(i, 6); - System.err.println("roughness " + i + " : " + roughness + " , map : " + size + " , samples : " + samples + " , iterations : " + iterations); - System.err.println("reverse " + EnvMapUtils.getMipFromRoughness(roughness, 6)); - - } - System.err.println("total " + total); - System.err.println(128 * 128 * 1024); - System.err.println("test " + EnvMapUtils.getMipFromRoughness(0.9999f, 6)); - System.err.println("nb mip = " + (Math.log(128) / Math.log(2) - 1)); - - }*/ - - public static int getSampleFromMip(int mipLevel, int miptot) { - return mipLevel==0?1:Math.min(1 << (miptot - 1 + (mipLevel) * 2 ), 8192); + public static int getSampleFromMip(int mipLevel, int miptot) { + return mipLevel == 0 ? 1 : Math.min(1 << (miptot - 1 + (mipLevel) * 2), 8192); } - public static float getRoughnessFromMip(int miplevel, int miptot) { - float mipScale = 1.0f; - float mipOffset = -0.3f; - return pow(2, (miplevel - (miptot - 1) + mipOffset) / mipScale); + //see lagarde's paper https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf + //linear roughness + public static float getRoughnessFromMip(int miplevel, int miptot) { + float step = 1f / ((float) miptot - 1); + step *= miplevel; + return step * step; } public static float getMipFromRoughness(float roughness, int miptot) { - float mipScale = 1.0f; - float Lod = (float) (Math.log(roughness) / Math.log(2)) * mipScale + miptot - 1.0f; - - return (float) Math.max(0.0, Lod); + return FastMath.sqrt(roughness) * (miptot - 1); } /** @@ -559,138 +515,6 @@ public class EnvMapUtils { shDir[7] = -(sqrt15Pi * xV * zV) / 2f; shDir[8] = sqrt15Pi * (x2 - y2) / 4f; -// shDir[0] = (1f/(2.f*sqrtPi)); -// -// shDir[1] = -(sqrt(3f/pi)*yV)/2.f; -// shDir[2] = (sqrt(3/pi)*zV)/2.f; -// shDir[3] = -(sqrt(3/pi)*xV)/2.f; -// -// shDir[4] = (sqrt(15f/pi)*xV*yV)/2.f; -// shDir[5] = -(sqrt(15f/pi)*yV*zV)/2.f; -// shDir[6] = (sqrt(5f/pi)*(-1 + 3f*z2))/4.f; -// shDir[7] = -(sqrt(15f/pi)*xV*zV)/2.f; -// shDir[8] = sqrt(15f/pi)*(x2 - y2)/4.f; - - - } - - /** - * {@link EnvMapUtils#generateIrradianceMap(com.jme3.math.Vector3f[], com.jme3.texture.TextureCubeMap, int, com.jme3.utils.EnvMapUtils.FixSeamsMethod) - * } - * - * @param shCoeffs the spherical harmonics coefficients to use - * @param targetMapSize the size of the target map - * @return the irradiance map. - */ - public static TextureCubeMap generateIrradianceMap(Vector3f[] shCoeffs, int targetMapSize) { - return generateIrradianceMap(shCoeffs, targetMapSize, FixSeamsMethod.Wrap, 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)} - * Note that the output cube map is in RGBA8 format. - * - * @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 static TextureCubeMap generateIrradianceMap(Vector3f[] shCoeffs, int targetMapSize, FixSeamsMethod fixSeamsMethod, TextureCubeMap store) { - TextureCubeMap irrCubeMap = store; - if (irrCubeMap == null) { - irrCubeMap = new TextureCubeMap(targetMapSize, targetMapSize, Image.Format.RGB16F); - irrCubeMap.setMagFilter(Texture.MagFilter.Bilinear); - irrCubeMap.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); - irrCubeMap.getImage().setColorSpace(ColorSpace.Linear); - } - - for (int i = 0; i < 6; i++) { - ByteBuffer buf = BufferUtils.createByteBuffer(targetMapSize * targetMapSize * irrCubeMap.getImage().getFormat().getBitsPerPixel()/8); - irrCubeMap.getImage().setData(i, buf); - } - - 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++) { - getVectorFromCubemapFaceTexCoord(x, y, targetMapSize, face, texelVect, fixSeamsMethod); - evalShBasis(texelVect, shDir); - color.set(0, 0, 0, 0); - for (int i = 0; i < 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); - } - } - } - return irrCubeMap; - } - - /** - * Generates the prefiltered env map (used for image based specular - * lighting) With the GGX/Shlick brdf - * {@link EnvMapUtils#getSphericalHarmonicsCoefficents(com.jme3.texture.TextureCubeMap)} - * Note that the output cube map is in RGBA8 format. - * - * @param sourceEnvMap - * @param targetMapSize the size of the irradiance map to generate - * @param store - * @param fixSeamsMethod the method to fix seams - * @return The irradiance cube map for the given coefficients - */ - public static TextureCubeMap generatePrefilteredEnvMap(TextureCubeMap sourceEnvMap, int targetMapSize, FixSeamsMethod fixSeamsMethod, TextureCubeMap store) { - TextureCubeMap pem = store; - if (pem == null) { - pem = new TextureCubeMap(targetMapSize, targetMapSize, Image.Format.RGB16F); - pem.setMagFilter(Texture.MagFilter.Bilinear); - pem.setMinFilter(Texture.MinFilter.Trilinear); - pem.getImage().setColorSpace(ColorSpace.Linear); - } - - int nbMipMap = (int) (Math.log(targetMapSize) / Math.log(2) - 1); - - CubeMapWrapper sourceWrapper = new CubeMapWrapper(sourceEnvMap); - CubeMapWrapper targetWrapper = new CubeMapWrapper(pem); - targetWrapper.initMipMaps(nbMipMap); - - Vector3f texelVect = new Vector3f(); - Vector3f color = new Vector3f(); - ColorRGBA outColor = new ColorRGBA(); - for (int mipLevel = 0; mipLevel < nbMipMap; mipLevel++) { - System.err.println("mip level " + mipLevel); - float roughness = getRoughnessFromMip(mipLevel, nbMipMap); - int nbSamples = getSampleFromMip(mipLevel, nbMipMap); - int targetMipMapSize = (int) pow(2, nbMipMap + 1 - mipLevel); - for (int face = 0; face < 6; face++) { - System.err.println("face " + face); - for (int y = 0; y < targetMipMapSize; y++) { - for (int x = 0; x < targetMipMapSize; x++) { - color.set(0, 0, 0); - getVectorFromCubemapFaceTexCoord(x, y, targetMipMapSize, face, texelVect, FixSeamsMethod.Wrap); - prefilterEnvMapTexel(sourceWrapper, roughness, texelVect, nbSamples, color); - outColor.set(color.x, color.y, color.z, 1.0f); - // System.err.println("coords " + x + "," + y); - targetWrapper.setPixel(x, y, face, mipLevel, outColor); - } - } - } - } - return pem; } public static Vector4f getHammersleyPoint(int i, final int nbrSample, Vector4f store) { @@ -719,43 +543,6 @@ public class EnvMapUtils { return store; } - private static Vector3f prefilterEnvMapTexel(CubeMapWrapper envMapReader, float roughness, Vector3f N, int numSamples, Vector3f store) { - - Vector3f prefilteredColor = store; - float totalWeight = 0.0f; - - TempVars vars = TempVars.get(); - Vector4f Xi = vars.vect4f1; - Vector3f H = vars.vect1; - Vector3f tmp = vars.vect2; - ColorRGBA c = vars.color; - // a = roughness² and a2 = a² - float a2 = roughness * roughness; - a2 *= a2; - a2 *= 10; - for (int i = 0; i < numSamples; i++) { - Xi = getHammersleyPoint(i, numSamples, Xi); - H = importanceSampleGGX(Xi, a2, N, H, vars); - - H.normalizeLocal(); - tmp.set(H); - float NoH = N.dot(tmp); - - Vector3f L = tmp.multLocal(NoH * 2).subtractLocal(N); - float NoL = clamp(N.dot(L), 0.0f, 1.0f); - if (NoL > 0) { - envMapReader.getPixel(L, c); - prefilteredColor.setX(prefilteredColor.x + c.r * NoL); - prefilteredColor.setY(prefilteredColor.y + c.g * NoL); - prefilteredColor.setZ(prefilteredColor.z + c.b * NoL); - - totalWeight += NoL; - } - } - vars.release(); - return prefilteredColor.divideLocal(totalWeight); - } - public static Vector3f importanceSampleGGX(Vector4f xi, float a2, Vector3f normal, Vector3f store, TempVars vars) { if (store == null) { store = new Vector3f(); @@ -945,3 +732,5 @@ public class EnvMapUtils { return pem; } } + + diff --git a/jme3-core/src/main/java/com/jme3/light/LightProbe.java b/jme3-core/src/main/java/com/jme3/light/LightProbe.java index 66c07342b..afebea823 100644 --- a/jme3-core/src/main/java/com/jme3/light/LightProbe.java +++ b/jme3-core/src/main/java/com/jme3/light/LightProbe.java @@ -81,6 +81,7 @@ public class LightProbe extends Light implements Savable { private boolean ready = false; private Vector3f position = new Vector3f(); private Node debugNode; + private int nbMipMaps; /** * Empty constructor used for serialization. @@ -226,6 +227,14 @@ public class LightProbe extends Light implements Savable { getBounds().setCenter(position); } + public int getNbMipMaps() { + return nbMipMaps; + } + + public void setNbMipMaps(int nbMipMaps) { + this.nbMipMaps = nbMipMaps; + } + @Override public boolean intersectsBox(BoundingBox box, TempVars vars) { return getBounds().intersectsBoundingBox(box); diff --git a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java index f8139af0f..31599ded2 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java @@ -128,10 +128,10 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique ambientColor.setValue(VarType.Vector4, ambientLightColor); } - //If there is a lightProbe in the list we force it's render on the first pass + //If there is a lightProbe in the list we force its render on the first pass if(lightProbe != null){ BoundingSphere s = (BoundingSphere)lightProbe.getBounds(); - lightProbeData.setVector4InArray(lightProbe.getPosition().x, lightProbe.getPosition().y, lightProbe.getPosition().z, 1f/s.getRadius(), 0); + lightProbeData.setVector4InArray(lightProbe.getPosition().x, lightProbe.getPosition().y, lightProbe.getPosition().z, 1f / s.getRadius() + lightProbe.getNbMipMaps(), 0); //assigning new texture indexes int irrUnit = lastTexUnit++; int pemUnit = lastTexUnit++; diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag index 3d0b8299b..6523fced3 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag @@ -244,8 +244,10 @@ void main(){ #ifdef INDIRECT_LIGHTING vec3 rv = reflect(-viewDir.xyz, normal.xyz); //prallax fix for spherical bounds from https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ - // g_LightProbeData.w is 1/probe radius, g_LightProbeData.xyz is the position of the lightProbe. - rv = g_LightProbeData.w * (wPosition - g_LightProbeData.xyz) +rv; + // g_LightProbeData.w is 1/probe radius + nbMipMaps, g_LightProbeData.xyz is the position of the lightProbe. + float invRadius = fract( g_LightProbeData.w); + float nbMipMaps = g_LightProbeData.w - invRadius; + rv = invRadius * (wPosition - g_LightProbeData.xyz) +rv; //horizon fade from http://marmosetco.tumblr.com/post/81245981087 float horiz = dot(rv, wNormal.xyz); @@ -257,7 +259,7 @@ void main(){ vec3 indirectSpecular = vec3(0.0); indirectDiffuse = textureCube(g_IrradianceMap, normal.xyz).rgb * diffuseColor.rgb; - indirectSpecular = ApproximateSpecularIBLPolynomial(g_PrefEnvMap, specularColor.rgb, Roughness, ndotv, rv.xyz); + indirectSpecular = ApproximateSpecularIBLPolynomial(g_PrefEnvMap, specularColor.rgb, Roughness, ndotv, rv.xyz, nbMipMaps); indirectSpecular *= vec3(horiz); vec3 indirectLighting = indirectDiffuse + indirectSpecular; @@ -273,7 +275,6 @@ void main(){ #endif gl_FragColor += emissive * pow(emissive.a, m_EmissivePower) * m_EmissiveIntensity; #endif - gl_FragColor.a = alpha; } diff --git a/jme3-core/src/main/resources/Common/ShaderLib/PBR.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/PBR.glsllib index 40aea5dfa..3e26bda8a 100644 --- a/jme3-core/src/main/resources/Common/ShaderLib/PBR.glsllib +++ b/jme3-core/src/main/resources/Common/ShaderLib/PBR.glsllib @@ -106,46 +106,26 @@ void PBR_ComputeDirectLight(vec3 normal, vec3 lightDir, vec3 viewDir, outSpecular = vec3(specular) * lightColor; } -//https://knarkowicz.wordpress.com/2014/12/27/analytical-dfg-term-for-ibl/ -vec3 EnvDFGPolynomial( vec3 specularColor, float roughness, float ndotv ){ - float x = 1.0 - roughness; - float y = ndotv; - - float b1 = -0.1688; - float b2 = 1.895; - float b3 = 0.9903; - float b4 = -4.853; - float b5 = 8.404; - float b6 = -5.069; - float bias = clamp( min( b1 * x + b2 * x * x, b3 + b4 * y + b5 * y * y + b6 * y * y * y ), 0.0, 1.0 ); - - float d0 = 0.6045; - float d1 = 1.699; - float d2 = -0.5228; - float d3 = -3.603; - float d4 = 1.404; - float d5 = 0.1939; - float d6 = 2.661; - float delta = clamp(( d0 + d1 * x + d2 * y + d3 * x * x + d4 * x * y + d5 * y * y + d6 * x * x * x ), 0.0, 1.0); - float scale = delta - bias; - - bias *= clamp( 2.5 / (roughness) * specularColor.y, 0.0, 1.0 ); - return specularColor * scale + bias; +vec3 integrateBRDFApprox( const in vec3 specular, float roughness, float NoV ){ + const vec4 c0 = vec4( -1, -0.0275, -0.572, 0.022 ); + const vec4 c1 = vec4( 1, 0.0425, 1.04, -0.04 ); + vec4 r = roughness * c0 + c1; + float a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y; + vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw; + return specular * AB.x + AB.y; } -vec3 ApproximateSpecularIBL(samplerCube envMap,sampler2D integrateBRDF, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec){ - //TODO magic values should be replaced by defines. - float Lod = log2(Roughness) * 1.1 + 6.0 - 2.0; +vec3 ApproximateSpecularIBL(samplerCube envMap,sampler2D integrateBRDF, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec, float nbMipMaps){ + float Lod = sqrt( Roughness ) * (nbMipMaps - 1.0); vec3 PrefilteredColor = textureCubeLod(envMap, refVec.xyz,Lod).rgb; vec2 EnvBRDF = texture2D(integrateBRDF,vec2(Roughness, ndotv)).rg; return PrefilteredColor * ( SpecularColor * EnvBRDF.x+ EnvBRDF.y ); } -vec3 ApproximateSpecularIBLPolynomial(samplerCube envMap, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec){ - //TODO magic values should be replaced by defines. - float Lod = log2(Roughness) * 1.6 + 5.0; +vec3 ApproximateSpecularIBLPolynomial(samplerCube envMap, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec, float nbMipMaps){ + float Lod = sqrt( Roughness ) * (nbMipMaps - 1.0); vec3 PrefilteredColor = textureCubeLod(envMap, refVec.xyz, Lod).rgb; - return PrefilteredColor * EnvDFGPolynomial(SpecularColor, Roughness, ndotv); + return PrefilteredColor * integrateBRDFApprox(SpecularColor, Roughness, ndotv); } diff --git a/jme3-examples/src/main/java/jme3test/light/pbr/RefEnv.java b/jme3-examples/src/main/java/jme3test/light/pbr/RefEnv.java index b613bc1f4..17f3efa55 100644 --- a/jme3-examples/src/main/java/jme3test/light/pbr/RefEnv.java +++ b/jme3-examples/src/main/java/jme3test/light/pbr/RefEnv.java @@ -18,7 +18,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.ui.Picture; -import com.jme3.util.MaterialDebugAppState; +import com.jme3.util.SkyFactory; /** * test @@ -29,8 +29,7 @@ public class RefEnv extends SimpleApplication { private Node tex; private Node ref; - private Picture refDE; - private Picture refM; + private Picture refImg; public static void main(String[] args) { RefEnv app = new RefEnv(); @@ -40,25 +39,25 @@ public class RefEnv extends SimpleApplication { @Override public void simpleInitApp() { - cam.setLocation(new Vector3f(-2.3324413f, 2.9567573f, 4.6054406f)); - cam.setRotation(new Quaternion(0.06310794f, 0.9321281f, -0.29613864f, 0.1986369f)); - Spatial sc = assetManager.loadModel("Scenes/PBR/spheres.j3o"); + cam.setLocation(new Vector3f(-17.713732f, 1.8661976f, 17.156784f)); + cam.setRotation(new Quaternion(0.021403445f, 0.9428821f, -0.06178002f, 0.32664734f)); + flyCam.setDragToRotate(true); + flyCam.setMoveSpeed(5); + Spatial sc = assetManager.loadModel("Models/gltf/ref/scene.gltf"); rootNode.attachChild(sc); - rootNode.getChild("Scene").setCullHint(Spatial.CullHint.Always); + Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap); + rootNode.attachChild(sky); + rootNode.getChild(0).setCullHint(Spatial.CullHint.Always); ref = new Node("reference pictures"); - refDE = new Picture("refDE"); - refDE.setHeight(cam.getHeight()); - refDE.setWidth(cam.getWidth()); - refDE.setImage(assetManager,"jme3test/light/pbr/spheresRefDE.png", false); - refM = new Picture("refM"); - refM.setImage(assetManager,"jme3test/light/pbr/spheresRefM.png", false); - refM.setHeight(cam.getHeight()); - refM.setWidth(cam.getWidth()); + refImg = new Picture("refImg"); + refImg.setHeight(cam.getHeight()); + refImg.setWidth(cam.getWidth()); + refImg.setImage(assetManager, "jme3test/light/pbr/ref.png", false); - ref.attachChild(refDE); + ref.attachChild(refImg); - stateManager.attach(new EnvironmentCamera()); + stateManager.attach(new EnvironmentCamera(256, Vector3f.ZERO)); inputManager.addMapping("tex", new KeyTrigger(KeyInput.KEY_SPACE)); inputManager.addMapping("switch", new KeyTrigger(KeyInput.KEY_RETURN)); @@ -105,13 +104,11 @@ public class RefEnv extends SimpleApplication { if (((Float) mat.getParam("Metallic").getValue()) == 1f) { mat.setFloat("Metallic", 0); mat.setColor("BaseColor", ColorRGBA.Black); - ref.attachChild(refDE); - refM.removeFromParent(); + ref.attachChild(refImg); } else { mat.setFloat("Metallic", 1); mat.setColor("BaseColor", ColorRGBA.White); - ref.attachChild(refM); - refDE.removeFromParent(); + refImg.removeFromParent(); } } } @@ -130,7 +127,7 @@ public class RefEnv extends SimpleApplication { System.err.println("Done rendering env maps"); tex = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(result.getPrefilteredEnvMap(), assetManager); // guiNode.attachChild(tex); - rootNode.getChild("Scene").setCullHint(Spatial.CullHint.Dynamic); + rootNode.getChild(0).setCullHint(Spatial.CullHint.Dynamic); } }); ((BoundingSphere) probe.getBounds()).setRadius(100); diff --git a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java index 388123e41..7e3f35bb1 100644 --- a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java +++ b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java @@ -78,8 +78,9 @@ public class TestPBRLighting extends SimpleApplication { private Geometry model; private DirectionalLight dl; private Node modelNode; - private int frame = 0; - private Material pbrMat; + private int frame = 0; + private Material pbrMat; + private float roughness = 1.0f; @Override public void simpleInitApp() { @@ -93,16 +94,16 @@ public class TestPBRLighting extends SimpleApplication { dl = new DirectionalLight(); dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); - rootNode.addLight(dl); + // rootNode.addLight(dl); dl.setColor(ColorRGBA.White); rootNode.attachChild(modelNode); - + FilterPostProcessor fpp = new FilterPostProcessor(assetManager); // fpp.addFilter(new FXAAFilter()); fpp.addFilter(new ToneMapFilter(Vector3f.UNIT_XYZ.mult(4.0f))); // 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/Path.hdr", SkyFactory.EnvMapType.EquirectMap); @@ -114,17 +115,17 @@ public class TestPBRLighting extends SimpleApplication { model.setMaterial(pbrMat); - final EnvironmentCamera envCam = new EnvironmentCamera(128, new Vector3f(0, 3f, 0)); + final EnvironmentCamera envCam = new EnvironmentCamera(256, new Vector3f(0, 3f, 0)); stateManager.attach(envCam); - + // EnvironmentManager envManager = new EnvironmentManager(); // stateManager.attach(envManager); - - // envManager.setScene(rootNode); - + + // envManager.setScene(rootNode); + LightsDebugState debugState = new LightsDebugState(); stateManager.attach(debugState); - + ChaseCamera chaser = new ChaseCamera(cam, modelNode, inputManager); chaser.setDragToRotate(true); chaser.setMinVerticalRotation(-FastMath.HALF_PI); @@ -144,7 +145,7 @@ public class TestPBRLighting extends SimpleApplication { } if (tex.getParent() == null && tex2.getParent() == null) { guiNode.attachChild(tex); - } else if (tex2.getParent() == null){ + } else if (tex2.getParent() == null) { tex.removeFromParent(); guiNode.attachChild(tex2); } else { @@ -152,6 +153,16 @@ public class TestPBRLighting extends SimpleApplication { } } + if (name.equals("rup") && isPressed) { + roughness = FastMath.clamp(roughness + 0.1f, 0.0f, 1.0f); + pbrMat.setFloat("Roughness", roughness); + } + if (name.equals("rdown") && isPressed) { + roughness = FastMath.clamp(roughness - 0.1f, 0.0f, 1.0f); + pbrMat.setFloat("Roughness", roughness); + } + + if (name.equals("up") && isPressed) { model.move(0, tpf * 100f, 0); } @@ -169,7 +180,7 @@ public class TestPBRLighting extends SimpleApplication { dl.setDirection(cam.getDirection().normalize()); } } - }, "toggle", "light", "up", "down", "left", "right", "debug"); + }, "toggle", "light", "up", "down", "left", "right", "debug", "rup", "rdown"); inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_RETURN)); inputManager.addMapping("light", new KeyTrigger(KeyInput.KEY_F)); @@ -178,8 +189,10 @@ public class TestPBRLighting extends SimpleApplication { inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_LEFT)); inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_RIGHT)); inputManager.addMapping("debug", new KeyTrigger(KeyInput.KEY_D)); - - + inputManager.addMapping("rup", new KeyTrigger(KeyInput.KEY_T)); + inputManager.addMapping("rdown", new KeyTrigger(KeyInput.KEY_G)); + + MaterialDebugAppState debug = new MaterialDebugAppState(); debug.registerBinding("Common/MatDefs/Light/PBRLighting.frag", rootNode); getStateManager().attach(debug); @@ -201,10 +214,10 @@ public class TestPBRLighting extends SimpleApplication { tex2 = EnvMapUtils.getCubeMapCrossDebugView(result.getIrradianceMap(), assetManager); } }); - ((BoundingSphere)probe.getBounds()).setRadius(100); + ((BoundingSphere) probe.getBounds()).setRadius(100); rootNode.addLight(probe); //getStateManager().getState(EnvironmentManager.class).addEnvProbe(probe); - + } if (frame > 10 && modelNode.getParent() == null) { rootNode.attachChild(modelNode); diff --git a/jme3-examples/src/main/resources/jme3test/light/pbr/ref.png b/jme3-examples/src/main/resources/jme3test/light/pbr/ref.png new file mode 100644 index 000000000..23ac5e8c1 Binary files /dev/null and b/jme3-examples/src/main/resources/jme3test/light/pbr/ref.png differ diff --git a/jme3-examples/src/main/resources/jme3test/light/pbr/spheresRefDE.png b/jme3-examples/src/main/resources/jme3test/light/pbr/spheresRefDE.png deleted file mode 100644 index ba041cfc3..000000000 Binary files a/jme3-examples/src/main/resources/jme3test/light/pbr/spheresRefDE.png and /dev/null differ diff --git a/jme3-examples/src/main/resources/jme3test/light/pbr/spheresRefM.png b/jme3-examples/src/main/resources/jme3test/light/pbr/spheresRefM.png deleted file mode 100644 index dadd04d94..000000000 Binary files a/jme3-examples/src/main/resources/jme3test/light/pbr/spheresRefM.png and /dev/null differ