Changed how roughness is stored into prefiltered env map mipmaps for a more consistent method.
Bumped the default size of the LightProbe maps to 256 Change he integrateBRDF approximation method
This commit is contained in:
parent
b56e321218
commit
0da2bfe0ba
@ -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<SnapshotJob> jobs = new ArrayList<SnapshotJob>();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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++;
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -80,6 +80,7 @@ public class TestPBRLighting extends SimpleApplication {
|
||||
private Node modelNode;
|
||||
private int frame = 0;
|
||||
private Material pbrMat;
|
||||
private float roughness = 1.0f;
|
||||
|
||||
@Override
|
||||
public void simpleInitApp() {
|
||||
@ -93,7 +94,7 @@ 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);
|
||||
|
||||
@ -102,7 +103,7 @@ public class TestPBRLighting extends SimpleApplication {
|
||||
// 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,7 +115,7 @@ 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();
|
||||
@ -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,6 +189,8 @@ 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();
|
||||
|
BIN
jme3-examples/src/main/resources/jme3test/light/pbr/ref.png
Normal file
BIN
jme3-examples/src/main/resources/jme3test/light/pbr/ref.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 MiB |
Binary file not shown.
Before Width: | Height: | Size: 508 KiB |
Binary file not shown.
Before Width: | Height: | Size: 555 KiB |
Loading…
x
Reference in New Issue
Block a user