com.jme3.environment.baker.IBLGLEnvBaker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jme3-core Show documentation
Show all versions of jme3-core Show documentation
jMonkeyEngine is a 3-D game engine for adventurous Java developers
The newest version!
/*
* Copyright (c) 2009-2024 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.baker;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.asset.AssetManager;
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.FrameBuffer.FrameBufferTarget;
import com.jme3.texture.image.ColorSpace;
import com.jme3.ui.Picture;
/**
* Fully accelerated env baker for IBL that runs entirely on the GPU
*
* @author Riccardo Balbo
*/
public class IBLGLEnvBaker extends GenericEnvBaker implements IBLEnvBaker {
private static final Logger LOGGER = Logger.getLogger(IBLGLEnvBakerLight.class.getName());
protected Texture2D brtf;
protected TextureCubeMap irradiance;
protected TextureCubeMap specular;
/**
* Create a new IBL env baker
* @param rm The render manager used to render the env scene
* @param am The asset manager used to load the baking shaders
* @param format The format of the color buffers
* @param depthFormat The format of the depth buffers
* @param env_size The size in pixels of the output environment cube map (eg. 1024)
* @param specular_size The size in pixels of the output specular cube map (eg. 1024)
* @param irradiance_size The size in pixels of the output irradiance cube map (eg. 512)
* @param brtf_size The size in pixels of the output brtf map (eg. 512)
*/
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);
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.Trilinear);
specular.setWrap(WrapMode.EdgeClamp);
specular.getImage().setColorSpace(ColorSpace.Linear);
int nbMipMaps = (int) (Math.log(specular_size) / Math.log(2) + 1);
nbMipMaps = limitMips(nbMipMaps, specular.getImage().getWidth(), specular.getImage().getHeight(), rm);
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;
}
private void bakeSpecularIBL(int mip, float roughness, Material mat, Geometry screen) throws Exception {
mat.setFloat("Roughness", roughness);
int mipWidth = (int) (specular.getImage().getWidth() * FastMath.pow(0.5f, mip));
int mipHeight = (int) (specular.getImage().getHeight() * FastMath.pow(0.5f, mip));
FrameBuffer specularbakers[] = new FrameBuffer[6];
for (int i = 0; i < 6; i++) {
specularbakers[i] = new FrameBuffer(mipWidth, mipHeight, 1);
specularbakers[i].setSrgb(false);
specularbakers[i].addColorTarget(FrameBufferTarget.newTarget(specular).level(mip).face(i));
specularbakers[i].setMipMapsGenerationHint(false);
}
for (int i = 0; i < 6; i++) {
FrameBuffer specularbaker = specularbakers[i];
mat.setInt("FaceId", i);
screen.updateLogicalState(0);
screen.updateGeometricState();
renderManager.setCamera(updateAndGetInternalCamera(i, specularbaker.getWidth(), specularbaker.getHeight(), Vector3f.ZERO, 1, 1000), false);
renderManager.getRenderer().setFrameBuffer(specularbaker);
renderManager.renderGeometry(screen);
if (isTexturePulling()) {
pull(specularbaker, specular, i);
}
}
for (int i = 0; i < 6; i++) {
specularbakers[i].dispose();
}
}
@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", envMap);
screen.setMaterial(mat);
if (isTexturePulling()) {
startPulling();
}
int mip = 0;
for (; mip < specular.getImage().getMipMapSizes().length; mip++) {
try {
float roughness = (float) mip / (float) (specular.getImage().getMipMapSizes().length - 1);
bakeSpecularIBL(mip, roughness, mat, screen);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Error while computing mip level " + mip, e);
break;
}
}
if (mip < specular.getImage().getMipMapSizes().length) {
int[] sizes = specular.getImage().getMipMapSizes();
sizes = Arrays.copyOf(sizes, mip);
specular.getImage().setMipMapSizes(sizes);
specular.getImage().setMipmapsGenerated(true);
if (sizes.length <= 1) {
try {
LOGGER.log(Level.WARNING, "Workaround driver BUG: only one mip level available, regenerate it with higher roughness (shiny fix)");
bakeSpecularIBL(0, 1f, mat, screen);
} catch (Exception e) {
LOGGER.log(Level.FINE, "Error while recomputing mip level 0", e);
}
}
}
if (isTexturePulling()) {
endPulling(specular);
}
specular.getImage().clearUpdateNeeded();
}
@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(FrameBufferTarget.newTarget(brtf));
if (isTexturePulling()) {
startPulling();
}
Camera envcam = updateAndGetInternalCamera(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);
if (isTexturePulling()) {
pull(brtfbaker, brtf, 0);
}
brtfbaker.dispose();
if (isTexturePulling()) {
endPulling(brtf);
}
brtf.getImage().clearUpdateNeeded();
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);
if (isTexturePulling()) {
startPulling();
}
for (int i = 0; i < 6; i++) {
irradiancebaker.addColorTarget(
FrameBufferTarget.newTarget(irradiance).face(TextureCubeMap.Face.values()[i]));
}
Material mat = new Material(assetManager, "Common/IBL/IBLKernels.j3md");
mat.setBoolean("UseIrradiance", true);
mat.setTexture("EnvMap", envMap);
screen.setMaterial(mat);
for (int i = 0; i < 6; i++) {
irradiancebaker.setTargetIndex(i);
mat.setInt("FaceId", i);
screen.updateLogicalState(0);
screen.updateGeometricState();
renderManager.setCamera(updateAndGetInternalCamera(i, irradiancebaker.getWidth(), irradiancebaker.getHeight(), Vector3f.ZERO, 1, 1000), false);
renderManager.getRenderer().setFrameBuffer(irradiancebaker);
renderManager.renderGeometry(screen);
if (isTexturePulling()) {
pull(irradiancebaker, irradiance, i);
}
}
irradiancebaker.dispose();
if (isTexturePulling()) {
endPulling(irradiance);
}
irradiance.getImage().clearUpdateNeeded();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy