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