parent
f1ab3be46c
commit
b14bb34176
@ -0,0 +1,98 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2019 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; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.environment.baker.IBLGLEnvBakerLight; |
||||||
|
import com.jme3.environment.util.EnvMapUtils; |
||||||
|
import com.jme3.light.LightProbe; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.texture.Image.Format; |
||||||
|
|
||||||
|
|
||||||
|
public class LightProbeFactory2 { |
||||||
|
|
||||||
|
|
||||||
|
public static LightProbe makeProbe(RenderManager rm, |
||||||
|
AssetManager am, int size,Vector3f pos, float frustumNear,float frustumFar,Spatial scene) { |
||||||
|
IBLGLEnvBakerLight baker=new IBLGLEnvBakerLight(rm, |
||||||
|
am, Format.RGB16F, Format.Depth, |
||||||
|
size, size); |
||||||
|
|
||||||
|
baker.bakeEnvironment(scene,pos, frustumNear,frustumFar); |
||||||
|
baker.bakeSpecularIBL(); |
||||||
|
baker.bakeSphericalHarmonicsCoefficients(); |
||||||
|
|
||||||
|
LightProbe probe = new LightProbe(); |
||||||
|
|
||||||
|
probe.setPosition(pos); |
||||||
|
probe.setPrefilteredMap(baker.getSpecularIBL()); |
||||||
|
probe.setNbMipMaps(probe.getPrefilteredEnvMap().getImage().getMipMapSizes().length); |
||||||
|
probe.setShCoeffs(baker.getSphericalHarmonicsCoefficients()); |
||||||
|
probe.setReady(true); |
||||||
|
|
||||||
|
baker.clean(); |
||||||
|
|
||||||
|
return probe; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* For debuging porpose only |
||||||
|
* Will return a Node meant to be added to a GUI presenting the 2 cube maps in a cross pattern with all the mip maps. |
||||||
|
* |
||||||
|
* @param manager the asset manager |
||||||
|
* @return a debug node |
||||||
|
*/ |
||||||
|
public static Node getDebugGui(AssetManager manager, LightProbe probe) { |
||||||
|
if (!probe.isReady()) { |
||||||
|
throw new UnsupportedOperationException("This EnvProbe is not ready yet, try to test isReady()"); |
||||||
|
} |
||||||
|
|
||||||
|
Node debugNode = new Node("debug gui probe"); |
||||||
|
Node debugPfemCm = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(probe.getPrefilteredEnvMap(), manager); |
||||||
|
debugNode.attachChild(debugPfemCm); |
||||||
|
debugPfemCm.setLocalTranslation(520, 0, 0); |
||||||
|
|
||||||
|
return debugNode; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,165 @@ |
|||||||
|
package com.jme3.environment.baker; |
||||||
|
|
||||||
|
import java.io.FileOutputStream; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.HashMap; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.environment.baker.EnvBaker; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.Quaternion; |
||||||
|
import com.jme3.math.Vector2f; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.renderer.Camera; |
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.renderer.ViewPort; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.texture.FrameBuffer; |
||||||
|
import com.jme3.texture.Image.Format; |
||||||
|
import com.jme3.texture.Texture.MagFilter; |
||||||
|
import com.jme3.texture.Texture.MinFilter; |
||||||
|
import com.jme3.texture.Texture.WrapMode; |
||||||
|
import com.jme3.texture.TextureCubeMap; |
||||||
|
import com.jme3.texture.image.ColorSpace; |
||||||
|
import com.jme3.util.BufferUtils; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Render the environment into a cubemap |
||||||
|
* |
||||||
|
* @author Riccardo Balbo |
||||||
|
*/ |
||||||
|
public abstract class GenericEnvBaker implements EnvBaker{ |
||||||
|
|
||||||
|
protected static Vector3f[] axisX=new Vector3f[6]; |
||||||
|
protected static Vector3f[] axisY=new Vector3f[6]; |
||||||
|
protected static Vector3f[] axisZ=new Vector3f[6]; |
||||||
|
static{ |
||||||
|
//PositiveX axis(left, up, direction)
|
||||||
|
axisX[0]=Vector3f.UNIT_Z.mult(1.0F); |
||||||
|
axisY[0]=Vector3f.UNIT_Y.mult(-1.0F); |
||||||
|
axisZ[0]=Vector3f.UNIT_X.mult(1.0F); |
||||||
|
//NegativeX
|
||||||
|
axisX[1]=Vector3f.UNIT_Z.mult(-1.0F); |
||||||
|
axisY[1]=Vector3f.UNIT_Y.mult(-1.0F); |
||||||
|
axisZ[1]=Vector3f.UNIT_X.mult(-1.0F); |
||||||
|
//PositiveY
|
||||||
|
axisX[2]=Vector3f.UNIT_X.mult(-1.0F); |
||||||
|
axisY[2]=Vector3f.UNIT_Z.mult(1.0F); |
||||||
|
axisZ[2]=Vector3f.UNIT_Y.mult(1.0F); |
||||||
|
//NegativeY
|
||||||
|
axisX[3]=Vector3f.UNIT_X.mult(-1.0F); |
||||||
|
axisY[3]=Vector3f.UNIT_Z.mult(-1.0F); |
||||||
|
axisZ[3]=Vector3f.UNIT_Y.mult(-1.0F); |
||||||
|
//PositiveZ
|
||||||
|
axisX[4]=Vector3f.UNIT_X.mult(-1.0F); |
||||||
|
axisY[4]=Vector3f.UNIT_Y.mult(-1.0F); |
||||||
|
axisZ[4]=Vector3f.UNIT_Z; |
||||||
|
//NegativeZ
|
||||||
|
axisX[5]=Vector3f.UNIT_X.mult(1.0F); |
||||||
|
axisY[5]=Vector3f.UNIT_Y.mult(-1.0F); |
||||||
|
axisZ[5]=Vector3f.UNIT_Z.mult(-1.0F); |
||||||
|
} |
||||||
|
|
||||||
|
protected TextureCubeMap env; |
||||||
|
protected Format depthFormat; |
||||||
|
|
||||||
|
|
||||||
|
protected final RenderManager renderManager; |
||||||
|
protected final AssetManager assetManager; |
||||||
|
protected final Camera cam; |
||||||
|
protected final boolean copyToRam; |
||||||
|
|
||||||
|
|
||||||
|
public GenericEnvBaker( |
||||||
|
RenderManager rm, |
||||||
|
AssetManager am, |
||||||
|
Format colorFormat, |
||||||
|
Format depthFormat, |
||||||
|
int env_size, |
||||||
|
boolean copyToRam |
||||||
|
){ |
||||||
|
this.copyToRam=copyToRam; |
||||||
|
this.depthFormat=depthFormat; |
||||||
|
|
||||||
|
renderManager=rm; |
||||||
|
assetManager=am; |
||||||
|
|
||||||
|
|
||||||
|
cam=new Camera(128,128); |
||||||
|
|
||||||
|
env=new TextureCubeMap(env_size,env_size,colorFormat); |
||||||
|
env.setMagFilter(MagFilter.Bilinear); |
||||||
|
env.setMinFilter(MinFilter.BilinearNoMipMaps); |
||||||
|
env.setWrap(WrapMode.EdgeClamp); |
||||||
|
env.getImage().setColorSpace(ColorSpace.Linear); |
||||||
|
} |
||||||
|
|
||||||
|
public TextureCubeMap getEnvMap(){ |
||||||
|
return env; |
||||||
|
} |
||||||
|
|
||||||
|
Camera getCam(int id,int w,int h,Vector3f position,float frustumNear,float frustumFar){ |
||||||
|
cam.resize(w,h,false); |
||||||
|
cam.setLocation(position); |
||||||
|
cam.setFrustumPerspective(90.0F,1F,frustumNear,frustumFar); |
||||||
|
cam.setRotation(new Quaternion().fromAxes(axisX[id],axisY[id],axisZ[id])); |
||||||
|
return cam; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void clean(){ |
||||||
|
env.getImage().dispose(); |
||||||
|
System.gc(); |
||||||
|
System.gc(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar) { |
||||||
|
FrameBuffer envbaker=new FrameBuffer(env.getImage().getWidth(),env.getImage().getHeight(),1); |
||||||
|
envbaker.setDepthTarget(FrameBuffer.newTarget(depthFormat)); |
||||||
|
envbaker.setSrgb(false); |
||||||
|
|
||||||
|
for(int i=0;i<6;i++) envbaker.addColorTarget(FrameBuffer.newTarget(env).face(TextureCubeMap.Face.values()[i])); |
||||||
|
|
||||||
|
for(int i=0;i<6;i++){ |
||||||
|
envbaker.setTargetIndex(i); |
||||||
|
|
||||||
|
ViewPort viewPort=new ViewPort("EnvBaker",getCam(i,envbaker.getWidth(),envbaker.getHeight(),position,frustumNear,frustumFar)); |
||||||
|
viewPort.setClearFlags(true,true,true); |
||||||
|
viewPort.setBackgroundColor(ColorRGBA.Pink); |
||||||
|
|
||||||
|
viewPort.setOutputFrameBuffer(envbaker); |
||||||
|
viewPort.clearScenes(); |
||||||
|
viewPort.attachScene(scene); |
||||||
|
|
||||||
|
scene.updateLogicalState(0); |
||||||
|
scene.updateModelBound(); |
||||||
|
scene.updateGeometricState(); |
||||||
|
|
||||||
|
renderManager.renderViewPort(viewPort,0.16f); |
||||||
|
|
||||||
|
if(copyToRam){ |
||||||
|
ByteBuffer face=BufferUtils.createByteBuffer( |
||||||
|
( |
||||||
|
env.getImage().getWidth()*env.getImage().getHeight()*( |
||||||
|
env.getImage().getFormat().getBitsPerPixel()/8 |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
renderManager.getRenderer().readFrameBufferWithFormat(envbaker, face,env.getImage().getFormat()); |
||||||
|
face.rewind(); |
||||||
|
env.getImage().setData(i,face); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
env.getImage().clearUpdateNeeded(); |
||||||
|
|
||||||
|
envbaker.dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package com.jme3.environment.baker; |
||||||
|
|
||||||
|
import com.jme3.texture.Texture2D; |
||||||
|
import com.jme3.texture.TextureCubeMap; |
||||||
|
|
||||||
|
/** |
||||||
|
* An environment baker, but this one is for Imaged Base Lighting |
||||||
|
* |
||||||
|
* @author Riccardo Balbo |
||||||
|
*/ |
||||||
|
public interface IBLEnvBaker extends EnvBaker{ |
||||||
|
public Texture2D genBRTF() ; |
||||||
|
|
||||||
|
public void bakeIrradiance(); |
||||||
|
public void bakeSpecularIBL() ; |
||||||
|
|
||||||
|
public TextureCubeMap getSpecularIBL(); |
||||||
|
public TextureCubeMap getIrradiance(); |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
package com.jme3.environment.baker; |
||||||
|
|
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.texture.TextureCubeMap; |
||||||
|
|
||||||
|
/** |
||||||
|
* An environment baker for IBL, that uses spherical harmonics for irradiance. |
||||||
|
* |
||||||
|
* @author Riccardo Balbo |
||||||
|
*/ |
||||||
|
public interface IBLEnvBakerLight extends EnvBaker{ |
||||||
|
public void bakeSpecularIBL(); |
||||||
|
public void bakeSphericalHarmonicsCoefficients(); |
||||||
|
|
||||||
|
public TextureCubeMap getSpecularIBL(); |
||||||
|
public Vector3f[] getSphericalHarmonicsCoefficients(); |
||||||
|
} |
@ -0,0 +1,182 @@ |
|||||||
|
package com.jme3.environment.baker; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.environment.baker.IBLEnvBaker; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.renderer.Camera; |
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.texture.FrameBuffer; |
||||||
|
import com.jme3.texture.Image.Format; |
||||||
|
import com.jme3.texture.Texture.MagFilter; |
||||||
|
import com.jme3.texture.Texture.MinFilter; |
||||||
|
import com.jme3.texture.Texture.WrapMode; |
||||||
|
import com.jme3.texture.Texture2D; |
||||||
|
import com.jme3.texture.TextureCubeMap; |
||||||
|
import com.jme3.texture.image.ColorSpace; |
||||||
|
import com.jme3.ui.Picture; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* An env baker for IBL that runs on the GPU |
||||||
|
* |
||||||
|
* @author Riccardo Balbo |
||||||
|
*/ |
||||||
|
public class IBLGLEnvBaker extends GenericEnvBaker implements IBLEnvBaker{ |
||||||
|
protected Texture2D brtf; |
||||||
|
protected TextureCubeMap irradiance; |
||||||
|
protected TextureCubeMap specular; |
||||||
|
|
||||||
|
public IBLGLEnvBaker(RenderManager rm,AssetManager am, |
||||||
|
Format format, |
||||||
|
Format depthFormat, |
||||||
|
int env_size,int specular_size, |
||||||
|
int irradiance_size, |
||||||
|
int brtf_size |
||||||
|
){ |
||||||
|
super(rm,am,format,depthFormat,env_size,false); |
||||||
|
|
||||||
|
irradiance=new TextureCubeMap(irradiance_size,irradiance_size,format); |
||||||
|
irradiance.setMagFilter(MagFilter.Bilinear); |
||||||
|
irradiance.setMinFilter(MinFilter.BilinearNoMipMaps); |
||||||
|
irradiance.setWrap(WrapMode.EdgeClamp); |
||||||
|
irradiance.getImage().setColorSpace(ColorSpace.Linear); |
||||||
|
|
||||||
|
specular=new TextureCubeMap(specular_size,specular_size,format); |
||||||
|
specular.setMagFilter(MagFilter.Bilinear); |
||||||
|
specular.setMinFilter(MinFilter.BilinearNoMipMaps); |
||||||
|
specular.setWrap(WrapMode.EdgeClamp); |
||||||
|
specular.getImage().setColorSpace(ColorSpace.Linear); |
||||||
|
int nbMipMaps=(int)(Math.log(specular_size)/Math.log(2)+1); |
||||||
|
if(nbMipMaps>6)nbMipMaps=6; |
||||||
|
int[] sizes=new int[nbMipMaps]; |
||||||
|
for(int i=0;i<nbMipMaps;i++){ |
||||||
|
int size=(int)FastMath.pow(2,nbMipMaps-1-i); |
||||||
|
sizes[i]=size*size*(specular.getImage().getFormat().getBitsPerPixel()/8); |
||||||
|
} |
||||||
|
specular.getImage().setMipMapSizes(sizes); |
||||||
|
|
||||||
|
brtf=new Texture2D(brtf_size,brtf_size,format); |
||||||
|
brtf.setMagFilter(MagFilter.Bilinear); |
||||||
|
brtf.setMinFilter(MinFilter.BilinearNoMipMaps); |
||||||
|
brtf.setWrap(WrapMode.EdgeClamp); |
||||||
|
brtf.getImage().setColorSpace(ColorSpace.Linear); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public TextureCubeMap getSpecularIBL(){ |
||||||
|
return specular; |
||||||
|
} |
||||||
|
|
||||||
|
public TextureCubeMap getIrradiance(){ |
||||||
|
return irradiance; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void bakeSpecularIBL() { |
||||||
|
Box boxm=new Box(1,1,1); |
||||||
|
Geometry screen=new Geometry("BakeBox",boxm); |
||||||
|
|
||||||
|
Material mat=new Material(assetManager,"Common/IBL/IBLKernels.j3md"); |
||||||
|
mat.setBoolean("UseSpecularIBL",true); |
||||||
|
mat.setTexture("EnvMap",env); |
||||||
|
screen.setMaterial(mat); |
||||||
|
|
||||||
|
for(int mip=0;mip<specular.getImage().getMipMapSizes().length;mip++){ |
||||||
|
int mipWidth=(int)(specular.getImage().getWidth()*FastMath.pow(0.5f,mip)); |
||||||
|
int mipHeight=(int)(specular.getImage().getHeight()*FastMath.pow(0.5f,mip)); |
||||||
|
|
||||||
|
FrameBuffer specularbaker=new FrameBuffer(mipWidth,mipHeight,1); |
||||||
|
specularbaker.setSrgb(false); |
||||||
|
|
||||||
|
for(int i=0;i<6;i++)specularbaker.addColorTarget( FrameBuffer.newTarget(specular).level(mip).face(i) ); |
||||||
|
|
||||||
|
float roughness=(float)mip/(float)(specular.getImage().getMipMapSizes().length-1); |
||||||
|
mat.setFloat("Roughness",roughness); |
||||||
|
|
||||||
|
for(int i=0;i<6;i++){ |
||||||
|
specularbaker.setTargetIndex(i); |
||||||
|
mat.setInt("FaceId",i); |
||||||
|
|
||||||
|
screen.updateLogicalState(0); |
||||||
|
screen.updateGeometricState(); |
||||||
|
|
||||||
|
renderManager.setCamera(getCam(i,specularbaker.getWidth(),specularbaker.getHeight(),Vector3f.ZERO,1,1000),false); |
||||||
|
renderManager.getRenderer().setFrameBuffer(specularbaker); |
||||||
|
renderManager.renderGeometry(screen); |
||||||
|
} |
||||||
|
specularbaker.dispose(); |
||||||
|
} |
||||||
|
specular.setMinFilter(MinFilter.Trilinear); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Texture2D genBRTF() { |
||||||
|
|
||||||
|
Picture screen=new Picture("BakeScreen",true); |
||||||
|
screen.setWidth(1); |
||||||
|
screen.setHeight(1); |
||||||
|
|
||||||
|
FrameBuffer brtfbaker=new FrameBuffer(brtf.getImage().getWidth(),brtf.getImage().getHeight(),1); |
||||||
|
brtfbaker.setSrgb(false); |
||||||
|
brtfbaker.addColorTarget(FrameBuffer.newTarget(brtf)); |
||||||
|
|
||||||
|
Camera envcam=getCam(0,brtf.getImage().getWidth(),brtf.getImage().getHeight(),Vector3f.ZERO,1,1000); |
||||||
|
|
||||||
|
Material mat=new Material(assetManager,"Common/IBL/IBLKernels.j3md"); |
||||||
|
mat.setBoolean("UseBRDF",true); |
||||||
|
screen.setMaterial(mat); |
||||||
|
|
||||||
|
renderManager.getRenderer().setFrameBuffer(brtfbaker); |
||||||
|
renderManager.setCamera(envcam,false); |
||||||
|
|
||||||
|
screen.updateLogicalState(0); |
||||||
|
screen.updateGeometricState(); |
||||||
|
renderManager.renderGeometry(screen); |
||||||
|
|
||||||
|
brtfbaker.dispose(); |
||||||
|
|
||||||
|
return brtf; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void bakeIrradiance() { |
||||||
|
|
||||||
|
Box boxm=new Box(1,1,1); |
||||||
|
Geometry screen=new Geometry("BakeBox",boxm); |
||||||
|
|
||||||
|
|
||||||
|
FrameBuffer irradiancebaker=new FrameBuffer(irradiance.getImage().getWidth(),irradiance.getImage().getHeight(),1); |
||||||
|
irradiancebaker.setSrgb(false); |
||||||
|
|
||||||
|
for(int i=0;i<6;i++) irradiancebaker.addColorTarget(FrameBuffer.newTarget(irradiance).face(TextureCubeMap.Face.values()[i])); |
||||||
|
|
||||||
|
Material mat=new Material(assetManager,"Common/IBL/IBLKernels.j3md"); |
||||||
|
mat.setBoolean("UseIrradiance",true); |
||||||
|
mat.setTexture("EnvMap",env); |
||||||
|
screen.setMaterial(mat); |
||||||
|
|
||||||
|
for(int i=0;i<6;i++){ |
||||||
|
irradiancebaker.setTargetIndex(i); |
||||||
|
|
||||||
|
mat.setInt("FaceId",i); |
||||||
|
|
||||||
|
screen.updateLogicalState(0); |
||||||
|
screen.updateGeometricState(); |
||||||
|
|
||||||
|
renderManager.setCamera( |
||||||
|
getCam(i,irradiancebaker.getWidth(),irradiancebaker.getHeight(),Vector3f.ZERO,1,1000) |
||||||
|
,false); |
||||||
|
renderManager.getRenderer().setFrameBuffer(irradiancebaker); |
||||||
|
renderManager.renderGeometry(screen); |
||||||
|
} |
||||||
|
|
||||||
|
irradiancebaker.dispose(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,104 @@ |
|||||||
|
package com.jme3.environment.baker; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.environment.baker.IBLEnvBakerLight; |
||||||
|
import com.jme3.environment.util.EnvMapUtils; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.texture.FrameBuffer; |
||||||
|
import com.jme3.texture.TextureCubeMap; |
||||||
|
import com.jme3.texture.Image.Format; |
||||||
|
import com.jme3.texture.Texture.MagFilter; |
||||||
|
import com.jme3.texture.Texture.MinFilter; |
||||||
|
import com.jme3.texture.Texture.WrapMode; |
||||||
|
import com.jme3.texture.image.ColorSpace; |
||||||
|
|
||||||
|
/** |
||||||
|
* An env baker for IBL that runs on the GPU |
||||||
|
* |
||||||
|
* @author Riccardo Balbo |
||||||
|
*/ |
||||||
|
public class IBLGLEnvBakerLight extends GenericEnvBaker implements IBLEnvBakerLight{ |
||||||
|
protected TextureCubeMap specular; |
||||||
|
protected Vector3f[] shCoef; |
||||||
|
public IBLGLEnvBakerLight(RenderManager rm,AssetManager am, |
||||||
|
Format format, |
||||||
|
Format depthFormat, |
||||||
|
int env_size, |
||||||
|
int specular_size |
||||||
|
|
||||||
|
){ |
||||||
|
super(rm,am,format,depthFormat,env_size,true); |
||||||
|
|
||||||
|
specular=new TextureCubeMap(specular_size,specular_size,format); |
||||||
|
specular.setMagFilter(MagFilter.Bilinear); |
||||||
|
specular.setMinFilter(MinFilter.BilinearNoMipMaps); |
||||||
|
specular.setWrap(WrapMode.EdgeClamp); |
||||||
|
specular.getImage().setColorSpace(ColorSpace.Linear); |
||||||
|
int nbMipMaps=(int)(Math.log(specular_size)/Math.log(2)+1); |
||||||
|
if(nbMipMaps>6)nbMipMaps=6; |
||||||
|
int[] sizes=new int[nbMipMaps]; |
||||||
|
for(int i=0;i<nbMipMaps;i++){ |
||||||
|
int size=(int)FastMath.pow(2,nbMipMaps-1-i); |
||||||
|
sizes[i]=size*size*(specular.getImage().getFormat().getBitsPerPixel()/8); |
||||||
|
} |
||||||
|
specular.getImage().setMipMapSizes(sizes); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void bakeSpecularIBL() { |
||||||
|
Box boxm=new Box(1,1,1); |
||||||
|
Geometry screen=new Geometry("BakeBox",boxm); |
||||||
|
|
||||||
|
Material mat=new Material(assetManager,"Common/IBL/IBLKernels.j3md"); |
||||||
|
mat.setBoolean("UseSpecularIBL",true); |
||||||
|
mat.setTexture("EnvMap",env); |
||||||
|
screen.setMaterial(mat); |
||||||
|
|
||||||
|
for(int mip=0;mip<specular.getImage().getMipMapSizes().length;mip++){ |
||||||
|
int mipWidth=(int)(specular.getImage().getWidth()*FastMath.pow(0.5f,mip)); |
||||||
|
int mipHeight=(int)(specular.getImage().getHeight()*FastMath.pow(0.5f,mip)); |
||||||
|
|
||||||
|
FrameBuffer specularbaker=new FrameBuffer(mipWidth,mipHeight,1); |
||||||
|
specularbaker.setSrgb(false); |
||||||
|
for(int i=0;i<6;i++)specularbaker.addColorTarget( FrameBuffer.newTarget(specular).level(mip).face(i) ); |
||||||
|
|
||||||
|
float roughness=(float)mip/(float)(specular.getImage().getMipMapSizes().length-1); |
||||||
|
mat.setFloat("Roughness",roughness); |
||||||
|
|
||||||
|
for(int i=0;i<6;i++){ |
||||||
|
specularbaker.setTargetIndex(i); |
||||||
|
mat.setInt("FaceId",i); |
||||||
|
|
||||||
|
screen.updateLogicalState(0); |
||||||
|
screen.updateGeometricState(); |
||||||
|
|
||||||
|
renderManager.setCamera(getCam(i,specularbaker.getWidth(),specularbaker.getHeight(),Vector3f.ZERO,1,1000),false); |
||||||
|
renderManager.getRenderer().setFrameBuffer(specularbaker); |
||||||
|
renderManager.renderGeometry(screen); |
||||||
|
} |
||||||
|
specularbaker.dispose(); |
||||||
|
} |
||||||
|
specular.setMinFilter(MinFilter.Trilinear); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public TextureCubeMap getSpecularIBL() { |
||||||
|
return specular; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void bakeSphericalHarmonicsCoefficients() { |
||||||
|
shCoef=EnvMapUtils.getSphericalHarmonicsCoefficents(getEnvMap()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Vector3f[] getSphericalHarmonicsCoefficients(){ |
||||||
|
return shCoef; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
/** |
||||||
|
* This code is based on the following articles: |
||||||
|
* https://learnopengl.com/PBR/IBL/Diffuse-irradiance |
||||||
|
* https://learnopengl.com/PBR/IBL/Specular-IBL |
||||||
|
* - Riccardo Balbo |
||||||
|
*/ |
||||||
|
#import "Common/IBL/Math.glsllib" |
||||||
|
|
||||||
|
out vec4 outFragColor; |
||||||
|
in vec2 TexCoords; |
||||||
|
in vec3 LocalPos; |
||||||
|
|
||||||
|
uniform samplerCube m_EnvMap; |
||||||
|
uniform float m_Roughness; |
||||||
|
uniform int m_FaceId; |
||||||
|
|
||||||
|
void brdfKernel(){ |
||||||
|
float NdotV=TexCoords.x; |
||||||
|
float m_Roughness=TexCoords.y; |
||||||
|
vec3 V; |
||||||
|
V.x = sqrt(1.0 - NdotV*NdotV); |
||||||
|
V.y = 0.0; |
||||||
|
V.z = NdotV; |
||||||
|
float A = 0.0; |
||||||
|
float B = 0.0; |
||||||
|
vec3 N = vec3(0.0, 0.0, 1.0); |
||||||
|
const uint SAMPLE_COUNT = 1024u; |
||||||
|
for(uint i = 0u; i < SAMPLE_COUNT; i++){ |
||||||
|
vec2 Xi = Hammersley(i, SAMPLE_COUNT); |
||||||
|
vec3 H = ImportanceSampleGGX(Xi, N, m_Roughness); |
||||||
|
vec3 L = normalize(2.0 * dot(V, H) * H - V); |
||||||
|
float NdotL = max(L.z, 0.0); |
||||||
|
float NdotH = max(H.z, 0.0); |
||||||
|
float VdotH = max(dot(V, H), 0.0); |
||||||
|
if(NdotL > 0.0){ |
||||||
|
float G = GeometrySmith(N, V, L, m_Roughness); |
||||||
|
float G_Vis = (G * VdotH) / (NdotH * NdotV); |
||||||
|
float Fc = pow(1.0 - VdotH, 5.0); |
||||||
|
A += (1.0 - Fc) * G_Vis; |
||||||
|
B += Fc * G_Vis; |
||||||
|
} |
||||||
|
} |
||||||
|
A /= float(SAMPLE_COUNT); |
||||||
|
B /= float(SAMPLE_COUNT); |
||||||
|
outFragColor.rg=vec2(A, B); |
||||||
|
outFragColor.ba=vec2(0); |
||||||
|
} |
||||||
|
|
||||||
|
void irradianceKernel(){ |
||||||
|
// the sample direction equals the hemisphere's orientation |
||||||
|
vec3 N = normalize(LocalPos); |
||||||
|
vec3 irradiance = vec3(0.0); |
||||||
|
vec3 up = vec3(0.0, 1.0, 0.0); |
||||||
|
vec3 right = cross(up, N); |
||||||
|
up = cross(N, right); |
||||||
|
float sampleDelta = 0.025; |
||||||
|
float nrSamples = 0.0; |
||||||
|
for(float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta){ |
||||||
|
for(float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta){ |
||||||
|
// spherical to cartesian (in tangent space) |
||||||
|
vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); |
||||||
|
// tangent space to world |
||||||
|
vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; |
||||||
|
irradiance += texture(m_EnvMap, sampleVec).rgb * cos(theta) * sin(theta); |
||||||
|
nrSamples++; |
||||||
|
} |
||||||
|
} |
||||||
|
irradiance = PI * irradiance * (1.0 / float(nrSamples)); |
||||||
|
outFragColor = vec4(irradiance, 1.0); |
||||||
|
} |
||||||
|
|
||||||
|
void prefilteredEnvKernel(){ |
||||||
|
vec3 N = normalize(LocalPos); |
||||||
|
vec3 R = N; |
||||||
|
vec3 V = R; |
||||||
|
const uint SAMPLE_COUNT = 1024u; |
||||||
|
float totalWeight = 0.0; |
||||||
|
vec3 prefilteredColor = vec3(0.0); |
||||||
|
for(uint i = 0u; i < SAMPLE_COUNT; ++i) { |
||||||
|
vec2 Xi = Hammersley(i, SAMPLE_COUNT); |
||||||
|
vec3 H = ImportanceSampleGGX(Xi, N, m_Roughness); |
||||||
|
vec3 L = normalize(2.0 * dot(V, H) * H - V); |
||||||
|
float NdotL = max(dot(N, L), 0.0); |
||||||
|
if(NdotL > 0.0) { |
||||||
|
prefilteredColor += texture(m_EnvMap, L).rgb * NdotL; |
||||||
|
totalWeight += NdotL; |
||||||
|
} |
||||||
|
} |
||||||
|
prefilteredColor = prefilteredColor / totalWeight; |
||||||
|
outFragColor = vec4(prefilteredColor, 1.0); |
||||||
|
} |
||||||
|
|
||||||
|
void main(){ |
||||||
|
#if defined(SIBL) |
||||||
|
prefilteredEnvKernel(); |
||||||
|
#elif defined(IRRADIANCE) |
||||||
|
irradianceKernel(); |
||||||
|
#else |
||||||
|
brdfKernel(); |
||||||
|
#endif |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
MaterialDef IBLKernels { |
||||||
|
|
||||||
|
MaterialParameters { |
||||||
|
TextureCubeMap EnvMap -LINEAR |
||||||
|
Float Roughness |
||||||
|
Int FaceId : 0 |
||||||
|
Boolean UseBRDF |
||||||
|
Boolean UseIrradiance |
||||||
|
Boolean UseSpecularIBL |
||||||
|
} |
||||||
|
|
||||||
|
Technique { |
||||||
|
|
||||||
|
VertexShader GLSL150: Common/IBL/IBLKernels.vert |
||||||
|
FragmentShader GLSL150: Common/IBL/IBLKernels.frag |
||||||
|
|
||||||
|
WorldParameters { |
||||||
|
WorldMatrix |
||||||
|
ViewMatrix |
||||||
|
ProjectionMatrix |
||||||
|
} |
||||||
|
|
||||||
|
RenderState { |
||||||
|
DepthWrite Off |
||||||
|
DepthTest Off |
||||||
|
DepthFunc Equal |
||||||
|
FaceCull Off |
||||||
|
} |
||||||
|
|
||||||
|
Defines { |
||||||
|
BRDF:UseBRDF |
||||||
|
IRRADIANCE: UseIrradiance |
||||||
|
SIBL: UseSpecularIBL |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
/** |
||||||
|
* This code is based on the following articles: |
||||||
|
* https://learnopengl.com/PBR/IBL/Diffuse-irradiance |
||||||
|
* https://learnopengl.com/PBR/IBL/Specular-IBL |
||||||
|
* - Riccardo Balbo |
||||||
|
*/ |
||||||
|
in vec3 inPosition; |
||||||
|
in vec2 inTexCoord; |
||||||
|
in vec3 inNormal; |
||||||
|
|
||||||
|
out vec2 TexCoords; |
||||||
|
out vec3 LocalPos; |
||||||
|
|
||||||
|
uniform mat4 g_ViewMatrix; |
||||||
|
uniform mat4 g_WorldMatrix; |
||||||
|
uniform mat4 g_ProjectionMatrix; |
||||||
|
|
||||||
|
void main() { |
||||||
|
LocalPos = inPosition.xyz; |
||||||
|
TexCoords = inTexCoord.xy; |
||||||
|
#ifdef BRDF |
||||||
|
vec2 pos = inPosition.xy * 2.0 - 1.0; |
||||||
|
gl_Position = vec4(pos, 0.0, 1.0); |
||||||
|
#else |
||||||
|
mat4 rotView = mat4(mat3(g_ViewMatrix)); // remove translation from the view matrix |
||||||
|
vec4 clipPos = g_ProjectionMatrix * rotView * vec4(LocalPos, 1.0); |
||||||
|
gl_Position = clipPos.xyww; |
||||||
|
#endif |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
/** |
||||||
|
* This code is based on the following articles: |
||||||
|
* https://learnopengl.com/PBR/IBL/Diffuse-irradiance |
||||||
|
* https://learnopengl.com/PBR/IBL/Specular-IBL |
||||||
|
* - Riccardo Balbo |
||||||
|
*/ |
||||||
|
const float PI = 3.14159265359; |
||||||
|
|
||||||
|
float RadicalInverse_VdC(uint bits) { |
||||||
|
bits = (bits << 16u) | (bits >> 16u); |
||||||
|
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); |
||||||
|
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); |
||||||
|
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); |
||||||
|
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); |
||||||
|
return float(bits) * 2.3283064365386963e-10; // / 0x100000000 |
||||||
|
} |
||||||
|
|
||||||
|
vec2 Hammersley(uint i, uint N){ |
||||||
|
return vec2(float(i)/float(N), RadicalInverse_VdC(i)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness){ |
||||||
|
float a = roughness*roughness; |
||||||
|
|
||||||
|
float phi = 2.0 * PI * Xi.x; |
||||||
|
float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); |
||||||
|
float sinTheta = sqrt(1.0 - cosTheta*cosTheta); |
||||||
|
|
||||||
|
// from spherical coordinates to cartesian coordinates |
||||||
|
vec3 H; |
||||||
|
H.x = cos(phi) * sinTheta; |
||||||
|
H.y = sin(phi) * sinTheta; |
||||||
|
H.z = cosTheta; |
||||||
|
|
||||||
|
// from tangent-space vector to world-space sample vector |
||||||
|
vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); |
||||||
|
vec3 tangent = normalize(cross(up, N)); |
||||||
|
vec3 bitangent = cross(N, tangent); |
||||||
|
|
||||||
|
vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z; |
||||||
|
return normalize(sampleVec); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
float GeometrySchlickGGX(float NdotV, float roughness){ |
||||||
|
float a = roughness; |
||||||
|
float k = (a * a) / 2.0; |
||||||
|
|
||||||
|
float nom = NdotV; |
||||||
|
float denom = NdotV * (1.0 - k) + k; |
||||||
|
|
||||||
|
return nom / denom; |
||||||
|
} |
||||||
|
// ---------------------------------------------------------------------------- |
||||||
|
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness){ |
||||||
|
float NdotV = max(dot(N, V), 0.0); |
||||||
|
float NdotL = max(dot(N, L), 0.0); |
||||||
|
float ggx2 = GeometrySchlickGGX(NdotV, roughness); |
||||||
|
float ggx1 = GeometrySchlickGGX(NdotL, roughness); |
||||||
|
|
||||||
|
return ggx1 * ggx2; |
||||||
|
} |
||||||
|
|
Loading…
Reference in new issue