From b88be511f1d826f0ce239fde31dee3ef2ba6ec3b Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 8 Aug 2023 09:55:47 +0200 Subject: [PATCH 01/29] Accelerated env baker that runs on the GPU. --- .../jme3/environment/LightProbeFactory2.java | 98 ++++++++++ .../com/jme3/environment/baker/EnvBaker.java | 16 ++ .../environment/baker/GenericEnvBaker.java | 165 ++++++++++++++++ .../jme3/environment/baker/IBLEnvBaker.java | 19 ++ .../environment/baker/IBLEnvBakerLight.java | 17 ++ .../jme3/environment/baker/IBLGLEnvBaker.java | 182 ++++++++++++++++++ .../environment/baker/IBLGLEnvBakerLight.java | 104 ++++++++++ .../main/resources/Common/IBL/IBLKernels.frag | 101 ++++++++++ .../main/resources/Common/IBL/IBLKernels.j3md | 37 ++++ .../main/resources/Common/IBL/IBLKernels.vert | 29 +++ .../main/resources/Common/IBL/Math.glsllib | 66 +++++++ .../jme3test/light/pbr/TestPBRLighting.java | 27 ++- 12 files changed, 851 insertions(+), 10 deletions(-) create mode 100644 jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java create mode 100644 jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java create mode 100644 jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java create mode 100644 jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java create mode 100644 jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBakerLight.java create mode 100644 jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java create mode 100644 jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java create mode 100644 jme3-core/src/main/resources/Common/IBL/IBLKernels.frag create mode 100644 jme3-core/src/main/resources/Common/IBL/IBLKernels.j3md create mode 100644 jme3-core/src/main/resources/Common/IBL/IBLKernels.vert create mode 100644 jme3-core/src/main/resources/Common/IBL/Math.glsllib diff --git a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java new file mode 100644 index 0000000000..543682d6fe --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java @@ -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; + } + + + +} diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java new file mode 100644 index 0000000000..5b73b6824a --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java @@ -0,0 +1,16 @@ +package com.jme3.environment.baker; + +import com.jme3.math.Vector3f; +import com.jme3.scene.Spatial; +import com.jme3.texture.TextureCubeMap; + +/** + * And environment baker. It bakes the environment. ( ͡° ͜ʖ ͡°) + * + * @author Riccardo Balbo + */ +public interface EnvBaker { + public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar); + public TextureCubeMap getEnvMap(); + public void clean(); +} \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java new file mode 100644 index 0000000000..096a66d146 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java @@ -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(); + } + + +} \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java new file mode 100644 index 0000000000..cef731dcd7 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java @@ -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(); +} \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBakerLight.java new file mode 100644 index 0000000000..7949714300 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBakerLight.java @@ -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(); +} \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java new file mode 100644 index 0000000000..83de93808c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java @@ -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;i6)nbMipMaps=6; + int[] sizes=new int[nbMipMaps]; + for(int i=0;i 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 +} \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/IBL/IBLKernels.j3md b/jme3-core/src/main/resources/Common/IBL/IBLKernels.j3md new file mode 100644 index 0000000000..257fde915d --- /dev/null +++ b/jme3-core/src/main/resources/Common/IBL/IBLKernels.j3md @@ -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 + } + + } +} \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/IBL/IBLKernels.vert b/jme3-core/src/main/resources/Common/IBL/IBLKernels.vert new file mode 100644 index 0000000000..6bd2599719 --- /dev/null +++ b/jme3-core/src/main/resources/Common/IBL/IBLKernels.vert @@ -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 +} \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/IBL/Math.glsllib b/jme3-core/src/main/resources/Common/IBL/Math.glsllib new file mode 100644 index 0000000000..abc4190afa --- /dev/null +++ b/jme3-core/src/main/resources/Common/IBL/Math.glsllib @@ -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; +} + \ No newline at end of file diff --git a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java index 7de1e456c7..bd6d9f5b59 100644 --- a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java +++ b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java @@ -34,6 +34,7 @@ import com.jme3.app.SimpleApplication; import com.jme3.environment.EnvironmentCamera; import com.jme3.environment.LightProbeFactory; +import com.jme3.environment.LightProbeFactory2; import com.jme3.environment.generation.JobProgressAdapter; import com.jme3.environment.util.EnvMapUtils; import com.jme3.environment.util.LightsDebugState; @@ -59,7 +60,8 @@ * @author nehon */ public class TestPBRLighting extends SimpleApplication { - + private static final boolean USE_ACCELERATED_BAKING=true; + private static final int RESOLUTION=256; public static void main(String[] args) { TestPBRLighting app = new TestPBRLighting(); app.start(); @@ -111,7 +113,7 @@ public void simpleInitApp() { model.setMaterial(pbrMat); - final EnvironmentCamera envCam = new EnvironmentCamera(256, new Vector3f(0, 3f, 0)); + final EnvironmentCamera envCam = new EnvironmentCamera(RESOLUTION, new Vector3f(0, 3f, 0)); stateManager.attach(envCam); // EnvironmentManager envManager = new EnvironmentManager(); @@ -199,18 +201,23 @@ public void simpleUpdate(float tpf) { if (frame == 2) { modelNode.removeFromParent(); - final LightProbe probe = LightProbeFactory.makeProbe(stateManager.getState(EnvironmentCamera.class), rootNode, new JobProgressAdapter() { + LightProbe probe; - @Override - public void done(LightProbe result) { - System.err.println("Done rendering env maps"); - tex = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(result.getPrefilteredEnvMap(), assetManager); - } - }); + if (USE_ACCELERATED_BAKING) { + probe = LightProbeFactory2.makeProbe(renderManager, assetManager, RESOLUTION, Vector3f.ZERO, 1f, 1000f, rootNode); + } else { + probe = LightProbeFactory.makeProbe(stateManager.getState(EnvironmentCamera.class), rootNode, new JobProgressAdapter() { + + @Override + public void done(LightProbe result) { + System.err.println("Done rendering env maps"); + tex = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(result.getPrefilteredEnvMap(), assetManager); + } + }); + } probe.getArea().setRadius(100); rootNode.addLight(probe); //getStateManager().getState(EnvironmentManager.class).addEnvProbe(probe); - } if (frame > 10 && modelNode.getParent() == null) { rootNode.attachChild(modelNode); From 0c8801fcc19804b9d8391e794340c3460906e15b Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 8 Aug 2023 09:56:54 +0200 Subject: [PATCH 02/29] Add render filter --- .../src/main/java/com/jme3/renderer/RenderManager.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java index 5969a1d5bd..311b26284d 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java @@ -66,6 +66,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.Function; import java.util.logging.Logger; /** @@ -104,7 +105,15 @@ public class RenderManager { private TechniqueDef.LightMode preferredLightMode = TechniqueDef.LightMode.MultiPass; private int singlePassLightBatchSize = 1; private MatParamOverride boundDrawBufferId=new MatParamOverride(VarType.Int,"BoundDrawBuffer",0); + private Function renderFilter; + public void setRenderFilter(Function filter){ + renderFilter=filter; + } + + public Function getRenderFilter(){ + return renderFilter; + } /** * Creates a high-level rendering interface over the @@ -626,6 +635,7 @@ public void updateUniformBindings(Shader shader) { * @see com.jme3.material.Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) */ public void renderGeometry(Geometry geom) { + if (renderFilter != null && !renderFilter.apply(geom)) return; this.renderer.pushDebugGroup(geom.getName()); if (geom.isIgnoreTransform()) { setWorldMatrix(Matrix4f.IDENTITY); From 616c052db8adeff6dff6d7cf795312ade8b04ecb Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 8 Aug 2023 09:57:58 +0200 Subject: [PATCH 03/29] EnvironmentProbeControl, for simpler env baking --- .../environment/EnvironmentProbeControl.java | 104 ++++++++++++++++++ .../jme3/environment/LightProbeFactory2.java | 3 +- .../com/jme3/environment/baker/EnvBaker.java | 5 +- .../environment/baker/GenericEnvBaker.java | 8 +- .../jme3test/light/pbr/TestPBRSimple.java | 40 +++++++ 5 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java create mode 100644 jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java new file mode 100644 index 0000000000..ec43733dd0 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -0,0 +1,104 @@ +package com.jme3.environment; + +import java.util.function.Function; +import java.util.function.Predicate; + +import com.jme3.asset.AssetManager; +import com.jme3.environment.baker.IBLGLEnvBakerLight; +import com.jme3.light.LightProbe; +import com.jme3.math.Vector3f; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.scene.Geometry; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.control.Control; +import com.jme3.texture.Image.Format; + +/** + * SmartLightProbe + */ +public class EnvironmentProbeControl extends LightProbe implements Control { + + + + RenderManager renderManager; + AssetManager assetManager; + int envMapSize; + Spatial spatial; + boolean BAKE_NEEDED=true; + Function filter=(s)->{ + return s.getUserData("tags.env")!=null; + }; + + public static void tag(Spatial s){ + if(s instanceof Node){ + Node n=(Node)s; + for(Spatial sx:n.getChildren()){ + tag(sx); + } + }else if(s instanceof Geometry){ + s.setUserData("tags.env", true); + } + } + + public EnvironmentProbeControl(RenderManager rm,AssetManager am, int size){ + renderManager=rm; + assetManager=am; + envMapSize=size; + } + + @Override + public Control cloneForSpatial(Spatial spatial) { + return null; + } + + @Override + public void setSpatial(Spatial spatial) { + + spatial.addLight(this); + this.spatial=spatial; + + } + + @Override + public void update(float tpf) { + + } + + @Override + public void render(RenderManager rm, ViewPort vp) { + if(BAKE_NEEDED){ + BAKE_NEEDED=false; + rebakeNow(); + } + } + + public void rebake(){ + BAKE_NEEDED=true; + } + + void rebakeNow() { + System.out.println("BAKE"); + + IBLGLEnvBakerLight baker = new IBLGLEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, + envMapSize, envMapSize); + + + baker.bakeEnvironment(spatial, Vector3f.ZERO, 0.001f, 1000f,filter); + baker.bakeSpecularIBL(); + baker.bakeSphericalHarmonicsCoefficients(); + + + // probe.setPosition(Vector3f.ZERO); + setPrefilteredMap(baker.getSpecularIBL()); + setNbMipMaps(getPrefilteredEnvMap().getImage().getMipMapSizes().length); + setShCoeffs(baker.getSphericalHarmonicsCoefficients()); + setPosition(Vector3f.ZERO); + setReady(true); + + baker.clean(); + + } + +} \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java index 543682d6fe..6e28d1cbed 100644 --- a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java +++ b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java @@ -51,7 +51,7 @@ public static LightProbe makeProbe(RenderManager rm, am, Format.RGB16F, Format.Depth, size, size); - baker.bakeEnvironment(scene,pos, frustumNear,frustumFar); + baker.bakeEnvironment(scene,pos, frustumNear,frustumFar,null); baker.bakeSpecularIBL(); baker.bakeSphericalHarmonicsCoefficients(); @@ -72,7 +72,6 @@ public static LightProbe makeProbe(RenderManager rm, - /** * 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. diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java index 5b73b6824a..7df8f68d23 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java @@ -1,6 +1,9 @@ package com.jme3.environment.baker; +import java.util.function.Function; + import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; import com.jme3.texture.TextureCubeMap; @@ -10,7 +13,7 @@ * @author Riccardo Balbo */ public interface EnvBaker { - public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar); + public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar,Function filter); public TextureCubeMap getEnvMap(); public void clean(); } \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java index 096a66d146..4046f7534e 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java @@ -4,6 +4,7 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.HashMap; +import java.util.function.Function; import com.jme3.asset.AssetManager; import com.jme3.environment.baker.EnvBaker; @@ -14,6 +15,7 @@ import com.jme3.renderer.Camera; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; +import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; import com.jme3.texture.FrameBuffer; import com.jme3.texture.Image.Format; @@ -117,7 +119,7 @@ public void clean(){ @Override - public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar) { + public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar,Function filter) { FrameBuffer envbaker=new FrameBuffer(env.getImage().getWidth(),env.getImage().getHeight(),1); envbaker.setDepthTarget(FrameBuffer.newTarget(depthFormat)); envbaker.setSrgb(false); @@ -138,8 +140,12 @@ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNea scene.updateLogicalState(0); scene.updateModelBound(); scene.updateGeometricState(); + + Function ofilter= renderManager.getRenderFilter(); + renderManager.setRenderFilter(filter); renderManager.renderViewPort(viewPort,0.16f); + renderManager.setRenderFilter(ofilter); if(copyToRam){ ByteBuffer face=BufferUtils.createByteBuffer( diff --git a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java new file mode 100644 index 0000000000..6114455669 --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java @@ -0,0 +1,40 @@ +package jme3test.light.pbr; + +import com.jme3.app.SimpleApplication; +import com.jme3.environment.EnvironmentProbeControl; +import com.jme3.material.Material; +import com.jme3.scene.Geometry; +import com.jme3.scene.Spatial; +import com.jme3.util.SkyFactory; +import com.jme3.util.mikktspace.MikktspaceTangentGenerator; + +/** + * TestPBRSimple + */ +public class TestPBRSimple extends SimpleApplication{ + + public static void main(String[] args) { + new TestPBRSimple().start(); + } + + @Override + public void simpleInitApp() { + + Geometry model = (Geometry) assetManager.loadModel("Models/Tank/tank.j3o"); + MikktspaceTangentGenerator.generate(model); + + Material pbrMat = assetManager.loadMaterial("Models/Tank/tank.j3m"); + model.setMaterial(pbrMat); + + rootNode.attachChild(model); + + + EnvironmentProbeControl envProbe=new EnvironmentProbeControl(renderManager,assetManager,256); + rootNode.addControl(envProbe); + + Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap); + rootNode.attachChild(sky); + EnvironmentProbeControl.tag(sky); + } + +} \ No newline at end of file From 67a37021eb471ab48550b7df719769f96a431f80 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 8 Aug 2023 10:11:34 +0200 Subject: [PATCH 04/29] Add copyright and javadoc, fix some merging issues --- .../environment/EnvironmentProbeControl.java | 77 ++++---- .../jme3/environment/LightProbeFactory.java | 31 +-- .../jme3/environment/LightProbeFactory2.java | 16 +- .../com/jme3/environment/baker/EnvBaker.java | 23 ++- .../environment/baker/GenericEnvBaker.java | 184 +++++++++--------- .../jme3/environment/baker/IBLEnvBaker.java | 55 +++++- .../environment/baker/IBLEnvBakerLight.java | 37 +++- .../jme3/environment/baker/IBLGLEnvBaker.java | 43 +++- .../environment/baker/IBLGLEnvBakerLight.java | 121 +++++++----- 9 files changed, 396 insertions(+), 191 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java index ec43733dd0..f5eb773a10 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -16,36 +16,45 @@ import com.jme3.texture.Image.Format; /** - * SmartLightProbe + * A control that automatically handles environment bake and rebake including + * only tagged spatials. + * + * @author Riccardo Balbo */ public class EnvironmentProbeControl extends LightProbe implements Control { + private RenderManager renderManager; + private AssetManager assetManager; + private int envMapSize; + private Spatial spatial; + private boolean BAKE_NEEDED = true; - - RenderManager renderManager; - AssetManager assetManager; - int envMapSize; - Spatial spatial; - boolean BAKE_NEEDED=true; - Function filter=(s)->{ - return s.getUserData("tags.env")!=null; + private Function filter = (s) -> { + return s.getUserData("tags.env") != null; }; - public static void tag(Spatial s){ - if(s instanceof Node){ - Node n=(Node)s; - for(Spatial sx:n.getChildren()){ + /** + * Tag spatial as part of the environment. Only tagged spatials will be + * rendered in the environment map. + * + * @param s + * the spatial + */ + public static void tag(Spatial s) { + if (s instanceof Node) { + Node n = (Node) s; + for (Spatial sx : n.getChildren()) { tag(sx); } - }else if(s instanceof Geometry){ + } else if (s instanceof Geometry) { s.setUserData("tags.env", true); } } - public EnvironmentProbeControl(RenderManager rm,AssetManager am, int size){ - renderManager=rm; - assetManager=am; - envMapSize=size; + public EnvironmentProbeControl(RenderManager rm, AssetManager am, int size) { + renderManager = rm; + assetManager = am; + envMapSize = size; } @Override @@ -55,9 +64,9 @@ public Control cloneForSpatial(Spatial spatial) { @Override public void setSpatial(Spatial spatial) { - + spatial.addLight(this); - this.spatial=spatial; + this.spatial = spatial; } @@ -67,30 +76,28 @@ public void update(float tpf) { } @Override - public void render(RenderManager rm, ViewPort vp) { - if(BAKE_NEEDED){ - BAKE_NEEDED=false; + public void render(RenderManager rm, ViewPort vp) { + if (BAKE_NEEDED) { + BAKE_NEEDED = false; rebakeNow(); } } - public void rebake(){ - BAKE_NEEDED=true; + /** + * Schedule a rebake of the environment map. + */ + public void rebake() { + BAKE_NEEDED = true; } - + void rebakeNow() { - System.out.println("BAKE"); - IBLGLEnvBakerLight baker = new IBLGLEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, - envMapSize, envMapSize); - - - baker.bakeEnvironment(spatial, Vector3f.ZERO, 0.001f, 1000f,filter); + IBLGLEnvBakerLight baker = new IBLGLEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, envMapSize, envMapSize); + + baker.bakeEnvironment(spatial, Vector3f.ZERO, 0.001f, 1000f, filter); baker.bakeSpecularIBL(); baker.bakeSphericalHarmonicsCoefficients(); - - // probe.setPosition(Vector3f.ZERO); setPrefilteredMap(baker.getSpecularIBL()); setNbMipMaps(getPrefilteredEnvMap().getImage().getMipMapSizes().length); setShCoeffs(baker.getSphericalHarmonicsCoefficients()); @@ -100,5 +107,5 @@ void rebakeNow() { baker.clean(); } - + } \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java index 7becab82e8..eac66502d0 100644 --- a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java +++ b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java @@ -45,31 +45,36 @@ /** * Creates LightProbes within a scene, given an EnvironmentCamera. * - * Since this process can take a long time, you can provide a JobProgressListener that - * will be notified of the ongoing generation process when calling the makeProbe method. + * Since this process can take a long time, you can provide a + * JobProgressListener that will be notified of the ongoing generation process + * when calling the makeProbe method. * - * The process is as follows: - * 1. Create an EnvironmentCamera - * 2. give it a position in the scene - * 3. call {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Spatial)} - * 4. add the created LightProbe to a node with the {@link Node#addLight(com.jme3.light.Light) } method. + * The process is as follows: 1. Create an EnvironmentCamera 2. give it a + * position in the scene 3. call + * {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Spatial)} + * 4. add the created LightProbe to a node with the + * {@link Node#addLight(com.jme3.light.Light) } method. * * Optionally for step 3 call * {@link #makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Spatial, com.jme3.environment.generation.JobProgressListener)} - * with a {@link JobProgressListener} to be notified of the progress of the generation process. + * with a {@link JobProgressListener} to be notified of the progress of the + * generation process. * - * The generation will be split in several threads for faster generation. + * The generation will be split in several threads for faster generation. * - * This class is entirely thread safe and can be called from any thread. + * This class is entirely thread safe and can be called from any thread. * * Note that in case you are using a {@link JobProgressListener}, all its - * methods will be called inside an app.enqueue callable. - * This means that it's completely safe to modify the scenegraph within the - * Listener method, but also means that the event will be delayed until next update loop. + * methods will be called inside an app.enqueue callable. This means that it's + * completely safe to modify the scenegraph within the Listener method, but also + * means that the event will be delayed until next update loop. * + * @deprecated Use LightProbeFactory2 or EnvironmentProbeControl whenever possible. * @see EnvironmentCamera * @author bouquet */ + +@Deprecated public class LightProbeFactory { /** diff --git a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java index 6e28d1cbed..04fb73d875 100644 --- a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java +++ b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java @@ -41,10 +41,24 @@ import com.jme3.scene.Spatial; import com.jme3.texture.Image.Format; - +/** + * A faster version of LightProbeFactory that uses accelerated Baking. + * @author Riccardo Balbo + */ public class LightProbeFactory2 { + /** + * Creates a LightProbe with the giver EnvironmentCamera in the given scene. + * @param rm The RenderManager + * @param am The AssetManager + * @param size The size of the probe + * @param pos The position of the probe + * @param frustumNear The near frustum of the probe + * @param frustumFar The far frustum of the probe + * @param scene The scene to bake + * @return The baked LightProbe + */ public static LightProbe makeProbe(RenderManager rm, AssetManager am, int size,Vector3f pos, float frustumNear,float frustumFar,Spatial scene) { IBLGLEnvBakerLight baker=new IBLGLEnvBakerLight(rm, diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java index 7df8f68d23..f41646536e 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java @@ -8,12 +8,31 @@ import com.jme3.texture.TextureCubeMap; /** - * And environment baker. It bakes the environment. ( ͡° ͜ʖ ͡°) + * And environment baker. It bakes the environment. * * @author Riccardo Balbo */ public interface EnvBaker { - public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar,Function filter); + /** + * Bake the environment + * @param scene The scene to bake + * @param position The position of the camera + * @param frustumNear The near frustum + * @param frustumFar The far frustum + * @param filter A filter to select which geometries to bake + */ + public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar, Function filter); + + /** + * Get the environment map + * @return The environment map + */ public TextureCubeMap getEnvMap(); + + /** + * Clean the environment baker + * This method should be called when the baker is no longer needed + * It will clean up all the resources + */ public void clean(); } \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java index 4046f7534e..72e4e0301a 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java @@ -1,16 +1,43 @@ +/* + * Copyright (c) 2009-2023 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.io.FileOutputStream; import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.HashMap; import java.util.function.Function; 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; @@ -18,6 +45,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; import com.jme3.texture.FrameBuffer; +import com.jme3.texture.FrameBuffer.FrameBufferTarget; import com.jme3.texture.Image.Format; import com.jme3.texture.Texture.MagFilter; import com.jme3.texture.Texture.MinFilter; @@ -26,113 +54,102 @@ 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); +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; - 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; + renderManager = rm; + assetManager = am; + cam = new Camera(128, 128); - cam=new Camera(128,128); - - env=new TextureCubeMap(env_size,env_size,colorFormat); + 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(){ + public TextureCubeMap getEnvMap() { return env; - } + } - Camera getCam(int id,int w,int h,Vector3f position,float frustumNear,float frustumFar){ - cam.resize(w,h,false); + 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])); + cam.setFrustumPerspective(90.0F, 1F, frustumNear, frustumFar); + cam.setRotation(new Quaternion().fromAxes(axisX[id], axisY[id], axisZ[id])); return cam; - } + } @Override - public void clean(){ + public void clean() { env.getImage().dispose(); System.gc(); - System.gc(); + System.gc(); } - @Override - public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar,Function filter) { - FrameBuffer envbaker=new FrameBuffer(env.getImage().getWidth(),env.getImage().getHeight(),1); - envbaker.setDepthTarget(FrameBuffer.newTarget(depthFormat)); + public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar, Function filter) { + FrameBuffer envbaker = new FrameBuffer(env.getImage().getWidth(), env.getImage().getHeight(), 1); + + envbaker.setDepthTarget(FrameBufferTarget.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++){ + for (int i = 0; i < 6; i++) envbaker.addColorTarget(FrameBufferTarget.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 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); @@ -140,32 +157,25 @@ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNea scene.updateLogicalState(0); scene.updateModelBound(); scene.updateGeometricState(); - - Function ofilter= renderManager.getRenderFilter(); + + Function ofilter = renderManager.getRenderFilter(); renderManager.setRenderFilter(filter); - renderManager.renderViewPort(viewPort,0.16f); + renderManager.renderViewPort(viewPort, 0.16f); renderManager.setRenderFilter(ofilter); - 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()); + 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().setData(i, face); } } env.getImage().clearUpdateNeeded(); - + envbaker.dispose(); } - } \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java index cef731dcd7..36de5c35bc 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java @@ -1,3 +1,36 @@ +/* + * Copyright (c) 2009-2023 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 com.jme3.texture.Texture2D; @@ -8,12 +41,32 @@ * * @author Riccardo Balbo */ -public interface IBLEnvBaker extends EnvBaker{ +public interface IBLEnvBaker extends EnvBaker { + /** + * Generate the BRDF texture + * @return The BRDF texture + */ public Texture2D genBRTF() ; + /** + * Bake the irradiance map + */ public void bakeIrradiance(); + + /** + * Bake the specular IBL map + */ public void bakeSpecularIBL() ; + /** + * Get the specular IBL map + * @return The specular IBL map + */ public TextureCubeMap getSpecularIBL(); + + /** + * Get the irradiance map + * @return The irradiance map + */ public TextureCubeMap getIrradiance(); } \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBakerLight.java index 7949714300..76b2fdeb2c 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBakerLight.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBakerLight.java @@ -1,3 +1,36 @@ +/* + * Copyright (c) 2009-2023 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 com.jme3.math.Vector3f; @@ -5,10 +38,10 @@ /** * An environment baker for IBL, that uses spherical harmonics for irradiance. - * * @author Riccardo Balbo */ -public interface IBLEnvBakerLight extends EnvBaker{ +public interface IBLEnvBakerLight extends EnvBaker { + public void bakeSpecularIBL(); public void bakeSphericalHarmonicsCoefficients(); diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java index 83de93808c..0cabed25ec 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java @@ -1,7 +1,39 @@ +/* + * Copyright (c) 2009-2023 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 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; @@ -16,12 +48,13 @@ 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; /** - * An env baker for IBL that runs on the GPU + * An env baker for IBL that runs entirely on the GPU * * @author Riccardo Balbo */ @@ -92,7 +125,7 @@ public void bakeSpecularIBL() { 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) ); + for(int i=0;i<6;i++)specularbaker.addColorTarget( FrameBufferTarget.newTarget(specular).level(mip).face(i) ); float roughness=(float)mip/(float)(specular.getImage().getMipMapSizes().length-1); mat.setFloat("Roughness",roughness); @@ -122,7 +155,7 @@ public Texture2D genBRTF() { FrameBuffer brtfbaker=new FrameBuffer(brtf.getImage().getWidth(),brtf.getImage().getHeight(),1); brtfbaker.setSrgb(false); - brtfbaker.addColorTarget(FrameBuffer.newTarget(brtf)); + brtfbaker.addColorTarget(FrameBufferTarget.newTarget(brtf)); Camera envcam=getCam(0,brtf.getImage().getWidth(),brtf.getImage().getHeight(),Vector3f.ZERO,1,1000); @@ -152,7 +185,7 @@ public void bakeIrradiance() { 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])); + 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); diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java index 718a12f669..a436448ff4 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java @@ -1,7 +1,38 @@ +/* + * Copyright (c) 2009-2023 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 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; @@ -11,6 +42,7 @@ import com.jme3.scene.shape.Box; import com.jme3.texture.FrameBuffer; import com.jme3.texture.TextureCubeMap; +import com.jme3.texture.FrameBuffer.FrameBufferTarget; import com.jme3.texture.Image.Format; import com.jme3.texture.Texture.MagFilter; import com.jme3.texture.Texture.MinFilter; @@ -18,87 +50,86 @@ import com.jme3.texture.image.ColorSpace; /** - * An env baker for IBL that runs on the GPU + * An env baker for IBL that bakes the specular map on the GPU and uses + * spherical harmonics for the irradiance map. + * + * This is lighter on VRAM but uses the CPU to compute the irradiance map. * * @author Riccardo Balbo */ -public class IBLGLEnvBakerLight extends GenericEnvBaker implements IBLEnvBakerLight{ +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); + + 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 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); + 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 Date: Tue, 8 Aug 2023 17:29:53 +0200 Subject: [PATCH 05/29] Make lightprobes serializable on demand when baked with accelerated baking. Make them serializable by default if created with LightProbeFactory2. Accelerate Spherical Harmonics generator. --- .../environment/EnvironmentProbeControl.java | 20 +- .../jme3/environment/LightProbeFactory2.java | 6 +- .../com/jme3/environment/baker/EnvBaker.java | 17 +- .../environment/baker/GenericEnvBaker.java | 95 ++++++++- .../jme3/environment/baker/IBLGLEnvBaker.java | 57 ++++-- .../environment/baker/IBLGLEnvBakerLight.java | 166 +++++++++------ .../baker/IBLHybridEnvBakerLight.java | 148 ++++++++++++++ .../main/resources/Common/IBL/IBLKernels.frag | 1 + .../main/resources/Common/IBL/Math.glsllib | 28 ++- .../resources/Common/IBLSphH/IBLSphH.frag | 191 ++++++++++++++++++ .../resources/Common/IBLSphH/IBLSphH.j3md | 32 +++ .../resources/Common/IBLSphH/IBLSphH.vert | 16 ++ .../jme3test/light/pbr/TestPBRSimple.java | 42 +++- 13 files changed, 711 insertions(+), 108 deletions(-) create mode 100644 jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java create mode 100644 jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.frag create mode 100644 jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.j3md create mode 100644 jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.vert diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java index f5eb773a10..60503c31b5 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -1,10 +1,10 @@ package com.jme3.environment; import java.util.function.Function; -import java.util.function.Predicate; import com.jme3.asset.AssetManager; import com.jme3.environment.baker.IBLGLEnvBakerLight; +import com.jme3.environment.baker.IBLHybridEnvBakerLight; import com.jme3.light.LightProbe; import com.jme3.math.Vector3f; import com.jme3.renderer.RenderManager; @@ -28,6 +28,8 @@ public class EnvironmentProbeControl extends LightProbe implements Control { private int envMapSize; private Spatial spatial; private boolean BAKE_NEEDED = true; + private boolean USE_GL_IR = true; + private boolean serializable = false; private Function filter = (s) -> { return s.getUserData("tags.env") != null; @@ -62,6 +64,14 @@ public Control cloneForSpatial(Spatial spatial) { return null; } + public void setSerializeBakeResults(boolean v) { + serializable = v; + } + + public boolean isSerializeBakeResults() { + return serializable; + } + @Override public void setSpatial(Spatial spatial) { @@ -92,7 +102,13 @@ public void rebake() { void rebakeNow() { - IBLGLEnvBakerLight baker = new IBLGLEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, envMapSize, envMapSize); + IBLHybridEnvBakerLight baker; + if(!USE_GL_IR){ + baker = new IBLHybridEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, envMapSize, envMapSize); + } else { + baker = new IBLGLEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, envMapSize, envMapSize); + } + baker.setTexturePulling(isSerializeBakeResults()); baker.bakeEnvironment(spatial, Vector3f.ZERO, 0.001f, 1000f, filter); baker.bakeSpecularIBL(); diff --git a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java index 04fb73d875..9cf1c7c903 100644 --- a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java +++ b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java @@ -33,6 +33,7 @@ import com.jme3.asset.AssetManager; import com.jme3.environment.baker.IBLGLEnvBakerLight; +import com.jme3.environment.baker.IBLHybridEnvBakerLight; import com.jme3.environment.util.EnvMapUtils; import com.jme3.light.LightProbe; import com.jme3.math.Vector3f; @@ -61,10 +62,9 @@ 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); + IBLHybridEnvBakerLight baker=new IBLGLEnvBakerLight(rm, am, Format.RGB16F, Format.Depth, size, size); + baker.setTexturePulling(true); baker.bakeEnvironment(scene,pos, frustumNear,frustumFar,null); baker.bakeSpecularIBL(); baker.bakeSphericalHarmonicsCoefficients(); diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java index f41646536e..89ca968005 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java @@ -34,5 +34,20 @@ public interface EnvBaker { * This method should be called when the baker is no longer needed * It will clean up all the resources */ - public void clean(); + public void clean(); + + + + /** + * Set if textures should be pulled from the GPU + * @param v + */ + public void setTexturePulling(boolean v); + + + /** + * Get if textures should be pulled from the GPU + * @return + */ + public boolean isTexturePulling(); } \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java index 72e4e0301a..791b0726f2 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java @@ -32,8 +32,13 @@ package com.jme3.environment.baker; +import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; import com.jme3.asset.AssetManager; import com.jme3.math.ColorRGBA; @@ -45,6 +50,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; import com.jme3.texture.FrameBuffer; +import com.jme3.texture.Texture; import com.jme3.texture.FrameBuffer.FrameBufferTarget; import com.jme3.texture.Image.Format; import com.jme3.texture.Texture.MagFilter; @@ -97,10 +103,13 @@ public abstract class GenericEnvBaker implements EnvBaker { protected final RenderManager renderManager; protected final AssetManager assetManager; protected final Camera cam; - protected final boolean copyToRam; + protected boolean texturePulling=false; + protected List bos = new ArrayList<>(); + private static final Logger LOG=Logger.getLogger(GenericEnvBaker.class.getName()); - public GenericEnvBaker(RenderManager rm, AssetManager am, Format colorFormat, Format depthFormat, int env_size, boolean copyToRam) { - this.copyToRam = copyToRam; + + + public GenericEnvBaker(RenderManager rm, AssetManager am, Format colorFormat, Format depthFormat, int env_size) { this.depthFormat = depthFormat; renderManager = rm; @@ -115,6 +124,16 @@ public GenericEnvBaker(RenderManager rm, AssetManager am, Format colorFormat, Fo env.getImage().setColorSpace(ColorSpace.Linear); } + @Override + public void setTexturePulling(boolean v) { + texturePulling = v; + } + + @Override + public boolean isTexturePulling() { + return texturePulling; + } + public TextureCubeMap getEnvMap() { return env; } @@ -141,6 +160,8 @@ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, envbaker.setDepthTarget(FrameBufferTarget.newTarget(depthFormat)); envbaker.setSrgb(false); + if(isTexturePulling())startPulling(); + for (int i = 0; i < 6; i++) envbaker.addColorTarget(FrameBufferTarget.newTarget(env).face(TextureCubeMap.Face.values()[i])); for (int i = 0; i < 6; i++) { @@ -164,18 +185,70 @@ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, renderManager.renderViewPort(viewPort, 0.16f); renderManager.setRenderFilter(ofilter); - 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); + if (isTexturePulling()) pull(envbaker, env, i); + + } - } + if (isTexturePulling()) endPulling(env); + env.getImage().clearUpdateNeeded(); + envbaker.dispose(); + } + + + /** + * Starts pulling the data from the framebuffer into the texture + */ + protected void startPulling() { + bos.clear(); + } + + /** + * Pulls the data from the framebuffer into the texture + * Nb. mipmaps must be pulled sequentially on the same faceId + * @param fb the framebuffer to pull from + * @param env the texture to pull into + * @param faceId id of face if cubemap or 0 otherwise + * @return + */ + protected ByteBuffer pull(FrameBuffer fb, Texture env, int faceId) { + + if (fb.getColorTarget().getFormat() != env.getImage().getFormat()) + throw new IllegalArgumentException("Format mismatch: " + fb.getColorTarget().getFormat() + "!=" + env.getImage().getFormat()); + + ByteBuffer face = BufferUtils.createByteBuffer(fb.getWidth() * fb.getHeight() * (fb.getColorTarget().getFormat().getBitsPerPixel() / 8)); + renderManager.getRenderer().readFrameBufferWithFormat(fb, face, fb.getColorTarget().getFormat()); + face.rewind(); + + while (bos.size() <= faceId) bos.add(null); + ByteArrayOutputStream bo = bos.get(faceId); + if (bo == null) bos.set(faceId, bo = new ByteArrayOutputStream()); + try { + byte array[] = new byte[face.limit()]; + face.get(array); + bo.write(array); + } catch (Exception ex) { + LOG.log(Level.SEVERE, null, ex); } + return face; + } - env.getImage().clearUpdateNeeded(); - envbaker.dispose(); + /** + * Ends pulling the data into the texture + * @param tx the texture to pull into + */ + protected void endPulling(Texture tx) { + for (int i = 0; i < bos.size(); i++) { + ByteArrayOutputStream bo = bos.get(i); + if (bo == null) { + LOG.log(Level.SEVERE, "Missing face {0}. Pulling incomplete!", i); + continue; + } + ByteBuffer faceMip = ByteBuffer.wrap(bo.toByteArray()); + tx.getImage().setData(i, faceMip); + } + bos.clear(); + tx.getImage().clearUpdateNeeded(); } } \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java index 0cabed25ec..69a544a6a5 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java @@ -54,7 +54,7 @@ /** - * An env baker for IBL that runs entirely on the GPU + * Fully accelerated env baker for IBL that runs entirely on the GPU * * @author Riccardo Balbo */ @@ -62,7 +62,6 @@ 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, @@ -70,7 +69,7 @@ public IBLGLEnvBaker(RenderManager rm,AssetManager am, int irradiance_size, int brtf_size ){ - super(rm,am,format,depthFormat,env_size,false); + super(rm,am,format,depthFormat,env_size); irradiance=new TextureCubeMap(irradiance_size,irradiance_size,format); irradiance.setMagFilter(MagFilter.Bilinear); @@ -80,7 +79,7 @@ public IBLGLEnvBaker(RenderManager rm,AssetManager am, specular=new TextureCubeMap(specular_size,specular_size,format); specular.setMagFilter(MagFilter.Bilinear); - specular.setMinFilter(MinFilter.BilinearNoMipMaps); + specular.setMinFilter(MinFilter.Trilinear); specular.setWrap(WrapMode.EdgeClamp); specular.getImage().setColorSpace(ColorSpace.Linear); int nbMipMaps=(int)(Math.log(specular_size)/Math.log(2)+1); @@ -117,33 +116,41 @@ public void bakeSpecularIBL() { mat.setBoolean("UseSpecularIBL",true); mat.setTexture("EnvMap",env); screen.setMaterial(mat); - - for(int mip=0;mip 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); +public class IBLGLEnvBakerLight extends IBLHybridEnvBakerLight { + public final static int NUM_SH_COEFFICIENT = 9; + private static final Logger LOG = Logger.getLogger(IBLGLEnvBakerLight.class.getName()); + + public IBLGLEnvBakerLight(RenderManager rm, AssetManager am, Format format, Format depthFormat, int env_size, int specular_size) { + super(rm, am, format, depthFormat, env_size, specular_size); + } + + @Override + public boolean isTexturePulling() { + return this.texturePulling; } @Override - public void bakeSpecularIBL() { + public void bakeSphericalHarmonicsCoefficients() { 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); + Material mat = new Material(assetManager, "Common/IBLSphH/IBLSphH.j3md"); + mat.setTexture("Texture", env); + mat.setVector2("Resolution", new Vector2f(env.getImage().getWidth(), env.getImage().getHeight())); screen.setMaterial(mat); + + + float remapMaxValue = 0; + Format format = Format.RGBA32F; + if (!renderManager.getRenderer().getCaps().contains(Caps.FloatTexture)) { + LOG.warning("Float textures not supported, using RGB8 instead. This may cause accuracy issues."); + format = Format.RGBA8; + remapMaxValue = 0.05f; + } - 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)); + + if (remapMaxValue > 0) { + mat.setFloat("RemapMaxValue", remapMaxValue); + } else { + mat.clearParam("RemapMaxValue"); + } - FrameBuffer specularbaker = new FrameBuffer(mipWidth, mipHeight, 1); - specularbaker.setSrgb(false); - for (int i = 0; i < 6; i++) specularbaker.addColorTarget(FrameBufferTarget.newTarget(specular).level(mip).face(i)); + Texture2D shCoefTx[] = { + new Texture2D(NUM_SH_COEFFICIENT, 1, 1, format), + new Texture2D(NUM_SH_COEFFICIENT, 1, 1, format) + }; - 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); + FrameBuffer shbaker = new FrameBuffer(NUM_SH_COEFFICIENT, 1, 1); + shbaker.setSrgb(false); + shbaker.addColorTarget(FrameBufferTarget.newTarget(shCoefTx[0])); + shbaker.addColorTarget(FrameBufferTarget.newTarget(shCoefTx[1])); - screen.updateLogicalState(0); - screen.updateGeometricState(); + int renderOnT = -1; - renderManager.setCamera(getCam(i, specularbaker.getWidth(), specularbaker.getHeight(), Vector3f.ZERO, 1, 1000), false); - renderManager.getRenderer().setFrameBuffer(specularbaker); - renderManager.renderGeometry(screen); + for (int faceId = 0; faceId < 6; faceId++) { + if (renderOnT != -1) { + int s = renderOnT; + renderOnT = renderOnT == 0 ? 1 : 0; + mat.setTexture("ShCoef", shCoefTx[s]); + mat.setInt("FaceId", faceId); + } else { + renderOnT = 0; } - specularbaker.dispose(); + + screen.updateLogicalState(0); + screen.updateGeometricState(); + + shbaker.setTargetIndex(renderOnT); + + renderManager.setCamera(getCam(0, shbaker.getWidth(), shbaker.getHeight(), Vector3f.ZERO, 1, 1000), false); + renderManager.getRenderer().setFrameBuffer(shbaker); + renderManager.renderGeometry(screen); } - specular.setMinFilter(MinFilter.Trilinear); - } - @Override - public TextureCubeMap getSpecularIBL() { - return specular; - } + + ByteBuffer shCoefRaw = BufferUtils.createByteBuffer( + NUM_SH_COEFFICIENT * 1 * ( shbaker.getColorTarget().getFormat().getBitsPerPixel() / 8) + ); + renderManager.getRenderer().readFrameBufferWithFormat(shbaker, shCoefRaw, shbaker.getColorTarget().getFormat()); + shCoefRaw.rewind(); + + Image img = new Image(format, NUM_SH_COEFFICIENT, 1, shCoefRaw, ColorSpace.Linear); + ImageRaster imgr=ImageRaster.create(img); + + shCoef = new Vector3f[NUM_SH_COEFFICIENT]; + float weightAccum = 0.0f; + + for (int i = 0; i < shCoef.length; i++) { + ColorRGBA c = imgr.getPixel(i, 0); + shCoef[i] = new Vector3f(c.r, c.g, c.b); + if (weightAccum == 0) weightAccum = c.a; + else if (weightAccum != c.a) { + LOG.warning("SH weight is not uniform, this may cause issues."); + } - @Override - public void bakeSphericalHarmonicsCoefficients() { - shCoef = EnvMapUtils.getSphericalHarmonicsCoefficents(getEnvMap()); - } + } + + if (remapMaxValue > 0) weightAccum /= remapMaxValue; + + for (int i = 0; i < NUM_SH_COEFFICIENT; ++i) { + if (remapMaxValue > 0) shCoef[i].divideLocal(remapMaxValue); + shCoef[i].multLocal(4.0f * FastMath.PI / weightAccum); + } + + img.dispose(); - @Override - public Vector3f[] getSphericalHarmonicsCoefficients() { - return shCoef; } -} \ No newline at end of file +} diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java new file mode 100644 index 0000000000..667178f474 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2009-2023 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 com.jme3.asset.AssetManager; +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.FrameBuffer.FrameBufferTarget; +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 bakes the specular map on the GPU and uses + * spherical harmonics generated on the CPU for the irradiance map. + * + * This is lighter on VRAM but uses the CPU to compute the irradiance map. + * + * @author Riccardo Balbo + */ +public class IBLHybridEnvBakerLight extends GenericEnvBaker implements IBLEnvBakerLight { + protected TextureCubeMap specular; + protected Vector3f[] shCoef; + + public IBLHybridEnvBakerLight(RenderManager rm, AssetManager am, Format format, Format depthFormat, int env_size, int specular_size + + ) { + super(rm, am, format, depthFormat, env_size); + + 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); + 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 boolean isTexturePulling() { // always pull textures from gpu + return true; + } + + @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); + + if (isTexturePulling()) startPulling(); + + 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(FrameBufferTarget.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); + + if (isTexturePulling()) pull(specularbaker, specular, i); + + } + specularbaker.dispose(); + } + + if (isTexturePulling()) endPulling(specular); + specular.getImage().clearUpdateNeeded(); + + } + + @Override + public TextureCubeMap getSpecularIBL() { + return specular; + } + + @Override + public void bakeSphericalHarmonicsCoefficients() { + shCoef = EnvMapUtils.getSphericalHarmonicsCoefficents(getEnvMap()); + } + + @Override + public Vector3f[] getSphericalHarmonicsCoefficients() { + return shCoef; + } +} \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/IBL/IBLKernels.frag b/jme3-core/src/main/resources/Common/IBL/IBLKernels.frag index 7bc245a464..40b328144b 100644 --- a/jme3-core/src/main/resources/Common/IBL/IBLKernels.frag +++ b/jme3-core/src/main/resources/Common/IBL/IBLKernels.frag @@ -82,6 +82,7 @@ void prefilteredEnvKernel(){ vec3 L = normalize(2.0 * dot(V, H) * H - V); float NdotL = max(dot(N, L), 0.0); if(NdotL > 0.0) { + // TODO: use mipmap prefilteredColor += texture(m_EnvMap, L).rgb * NdotL; totalWeight += NdotL; } diff --git a/jme3-core/src/main/resources/Common/IBL/Math.glsllib b/jme3-core/src/main/resources/Common/IBL/Math.glsllib index abc4190afa..6911e703eb 100644 --- a/jme3-core/src/main/resources/Common/IBL/Math.glsllib +++ b/jme3-core/src/main/resources/Common/IBL/Math.glsllib @@ -17,7 +17,33 @@ float RadicalInverse_VdC(uint bits) { vec2 Hammersley(uint i, uint N){ return vec2(float(i)/float(N), RadicalInverse_VdC(i)); -} +} + +/* +Compatible with GL ES 2 +float VanDerCorput(uint n, uint base){ + float invBase = 1.0 / float(base); + float denom = 1.0; + float result = 0.0; + + for(uint i = 0u; i < 32u; ++i) + { + if(n > 0u) + { + denom = mod(float(n), 2.0); + result += denom * invBase; + invBase = invBase / 2.0; + n = uint(float(n) / 2.0); + } + } + + return result; +} + +vec2 Hammersley(uint i, uint N){ + return vec2(float(i)/float(N), VanDerCorput(i, 2u)); +} +*/ vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness){ diff --git a/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.frag b/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.frag new file mode 100644 index 0000000000..04d4a06eb7 --- /dev/null +++ b/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.frag @@ -0,0 +1,191 @@ +/** + +* - Riccardo Balbo +*/ +#import "Common/IBL/Math.glsllib" + +// #define NUM_SH_COEFFICIENT 9 +#ifndef PI + #define PI 3.1415926535897932384626433832795 +#endif + +out vec4 outFragColor; +in vec2 TexCoords; +in vec3 LocalPos; + + +uniform samplerCube m_Texture; +#ifdef SH_COEF + uniform sampler2D m_ShCoef; +#endif +uniform vec2 m_Resolution; +uniform int m_FaceId; + +const float sqrtPi = sqrt(PI); +const float sqrt3Pi = sqrt(3 / PI); +const float sqrt5Pi = sqrt(5 / PI); +const float sqrt15Pi = sqrt(15 / PI); + +#ifdef REMAP_MAX_VALUE + uniform float m_RemapMaxValue; +#endif + + +vec3 getVectorFromCubemapFaceTexCoord(float x, float y, float mapSize, int face) { + float u; + float v; + + /* transform from [0..res - 1] to [- (1 - 1 / res) .. (1 - 1 / res)] + * (+ 0.5f is for texel center addressing) */ + u = (2.0 * (x + 0.5) / mapSize) - 1.0; + v = (2.0 * (y + 0.5) / mapSize) - 1.0; + + + // Warp texel centers in the proximity of the edges. + float a = pow(mapSize, 2.0) / pow(mapSize - 1, 3.0); + + u = a * pow(u, 3) + u; + v = a * pow(v, 3) + v; + //compute vector depending on the face + // Code from Nvtt : https://github.com/castano/nvidia-texture-tools/blob/master/src/nvtt/CubeSurface.cpp#L101 + vec3 o =vec3(0); + switch(face) { + case 0: + o= normalize(vec3(1, -v, -u)); + break; + case 1: + o= normalize(vec3(-1, -v, u)); + break; + case 2: + o= normalize(vec3(u, 1, v)); + break; + case 3: + o= normalize(vec3(u, -1, -v)); + break; + case 4: + o= normalize(vec3(u, -v, 1)); + break; + case 5: + o= normalize(vec3(-u, -v, -1.0)); + break; + } + + return o; +} + +float atan2(in float y, in float x) { + bool s = (abs(x) > abs(y)); + return mix(PI / 2.0 - atan(x, y), atan(y, x), s); +} + +float areaElement(float x, float y) { + return atan2(x * y, sqrt(x * x + y * y + 1.)); +} + +float getSolidAngleAndVector(float x, float y, float mapSize, int face, out vec3 store) { + /* transform from [0..res - 1] to [- (1 - 1 / res) .. (1 - 1 / res)] + (+ 0.5f is for texel center addressing) */ + float u = (2.0 * (x + 0.5) / mapSize) - 1.0; + float v = (2.0 * (y + 0.5) / mapSize) - 1.0; + + store = getVectorFromCubemapFaceTexCoord(x, y, mapSize, face); + + /* Solid angle weight approximation : + * U and V are the -1..1 texture coordinate on the current face. + * Get projected area for this texel */ + float x0, y0, x1, y1; + float invRes = 1.0 / mapSize; + x0 = u - invRes; + y0 = v - invRes; + x1 = u + invRes; + y1 = v + invRes; + + return areaElement(x0, y0) - areaElement(x0, y1) - areaElement(x1, y0) + areaElement(x1, y1); +} + +void evalShBasis(vec3 texelVect, int i, out float shDir) { + float xV = texelVect.x; + float yV = texelVect.y; + float zV = texelVect.z; + + float x2 = xV * xV; + float y2 = yV * yV; + float z2 = zV * zV; + + if(i==0) shDir = (1. / (2. * sqrtPi)); + else if(i==1) shDir = -(sqrt3Pi * yV) / 2.; + else if(i == 2) shDir = (sqrt3Pi * zV) / 2.; + else if(i == 3) shDir = -(sqrt3Pi * xV) / 2.; + else if(i == 4) shDir = (sqrt15Pi * xV * yV) / 2.; + else if(i == 5) shDir = -(sqrt15Pi * yV * zV) / 2.; + else if(i == 6) shDir = (sqrt5Pi * (-1. + 3. * z2)) / 4.; + else if(i == 7) shDir = -(sqrt15Pi * xV * zV) / 2.; + else shDir = sqrt15Pi * (x2 - y2) / 4.; +} + +vec3 pixelFaceToV(int faceId, float pixelX, float pixelY, float cubeMapSize) { + vec2 normalizedCoords = vec2((2.0 * pixelX + 1.0) / cubeMapSize, (2.0 * pixelY + 1.0) / cubeMapSize); + + vec3 direction; + if(faceId == 0) { + direction = vec3(1.0, -normalizedCoords.y, -normalizedCoords.x); + } else if(faceId == 1) { + direction = vec3(-1.0, -normalizedCoords.y, normalizedCoords.x); + } else if(faceId == 2) { + direction = vec3(normalizedCoords.x, 1.0, normalizedCoords.y); + } else if(faceId == 3) { + direction = vec3(normalizedCoords.x, -1.0, -normalizedCoords.y); + } else if(faceId == 4) { + direction = vec3(normalizedCoords.x, -normalizedCoords.y, 1.0); + } else if(faceId == 5) { + direction = vec3(-normalizedCoords.x, -normalizedCoords.y, -1.0); + } + + return normalize(direction); +} + +void sphKernel() { + int width = int(m_Resolution.x); + int height = int(m_Resolution.y); + vec3 texelVect=vec3(0); + float shDir=0; + float weight=0; + vec4 color=vec4(0); + + int i=int(gl_FragCoord.x); + + #ifdef SH_COEF + vec4 r=texelFetch(m_ShCoef, ivec2(i, 0), 0); + vec3 shCoef=r.rgb; + float weightAccum = r.a; + #else + vec3 shCoef=vec3(0.0); + float weightAccum = 0.0; + #endif + + for(int y = 0; y < height; y++) { + for(int x = 0; x < width; x++) { + weight = getSolidAngleAndVector(float(x), float(y), float(width), m_FaceId, texelVect); + evalShBasis(texelVect, i, shDir); + color = texture(m_Texture, texelVect); + shCoef.x = (shCoef.x + color.r * shDir * weight); + shCoef.y = (shCoef.y + color.g * shDir * weight); + shCoef.z = (shCoef.z + color.b * shDir * weight); + weightAccum += weight; + } + } + + + + #ifdef REMAP_MAX_VALUE + shCoef.xyz=shCoef.xyz*m_RemapMaxValue; + weightAccum=weightAccum*m_RemapMaxValue; + #endif + + outFragColor = vec4(shCoef.xyz,weightAccum); + +} + +void main() { + sphKernel(); +} \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.j3md b/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.j3md new file mode 100644 index 0000000000..eea1990387 --- /dev/null +++ b/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.j3md @@ -0,0 +1,32 @@ +MaterialDef IBLSphH { + + MaterialParameters { + TextureCubeMap Texture -LINEAR + Int FaceId : 0 + Texture2D ShCoef -LINEAR + Vector2 Resolution + Float RemapMaxValue + } + + Technique { + + VertexShader GLSL150: Common/IBLSphH/IBLSphH.vert + FragmentShader GLSL150: Common/IBLSphH/IBLSphH.frag + + WorldParameters { + } + + RenderState { + DepthWrite Off + DepthTest Off + DepthFunc Equal + FaceCull Off + } + + Defines { + REMAP_MAX_VALUE: RemapMaxValue + SH_COEF: ShCoef + } + + } +} \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.vert b/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.vert new file mode 100644 index 0000000000..266eb5e02a --- /dev/null +++ b/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.vert @@ -0,0 +1,16 @@ +/** +*- Riccardo Balbo +*/ +in vec3 inPosition; +in vec2 inTexCoord; + +out vec2 TexCoords; +out vec3 LocalPos; + + +void main() { + LocalPos = inPosition.xyz; + TexCoords = inTexCoord.xy; + vec2 pos = inPosition.xy * 2.0 - 1.0; + gl_Position = vec4(pos, 0.0, 1.0); +} \ No newline at end of file diff --git a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java index 6114455669..0287459cc8 100644 --- a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java +++ b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java @@ -1,8 +1,11 @@ package jme3test.light.pbr; + import com.jme3.app.SimpleApplication; import com.jme3.environment.EnvironmentProbeControl; +import com.jme3.input.ChaseCamera; import com.jme3.material.Material; +import com.jme3.math.FastMath; import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; import com.jme3.util.SkyFactory; @@ -11,7 +14,8 @@ /** * TestPBRSimple */ -public class TestPBRSimple extends SimpleApplication{ +public class TestPBRSimple extends SimpleApplication { + private boolean REALTIME_BAKING = false; public static void main(String[] args) { new TestPBRSimple().start(); @@ -20,21 +24,47 @@ public static void main(String[] args) { @Override public void simpleInitApp() { + Geometry model = (Geometry) assetManager.loadModel("Models/Tank/tank.j3o"); MikktspaceTangentGenerator.generate(model); Material pbrMat = assetManager.loadMaterial("Models/Tank/tank.j3m"); model.setMaterial(pbrMat); - rootNode.attachChild(model); - - EnvironmentProbeControl envProbe=new EnvironmentProbeControl(renderManager,assetManager,256); - rootNode.addControl(envProbe); - + ChaseCamera chaseCam = new ChaseCamera(cam, model, inputManager); + chaseCam.setDragToRotate(true); + chaseCam.setMinVerticalRotation(-FastMath.HALF_PI); + chaseCam.setMaxDistance(1000); + chaseCam.setSmoothMotion(true); + chaseCam.setRotationSensitivity(10); + chaseCam.setZoomSensitivity(5); + flyCam.setEnabled(false); + Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap); rootNode.attachChild(sky); + + // Create baker control + EnvironmentProbeControl envProbe=new EnvironmentProbeControl(renderManager,assetManager,256); + rootNode.addControl(envProbe); + + // Tag the sky, only the tagged spatials will be rendered in the env map EnvironmentProbeControl.tag(sky); + + + } + + float lastBake = 0; + @Override + public void simpleUpdate(float tpf) { + if (REALTIME_BAKING) { + lastBake += tpf; + if (lastBake > 1.4f) { + rootNode.getControl(EnvironmentProbeControl.class).rebake(); + lastBake = 0; + } + } + } } \ No newline at end of file From 5261a986e7261989f337f8be47cf7a6837ec70fc Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 8 Aug 2023 18:33:45 +0200 Subject: [PATCH 06/29] Add javadoc fix typo --- .../com/jme3/environment/baker/EnvBaker.java | 34 ++++++++++++++++++- .../java/com/jme3/renderer/RenderManager.java | 29 +++++++++++----- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java index 89ca968005..be20e86197 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java @@ -1,3 +1,35 @@ +/* + * Copyright (c) 2009-2023 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.function.Function; @@ -8,7 +40,7 @@ import com.jme3.texture.TextureCubeMap; /** - * And environment baker. It bakes the environment. + * An environment baker. It bakes the environment. * * @author Riccardo Balbo */ diff --git a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java index 311b26284d..d556737cf0 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java @@ -105,15 +105,8 @@ public class RenderManager { private TechniqueDef.LightMode preferredLightMode = TechniqueDef.LightMode.MultiPass; private int singlePassLightBatchSize = 1; private MatParamOverride boundDrawBufferId=new MatParamOverride(VarType.Int,"BoundDrawBuffer",0); - private Function renderFilter; + private Function renderFilter; - public void setRenderFilter(Function filter){ - renderFilter=filter; - } - - public Function getRenderFilter(){ - return renderFilter; - } /** * Creates a high-level rendering interface over the @@ -1346,4 +1339,24 @@ public void setPassDrawBufferTargetIdToShaders(boolean v) { this.forcedOverrides.remove(boundDrawBufferId); } } + /** + * Set a render filter. Every geometry will be tested against this filter + * before rendering and will only be rendered if the filter returns true. + * + * @param filter + */ + public void setRenderFilter(Function filter) { + renderFilter = filter; + } + + /** + * Returns the render filter that the RenderManager is currently using + * + * @param filter + * the render filter + */ + public Function getRenderFilter() { + return renderFilter; + } + } From 7bc76708806f7fcec4aefc5b2449783ff9166030 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 8 Aug 2023 19:04:35 +0200 Subject: [PATCH 07/29] Make EnvironmentProbeControl serializable --- .../environment/EnvironmentProbeControl.java | 52 +++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java index 60503c31b5..cbcae3822e 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -1,10 +1,15 @@ package com.jme3.environment; +import java.io.IOException; import java.util.function.Function; import com.jme3.asset.AssetManager; import com.jme3.environment.baker.IBLGLEnvBakerLight; import com.jme3.environment.baker.IBLHybridEnvBakerLight; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; import com.jme3.light.LightProbe; import com.jme3.math.Vector3f; import com.jme3.renderer.RenderManager; @@ -22,13 +27,12 @@ * @author Riccardo Balbo */ public class EnvironmentProbeControl extends LightProbe implements Control { - - private RenderManager renderManager; + private final boolean USE_GL_IR = true; + private AssetManager assetManager; - private int envMapSize; + private boolean bakeNeeded = true; + private int envMapSize=256; private Spatial spatial; - private boolean BAKE_NEEDED = true; - private boolean USE_GL_IR = true; private boolean serializable = false; private Function filter = (s) -> { @@ -53,10 +57,9 @@ public static void tag(Spatial s) { } } - public EnvironmentProbeControl(RenderManager rm, AssetManager am, int size) { - renderManager = rm; - assetManager = am; - envMapSize = size; + public EnvironmentProbeControl(AssetManager assetManager,int size) { + this.envMapSize = size; + this.assetManager = assetManager; } @Override @@ -74,7 +77,6 @@ public boolean isSerializeBakeResults() { @Override public void setSpatial(Spatial spatial) { - spatial.addLight(this); this.spatial = spatial; @@ -87,9 +89,9 @@ public void update(float tpf) { @Override public void render(RenderManager rm, ViewPort vp) { - if (BAKE_NEEDED) { - BAKE_NEEDED = false; - rebakeNow(); + if (bakeNeeded) { + bakeNeeded = false; + rebakeNow(rm); } } @@ -97,11 +99,10 @@ public void render(RenderManager rm, ViewPort vp) { * Schedule a rebake of the environment map. */ public void rebake() { - BAKE_NEEDED = true; + bakeNeeded = true; } - void rebakeNow() { - + void rebakeNow(RenderManager renderManager) { IBLHybridEnvBakerLight baker; if(!USE_GL_IR){ baker = new IBLHybridEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, envMapSize, envMapSize); @@ -121,7 +122,26 @@ void rebakeNow() { setReady(true); baker.clean(); + } + + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule oc = ex.getCapsule(this); + oc.write(envMapSize, "size", 256); + oc.write(serializable, "serializable", false); + oc.write(bakeNeeded, "bakeNeeded", true); + } + + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule ic = im.getCapsule(this); + envMapSize = ic.readInt("size", 256); + serializable = ic.readBoolean("serializable", false); + bakeNeeded = ic.readBoolean("bakeNeeded", true); + assetManager = im.getAssetManager(); } } \ No newline at end of file From 29ffa68ffaf397d8f08359b4735e96727dae0466 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 8 Aug 2023 19:14:14 +0200 Subject: [PATCH 08/29] Fix TestPBRSimple to use new api --- .../src/main/java/jme3test/light/pbr/TestPBRSimple.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java index 0287459cc8..21e2102dd7 100644 --- a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java +++ b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java @@ -45,7 +45,7 @@ public void simpleInitApp() { rootNode.attachChild(sky); // Create baker control - EnvironmentProbeControl envProbe=new EnvironmentProbeControl(renderManager,assetManager,256); + EnvironmentProbeControl envProbe=new EnvironmentProbeControl(assetManager,256); rootNode.addControl(envProbe); // Tag the sky, only the tagged spatials will be rendered in the env map From 33e7f34fabc765b99ddd0cf94c22643c531bef57 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 8 Aug 2023 19:26:37 +0200 Subject: [PATCH 09/29] do not dispose the env image --- .../main/java/com/jme3/environment/baker/GenericEnvBaker.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java index 791b0726f2..448a451cac 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java @@ -148,9 +148,7 @@ Camera getCam(int id, int w, int h, Vector3f position, float frustumNear, float @Override public void clean() { - env.getImage().dispose(); - System.gc(); - System.gc(); + } @Override From 8b59c315d137f581b40577e4970fda7f5dabf6f0 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Wed, 9 Aug 2023 08:58:28 +0200 Subject: [PATCH 10/29] Add default constructor --- .../environment/EnvironmentProbeControl.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java index cbcae3822e..e0c0516eee 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -2,6 +2,8 @@ import java.io.IOException; import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; import com.jme3.asset.AssetManager; import com.jme3.environment.baker.IBLGLEnvBakerLight; @@ -28,6 +30,8 @@ */ public class EnvironmentProbeControl extends LightProbe implements Control { private final boolean USE_GL_IR = true; + private static final Logger LOG = Logger.getLogger(EnvironmentProbeControl.class.getName()); + private AssetManager assetManager; private boolean bakeNeeded = true; @@ -57,6 +61,9 @@ public static void tag(Spatial s) { } } + protected EnvironmentProbeControl() { + } + public EnvironmentProbeControl(AssetManager assetManager,int size) { this.envMapSize = size; this.assetManager = assetManager; @@ -64,7 +71,7 @@ public EnvironmentProbeControl(AssetManager assetManager,int size) { @Override public Control cloneForSpatial(Spatial spatial) { - return null; + throw new UnsupportedOperationException(); } public void setSerializeBakeResults(boolean v) { @@ -79,7 +86,6 @@ public boolean isSerializeBakeResults() { public void setSpatial(Spatial spatial) { spatial.addLight(this); this.spatial = spatial; - } @Override @@ -102,7 +108,19 @@ public void rebake() { bakeNeeded = true; } + /** + * Set the asset manager used to load the shaders needed for the baking + * @param assetManager + */ + public void setAssetManager(AssetManager assetManager) { + this.assetManager = assetManager; + } + void rebakeNow(RenderManager renderManager) { + if (assetManager == null) { + LOG.log(Level.SEVERE, "AssetManager is null, cannot bake environment. Please use setAssetManager() to set it."); + return; + } IBLHybridEnvBakerLight baker; if(!USE_GL_IR){ baker = new IBLHybridEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, envMapSize, envMapSize); From 9ad84f1a81162328ddbeebdad5308d228be73b06 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Mon, 21 Aug 2023 16:41:51 +0200 Subject: [PATCH 11/29] Set default EnvironmentProbeControl area to Float.MAX_VALUE --- .../java/com/jme3/environment/EnvironmentProbeControl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java index e0c0516eee..23ab7d06f1 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -67,6 +67,8 @@ protected EnvironmentProbeControl() { public EnvironmentProbeControl(AssetManager assetManager,int size) { this.envMapSize = size; this.assetManager = assetManager; + this.setAreaType(AreaType.Spherical); + this.getArea().setRadius(Float.MAX_VALUE); } @Override @@ -162,4 +164,4 @@ public void read(JmeImporter im) throws IOException { assetManager = im.getAssetManager(); } -} \ No newline at end of file +} From 6c5322c56f334f8606fcd73fff801ec8963a33f4 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Mon, 21 Aug 2023 18:33:42 +0200 Subject: [PATCH 12/29] Do not call Spatial.updateModelBound() in env baker --- .../main/java/com/jme3/environment/baker/GenericEnvBaker.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java index 448a451cac..4cd2bc9eee 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java @@ -174,7 +174,6 @@ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, viewPort.attachScene(scene); scene.updateLogicalState(0); - scene.updateModelBound(); scene.updateGeometricState(); Function ofilter = renderManager.getRenderFilter(); @@ -249,4 +248,4 @@ protected void endPulling(Texture tx) { tx.getImage().clearUpdateNeeded(); } -} \ No newline at end of file +} From e648d6cf5899199a1047d590c3fe9474f37008b0 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Sat, 2 Sep 2023 15:31:22 +0200 Subject: [PATCH 13/29] Fix compatibility with GL ES. Ensure mipmaps are never recomputed by the engine --- .../environment/baker/GenericEnvBaker.java | 22 ++++++++++------ .../jme3/environment/baker/IBLGLEnvBaker.java | 16 +++++++----- .../environment/baker/IBLGLEnvBakerLight.java | 26 +++++++++++-------- .../baker/IBLHybridEnvBakerLight.java | 26 +++++++++++++------ 4 files changed, 57 insertions(+), 33 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java index 4cd2bc9eee..fffd5990c7 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java @@ -153,17 +153,21 @@ public void clean() { @Override public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar, Function filter) { - FrameBuffer envbaker = new FrameBuffer(env.getImage().getWidth(), env.getImage().getHeight(), 1); + FrameBuffer envbakers[] = new FrameBuffer[6]; + for (int i = 0; i < 6; i++) { + envbakers[i] = new FrameBuffer(env.getImage().getWidth(), env.getImage().getHeight(), 1); + envbakers[i].setDepthTarget(FrameBufferTarget.newTarget(depthFormat)); + envbakers[i].setSrgb(false); + envbakers[i].addColorTarget(FrameBufferTarget.newTarget(env).face(TextureCubeMap.Face.values()[i])); + } - envbaker.setDepthTarget(FrameBufferTarget.newTarget(depthFormat)); - envbaker.setSrgb(false); + if(isTexturePulling())startPulling(); - for (int i = 0; i < 6; i++) envbaker.addColorTarget(FrameBufferTarget.newTarget(env).face(TextureCubeMap.Face.values()[i])); for (int i = 0; i < 6; i++) { - envbaker.setTargetIndex(i); + FrameBuffer envbaker = envbakers[i]; ViewPort viewPort = new ViewPort("EnvBaker", getCam(i, envbaker.getWidth(), envbaker.getHeight(), position, frustumNear, frustumFar)); viewPort.setClearFlags(true, true, true); @@ -187,8 +191,10 @@ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, } if (isTexturePulling()) endPulling(env); - env.getImage().clearUpdateNeeded(); - envbaker.dispose(); + env.getImage().clearUpdateNeeded(); + for (int i = 0; i < 6; i++) { + envbakers[i].dispose(); + } } @@ -248,4 +254,4 @@ protected void endPulling(Texture tx) { tx.getImage().clearUpdateNeeded(); } -} +} \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java index 69a544a6a5..c54dd747ae 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java @@ -123,16 +123,18 @@ public void bakeSpecularIBL() { 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(FrameBufferTarget.newTarget(specular).level(mip).face(i)); + 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)); + } float roughness = (float) mip / (float) (specular.getImage().getMipMapSizes().length - 1); mat.setFloat("Roughness", roughness); for (int i = 0; i < 6; i++) { - specularbaker.setTargetIndex(i); + FrameBuffer specularbaker = specularbakers[i]; mat.setInt("FaceId", i); screen.updateLogicalState(0); @@ -145,7 +147,9 @@ public void bakeSpecularIBL() { if (isTexturePulling()) pull(specularbaker, specular,i); } - specularbaker.dispose(); + for (int i = 0; i < 6; i++) { + specularbakers[i].dispose(); + } } if (isTexturePulling())endPulling(specular); diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java index c9f8112354..dcfd35b454 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java @@ -87,7 +87,7 @@ public void bakeSphericalHarmonicsCoefficients() { float remapMaxValue = 0; Format format = Format.RGBA32F; - if (!renderManager.getRenderer().getCaps().contains(Caps.FloatTexture)) { + if (!renderManager.getRenderer().getCaps().contains(Caps.FloatColorBufferRGBA)) { LOG.warning("Float textures not supported, using RGB8 instead. This may cause accuracy issues."); format = Format.RGBA8; remapMaxValue = 0.05f; @@ -106,10 +106,16 @@ public void bakeSphericalHarmonicsCoefficients() { }; - FrameBuffer shbaker = new FrameBuffer(NUM_SH_COEFFICIENT, 1, 1); - shbaker.setSrgb(false); - shbaker.addColorTarget(FrameBufferTarget.newTarget(shCoefTx[0])); - shbaker.addColorTarget(FrameBufferTarget.newTarget(shCoefTx[1])); + FrameBuffer shbaker[] = { + new FrameBuffer(NUM_SH_COEFFICIENT, 1, 1), + new FrameBuffer(NUM_SH_COEFFICIENT, 1, 1) + }; + shbaker[0].setSrgb(false); + shbaker[0].addColorTarget(FrameBufferTarget.newTarget(shCoefTx[0])); + + shbaker[1].setSrgb(false); + shbaker[1].addColorTarget(FrameBufferTarget.newTarget(shCoefTx[1])); + int renderOnT = -1; @@ -125,19 +131,17 @@ public void bakeSphericalHarmonicsCoefficients() { screen.updateLogicalState(0); screen.updateGeometricState(); - - shbaker.setTargetIndex(renderOnT); - renderManager.setCamera(getCam(0, shbaker.getWidth(), shbaker.getHeight(), Vector3f.ZERO, 1, 1000), false); - renderManager.getRenderer().setFrameBuffer(shbaker); + renderManager.setCamera(getCam(0, shbaker[renderOnT].getWidth(), shbaker[renderOnT].getHeight(), Vector3f.ZERO, 1, 1000), false); + renderManager.getRenderer().setFrameBuffer(shbaker[renderOnT]); renderManager.renderGeometry(screen); } ByteBuffer shCoefRaw = BufferUtils.createByteBuffer( - NUM_SH_COEFFICIENT * 1 * ( shbaker.getColorTarget().getFormat().getBitsPerPixel() / 8) + NUM_SH_COEFFICIENT * 1 * ( shbaker[renderOnT].getColorTarget().getFormat().getBitsPerPixel() / 8) ); - renderManager.getRenderer().readFrameBufferWithFormat(shbaker, shCoefRaw, shbaker.getColorTarget().getFormat()); + renderManager.getRenderer().readFrameBufferWithFormat(shbaker[renderOnT], shCoefRaw, shbaker[renderOnT].getColorTarget().getFormat()); shCoefRaw.rewind(); Image img = new Image(format, NUM_SH_COEFFICIENT, 1, shCoefRaw, ColorSpace.Linear); diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java index 667178f474..48f60e4594 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java @@ -67,9 +67,10 @@ public IBLHybridEnvBakerLight(RenderManager rm, AssetManager am, Format format, super(rm, am, format, depthFormat, env_size); specular = new TextureCubeMap(specular_size, specular_size, format); + specular.setWrap(WrapMode.EdgeClamp); 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); if (nbMipMaps > 6) nbMipMaps = 6; @@ -79,8 +80,10 @@ public IBLHybridEnvBakerLight(RenderManager rm, AssetManager am, Format format, sizes[i] = size * size * (specular.getImage().getFormat().getBitsPerPixel() / 8); } specular.getImage().setMipMapSizes(sizes); - } + specular.getImage().setMipmapsGenerated(true); + } + @Override public boolean isTexturePulling() { // always pull textures from gpu return true; @@ -102,15 +105,20 @@ public void bakeSpecularIBL() { 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(FrameBufferTarget.newTarget(specular).level(mip).face(i)); + 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); + } + float roughness = (float) mip / (float) (specular.getImage().getMipMapSizes().length - 1); mat.setFloat("Roughness", roughness); for (int i = 0; i < 6; i++) { - specularbaker.setTargetIndex(i); + FrameBuffer specularbaker = specularbakers[i]; mat.setInt("FaceId", i); screen.updateLogicalState(0); @@ -123,12 +131,14 @@ public void bakeSpecularIBL() { if (isTexturePulling()) pull(specularbaker, specular, i); } - specularbaker.dispose(); + for (int i = 0; i < 6; i++) { + specularbakers[i].dispose(); + } } if (isTexturePulling()) endPulling(specular); specular.getImage().clearUpdateNeeded(); - + } @Override From d756e17525e883a4cf4c4845694bea69e21c65ae Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Sat, 2 Sep 2023 15:36:40 +0200 Subject: [PATCH 14/29] Refactor accelerated baker code to map more closely with the original baker --- .../main/resources/Common/IBL/IBLKernels.frag | 21 ++++--- .../main/resources/Common/IBL/IBLKernels.j3md | 6 +- .../main/resources/Common/IBL/IBLKernels.vert | 2 + .../Common/IBL/{Math.glsllib => Math.glsl} | 63 ++++++++++--------- .../resources/Common/IBLSphH/IBLSphH.frag | 32 +++++----- .../resources/Common/IBLSphH/IBLSphH.j3md | 6 +- .../resources/Common/IBLSphH/IBLSphH.vert | 2 + 7 files changed, 75 insertions(+), 57 deletions(-) rename jme3-core/src/main/resources/Common/IBL/{Math.glsllib => Math.glsl} (64%) diff --git a/jme3-core/src/main/resources/Common/IBL/IBLKernels.frag b/jme3-core/src/main/resources/Common/IBL/IBLKernels.frag index 40b328144b..7f32c0ae01 100644 --- a/jme3-core/src/main/resources/Common/IBL/IBLKernels.frag +++ b/jme3-core/src/main/resources/Common/IBL/IBLKernels.frag @@ -4,9 +4,9 @@ * https://learnopengl.com/PBR/IBL/Specular-IBL * - Riccardo Balbo */ -#import "Common/IBL/Math.glsllib" +#import "Common/ShaderLib/GLSLCompat.glsllib" +#import "Common/IBL/Math.glsl" -out vec4 outFragColor; in vec2 TexCoords; in vec3 LocalPos; @@ -17,6 +17,7 @@ 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; @@ -26,8 +27,8 @@ void brdfKernel(){ 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); + vec4 Xi = Hammersley(i, SAMPLE_COUNT); + vec3 H = ImportanceSampleGGX(Xi, m_Roughness, N); vec3 L = normalize(2.0 * dot(V, H) * H - V); float NdotL = max(L.z, 0.0); float NdotH = max(H.z, 0.0); @@ -73,13 +74,19 @@ void prefilteredEnvKernel(){ vec3 N = normalize(LocalPos); vec3 R = N; vec3 V = R; + + // float a2 = m_Roughness; + float a2 = m_Roughness * m_Roughness; // jme impl, why? + a2 *= a2; + 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); + vec4 Xi = Hammersley(i, SAMPLE_COUNT); + vec3 H = ImportanceSampleGGX(Xi, a2, N); + float VoH = dot(V,H); + vec3 L = normalize(2.0 * VoH * H - V); float NdotL = max(dot(N, L), 0.0); if(NdotL > 0.0) { // TODO: use mipmap diff --git a/jme3-core/src/main/resources/Common/IBL/IBLKernels.j3md b/jme3-core/src/main/resources/Common/IBL/IBLKernels.j3md index 257fde915d..147c1bb8a4 100644 --- a/jme3-core/src/main/resources/Common/IBL/IBLKernels.j3md +++ b/jme3-core/src/main/resources/Common/IBL/IBLKernels.j3md @@ -1,6 +1,7 @@ MaterialDef IBLKernels { MaterialParameters { + Int BoundDrawBuffer TextureCubeMap EnvMap -LINEAR Float Roughness Int FaceId : 0 @@ -11,8 +12,8 @@ MaterialDef IBLKernels { Technique { - VertexShader GLSL150: Common/IBL/IBLKernels.vert - FragmentShader GLSL150: Common/IBL/IBLKernels.frag + VertexShader GLSL300 GLSL150 : Common/IBL/IBLKernels.vert + FragmentShader GLSL300 GLSL150 : Common/IBL/IBLKernels.frag WorldParameters { WorldMatrix @@ -28,6 +29,7 @@ MaterialDef IBLKernels { } Defines { + BOUND_DRAW_BUFFER: BoundDrawBuffer BRDF:UseBRDF IRRADIANCE: UseIrradiance SIBL: UseSpecularIBL diff --git a/jme3-core/src/main/resources/Common/IBL/IBLKernels.vert b/jme3-core/src/main/resources/Common/IBL/IBLKernels.vert index 6bd2599719..aff3d7eae6 100644 --- a/jme3-core/src/main/resources/Common/IBL/IBLKernels.vert +++ b/jme3-core/src/main/resources/Common/IBL/IBLKernels.vert @@ -1,3 +1,5 @@ +#import "Common/ShaderLib/GLSLCompat.glsllib" + /** * This code is based on the following articles: * https://learnopengl.com/PBR/IBL/Diffuse-irradiance diff --git a/jme3-core/src/main/resources/Common/IBL/Math.glsllib b/jme3-core/src/main/resources/Common/IBL/Math.glsl similarity index 64% rename from jme3-core/src/main/resources/Common/IBL/Math.glsllib rename to jme3-core/src/main/resources/Common/IBL/Math.glsl index 6911e703eb..e7e57240bd 100644 --- a/jme3-core/src/main/resources/Common/IBL/Math.glsllib +++ b/jme3-core/src/main/resources/Common/IBL/Math.glsl @@ -15,48 +15,51 @@ float RadicalInverse_VdC(uint bits) { return float(bits) * 2.3283064365386963e-10; // / 0x100000000 } -vec2 Hammersley(uint i, uint N){ - return vec2(float(i)/float(N), RadicalInverse_VdC(i)); +vec4 Hammersley(uint i, uint N){ + vec4 store=vec4(0); + store.x = float(i) / float(N); + store.y = RadicalInverse_VdC(i); + + float phi = 2.0 * PI *store.x; + store.z = cos(phi); + store.w = sin(phi); + + return store; } -/* -Compatible with GL ES 2 -float VanDerCorput(uint n, uint base){ - float invBase = 1.0 / float(base); - float denom = 1.0; - float result = 0.0; +// float VanDerCorput(uint n, uint base){ +// float invBase = 1.0 / float(base); +// float denom = 1.0; +// float result = 0.0; - for(uint i = 0u; i < 32u; ++i) - { - if(n > 0u) - { - denom = mod(float(n), 2.0); - result += denom * invBase; - invBase = invBase / 2.0; - n = uint(float(n) / 2.0); - } - } +// for(uint i = 0u; i < 32u; ++i) +// { +// if(n > 0u) +// { +// denom = mod(float(n), 2.0); +// result += denom * invBase; +// invBase = invBase / 2.0; +// n = uint(float(n) / 2.0); +// } +// } - return result; -} +// return result; +// } -vec2 Hammersley(uint i, uint N){ - return vec2(float(i)/float(N), VanDerCorput(i, 2u)); -} -*/ +// vec2 Hammersley(uint i, uint N){ +// return vec2(float(i)/float(N), VanDerCorput(i, 2u)); +// } -vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness){ - float a = roughness*roughness; +vec3 ImportanceSampleGGX(vec4 Xi, float a2, vec3 N){ - float phi = 2.0 * PI * Xi.x; - float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); + float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a2 - 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.x = Xi.z * sinTheta; + H.y = Xi.w * sinTheta; H.z = cosTheta; // from tangent-space vector to world-space sample vector diff --git a/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.frag b/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.frag index 04d4a06eb7..6e83dcfde8 100644 --- a/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.frag +++ b/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.frag @@ -2,14 +2,14 @@ * - Riccardo Balbo */ -#import "Common/IBL/Math.glsllib" +#import "Common/ShaderLib/GLSLCompat.glsllib" +#import "Common/IBL/Math.glsl" // #define NUM_SH_COEFFICIENT 9 #ifndef PI #define PI 3.1415926535897932384626433832795 #endif -out vec4 outFragColor; in vec2 TexCoords; in vec3 LocalPos; @@ -22,9 +22,9 @@ uniform vec2 m_Resolution; uniform int m_FaceId; const float sqrtPi = sqrt(PI); -const float sqrt3Pi = sqrt(3 / PI); -const float sqrt5Pi = sqrt(5 / PI); -const float sqrt15Pi = sqrt(15 / PI); +const float sqrt3Pi = sqrt(3. / PI); +const float sqrt5Pi = sqrt(5. / PI); +const float sqrt15Pi = sqrt(15. / PI); #ifdef REMAP_MAX_VALUE uniform float m_RemapMaxValue; @@ -42,31 +42,31 @@ vec3 getVectorFromCubemapFaceTexCoord(float x, float y, float mapSize, int face) // Warp texel centers in the proximity of the edges. - float a = pow(mapSize, 2.0) / pow(mapSize - 1, 3.0); + float a = pow(mapSize, 2.0) / pow(mapSize - 1., 3.0); - u = a * pow(u, 3) + u; - v = a * pow(v, 3) + v; + u = a * pow(u, 3.) + u; + v = a * pow(v, 3.) + v; //compute vector depending on the face // Code from Nvtt : https://github.com/castano/nvidia-texture-tools/blob/master/src/nvtt/CubeSurface.cpp#L101 vec3 o =vec3(0); switch(face) { case 0: - o= normalize(vec3(1, -v, -u)); + o= normalize(vec3(1., -v, -u)); break; case 1: - o= normalize(vec3(-1, -v, u)); + o= normalize(vec3(-1., -v, u)); break; case 2: - o= normalize(vec3(u, 1, v)); + o= normalize(vec3(u, 1., v)); break; case 3: - o= normalize(vec3(u, -1, -v)); + o= normalize(vec3(u, -1., -v)); break; case 4: - o= normalize(vec3(u, -v, 1)); + o= normalize(vec3(u, -v, 1.)); break; case 5: - o= normalize(vec3(-u, -v, -1.0)); + o= normalize(vec3(-u, -v, -1.)); break; } @@ -148,8 +148,8 @@ void sphKernel() { int width = int(m_Resolution.x); int height = int(m_Resolution.y); vec3 texelVect=vec3(0); - float shDir=0; - float weight=0; + float shDir=0.; + float weight=0.; vec4 color=vec4(0); int i=int(gl_FragCoord.x); diff --git a/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.j3md b/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.j3md index eea1990387..eaafd2e108 100644 --- a/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.j3md +++ b/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.j3md @@ -1,6 +1,7 @@ MaterialDef IBLSphH { MaterialParameters { + Int BoundDrawBuffer TextureCubeMap Texture -LINEAR Int FaceId : 0 Texture2D ShCoef -LINEAR @@ -10,8 +11,8 @@ MaterialDef IBLSphH { Technique { - VertexShader GLSL150: Common/IBLSphH/IBLSphH.vert - FragmentShader GLSL150: Common/IBLSphH/IBLSphH.frag + VertexShader GLSL300 GLSL150 : Common/IBLSphH/IBLSphH.vert + FragmentShader GLSL300 GLSL150 : Common/IBLSphH/IBLSphH.frag WorldParameters { } @@ -24,6 +25,7 @@ MaterialDef IBLSphH { } Defines { + BOUND_DRAW_BUFFER: BoundDrawBuffer REMAP_MAX_VALUE: RemapMaxValue SH_COEF: ShCoef } diff --git a/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.vert b/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.vert index 266eb5e02a..f7a3c82655 100644 --- a/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.vert +++ b/jme3-core/src/main/resources/Common/IBLSphH/IBLSphH.vert @@ -1,3 +1,5 @@ +#import "Common/ShaderLib/GLSLCompat.glsllib" + /** *- Riccardo Balbo */ From 0804ecd44ef2951695d402d1f1bbabb34354258d Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Sat, 14 Oct 2023 11:12:37 +0200 Subject: [PATCH 15/29] Drivers workaround: do not crash if lower mips cannot be computed. --- .../environment/EnvironmentProbeControl.java | 5 +- .../jme3/environment/LightProbeFactory2.java | 7 +- .../environment/baker/GenericEnvBaker.java | 5 + .../jme3/environment/baker/IBLGLEnvBaker.java | 109 ++++++++++++------ .../baker/IBLHybridEnvBakerLight.java | 97 ++++++++++------ 5 files changed, 150 insertions(+), 73 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java index 23ab7d06f1..ae9753cd9d 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -136,7 +136,10 @@ void rebakeNow(RenderManager renderManager) { baker.bakeSphericalHarmonicsCoefficients(); setPrefilteredMap(baker.getSpecularIBL()); - setNbMipMaps(getPrefilteredEnvMap().getImage().getMipMapSizes().length); + + int[] mipSizes = getPrefilteredEnvMap().getImage().getMipMapSizes(); + setNbMipMaps(mipSizes != null ? mipSizes.length : 1); + setShCoeffs(baker.getSphericalHarmonicsCoefficients()); setPosition(Vector3f.ZERO); setReady(true); diff --git a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java index 9cf1c7c903..aadea0fc91 100644 --- a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java +++ b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java @@ -70,10 +70,13 @@ public static LightProbe makeProbe(RenderManager rm, baker.bakeSphericalHarmonicsCoefficients(); LightProbe probe = new LightProbe(); - + probe.setPosition(pos); probe.setPrefilteredMap(baker.getSpecularIBL()); - probe.setNbMipMaps(probe.getPrefilteredEnvMap().getImage().getMipMapSizes().length); + + int[] mipSizes = probe.getPrefilteredEnvMap().getImage().getMipMapSizes(); + probe.setNbMipMaps(mipSizes != null ? mipSizes.length : 1); + probe.setShCoeffs(baker.getSphericalHarmonicsCoefficients()); probe.setReady(true); diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java index fffd5990c7..2dd7874f5b 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java @@ -254,4 +254,9 @@ protected void endPulling(Texture tx) { tx.getImage().clearUpdateNeeded(); } + protected int limitMips(int nbMipMaps, int baseW, int baseH,RenderManager rm) { + if (nbMipMaps > 6) nbMipMaps = 6; + return nbMipMaps; + } + } \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java index c54dd747ae..fe2b08411f 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java @@ -33,6 +33,10 @@ 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; @@ -58,7 +62,9 @@ * * @author Riccardo Balbo */ -public class IBLGLEnvBaker extends GenericEnvBaker implements IBLEnvBaker{ +public class IBLGLEnvBaker extends GenericEnvBaker implements IBLEnvBaker { + private final Logger LOGGER=Logger.getLogger(IBLHybridEnvBakerLight.class.getName()); + protected Texture2D brtf; protected TextureCubeMap irradiance; protected TextureCubeMap specular; @@ -82,11 +88,13 @@ public IBLGLEnvBaker(RenderManager rm,AssetManager am, specular.setMinFilter(MinFilter.Trilinear); 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 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 6) nbMipMaps = 6; + 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); @@ -89,6 +95,39 @@ public boolean isTexturePulling() { // always pull textures from gpu return true; } + 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(getCam(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); @@ -101,38 +140,30 @@ public void bakeSpecularIBL() { if (isTexturePulling()) startPulling(); - 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 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); + 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; } - - - float roughness = (float) mip / (float) (specular.getImage().getMipMapSizes().length - 1); - mat.setFloat("Roughness", roughness); - - for (int i = 0; i < 6; i++) { - FrameBuffer specularbaker = specularbakers[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); - - if (isTexturePulling()) pull(specularbaker, specular, i); - - } - for (int i = 0; i < 6; i++) { - specularbakers[i].dispose(); + } + + 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); + } } } From 4aa922de22407bc742c79dbaafc5b3bf1f9725cf Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Fri, 3 Nov 2023 20:21:20 +0100 Subject: [PATCH 16/29] Rename LightProbeFactory2 to FastLightProbeFactory --- ...ctory2.java => FastLightProbeFactory.java} | 66 +++++++++++-------- 1 file changed, 37 insertions(+), 29 deletions(-) rename jme3-core/src/main/java/com/jme3/environment/{LightProbeFactory2.java => FastLightProbeFactory.java} (67%) diff --git a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java b/jme3-core/src/main/java/com/jme3/environment/FastLightProbeFactory.java similarity index 67% rename from jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java rename to jme3-core/src/main/java/com/jme3/environment/FastLightProbeFactory.java index aadea0fc91..d78edc561e 100644 --- a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory2.java +++ b/jme3-core/src/main/java/com/jme3/environment/FastLightProbeFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2019 jMonkeyEngine + * Copyright (c) 2009-2023 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,7 +33,6 @@ import com.jme3.asset.AssetManager; import com.jme3.environment.baker.IBLGLEnvBakerLight; -import com.jme3.environment.baker.IBLHybridEnvBakerLight; import com.jme3.environment.util.EnvMapUtils; import com.jme3.light.LightProbe; import com.jme3.math.Vector3f; @@ -43,40 +42,53 @@ import com.jme3.texture.Image.Format; /** - * A faster version of LightProbeFactory that uses accelerated Baking. + * A faster LightProbeFactory that uses GPU accelerated algorithms. + * This is the GPU version of @{link LightProbeFactory} and should be generally preferred. + * + * For common use cases where the probe is baking the scene or part of the scene around it, it + * is advised to use the @{link EnvironmentProbeControl} instead since it does automatically most of the + * boilerplate work. + * + * * @author Riccardo Balbo */ -public class LightProbeFactory2 { +public class FastLightProbeFactory { - /** - * Creates a LightProbe with the giver EnvironmentCamera in the given scene. - * @param rm The RenderManager - * @param am The AssetManager - * @param size The size of the probe - * @param pos The position of the probe - * @param frustumNear The near frustum of the probe - * @param frustumFar The far frustum of the probe - * @param scene The scene to bake + * Creates a LightProbe with the given EnvironmentCamera in the given scene. + * + * @param rm + * The RenderManager + * @param am + * The AssetManager + * @param size + * The size of the probe + * @param pos + * The position of the probe + * @param frustumNear + * The near frustum of the probe + * @param frustumFar + * The far frustum of the probe + * @param scene + * The scene to bake * @return The baked LightProbe */ - public static LightProbe makeProbe(RenderManager rm, - AssetManager am, int size,Vector3f pos, float frustumNear,float frustumFar,Spatial scene) { - IBLHybridEnvBakerLight baker=new IBLGLEnvBakerLight(rm, am, Format.RGB16F, Format.Depth, size, size); + 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.setTexturePulling(true); - baker.bakeEnvironment(scene,pos, frustumNear,frustumFar,null); + baker.bakeEnvironment(scene, pos, frustumNear, frustumFar, null); baker.bakeSpecularIBL(); baker.bakeSphericalHarmonicsCoefficients(); - + LightProbe probe = new LightProbe(); - + probe.setPosition(pos); probe.setPrefilteredMap(baker.getSpecularIBL()); - + int[] mipSizes = probe.getPrefilteredEnvMap().getImage().getMipMapSizes(); probe.setNbMipMaps(mipSizes != null ? mipSizes.length : 1); - + probe.setShCoeffs(baker.getSphericalHarmonicsCoefficients()); probe.setReady(true); @@ -86,14 +98,12 @@ public static LightProbe makeProbe(RenderManager rm, } - - - /** - * 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. + * For debuging purposes 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 + * @param manager + * the asset manager * @return a debug node */ public static Node getDebugGui(AssetManager manager, LightProbe probe) { @@ -109,6 +119,4 @@ public static Node getDebugGui(AssetManager manager, LightProbe probe) { return debugNode; } - - } From 3a6352883dfa5d9bf6485c3e3857c8e24e8a6975 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Fri, 3 Nov 2023 20:22:45 +0100 Subject: [PATCH 17/29] Add support for probe position, frustum and local tagging --- .../environment/EnvironmentProbeControl.java | 219 ++++++++++++++++-- 1 file changed, 194 insertions(+), 25 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java index ae9753cd9d..a52c219dd2 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -1,10 +1,41 @@ +/* + * Copyright (c) 2009-2023 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 java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; - import com.jme3.asset.AssetManager; import com.jme3.environment.baker.IBLGLEnvBakerLight; import com.jme3.environment.baker.IBLHybridEnvBakerLight; @@ -26,49 +57,134 @@ * A control that automatically handles environment bake and rebake including * only tagged spatials. * + * Simple usage example: + * 1. Load a scene + * Node scene=(Node)assetManager.loadModel("Scenes/MyScene.j3o"); + * 2. Add one or more EnvironmentProbeControl to the root of the scene + * EnvironmentProbeControl ec1=new EnvironmentProbeControl(assetManager, 512); + * // EnvironmentProbeControl ec2=new EnvironmentProbeControl(assetManager, 512); + * 2b. (optional) Set the position of the probes + * ec1.setPosition(new Vector3f(0,0,0)); + * // ec2.setPosition(new Vector3f(0,0,10)); + * 3. Tag the spatials that are part of the environment + * scene.deepFirstTraversal(s->{ + * if(s.getUserData("isEnvNode")!=null){ + * EnvironmentProbeControl.tagGlobal(s); + * // or ec1.tag(s); + * // ec2.tag(s); + * } + * }); + * + * * @author Riccardo Balbo */ public class EnvironmentProbeControl extends LightProbe implements Control { private final boolean USE_GL_IR = true; private static final Logger LOG = Logger.getLogger(EnvironmentProbeControl.class.getName()); + private static AtomicInteger instanceCounter = new AtomicInteger(0); - private AssetManager assetManager; private boolean bakeNeeded = true; - private int envMapSize=256; + private int envMapSize = 256; private Spatial spatial; private boolean serializable = false; + private float frustumNear = 0.001f, frustumFar = 1000f; + private String uuid = "none"; private Function filter = (s) -> { - return s.getUserData("tags.env") != null; + return s.getUserData("tags.env") != null || s.getUserData("tags.env.env" + uuid) != null; }; + protected EnvironmentProbeControl() { + uuid = System.currentTimeMillis() + "_" + instanceCounter.getAndIncrement(); + } + + /** + * Create a new environment probe control. + * + * @param assetManager + * the asset manager used to load the shaders needed for the + * baking + * @param size + * the size of side of the resulting cube map (eg. 1024) + */ + public EnvironmentProbeControl(AssetManager assetManager, int size) { + this(); + this.envMapSize = size; + this.assetManager = assetManager; + this.setAreaType(AreaType.Spherical); + this.getArea().setRadius(Float.MAX_VALUE); + } + /** - * Tag spatial as part of the environment. Only tagged spatials will be - * rendered in the environment map. + * Tag spatial as part of the environment for this EnvironmentProbeControl. + * Only tagged spatials will be rendered in the environment map. * * @param s * the spatial */ - public static void tag(Spatial s) { + public void tag(Spatial s) { if (s instanceof Node) { Node n = (Node) s; for (Spatial sx : n.getChildren()) { tag(sx); } } else if (s instanceof Geometry) { - s.setUserData("tags.env", true); + s.setUserData("tags.env.env" + uuid, true); } } - protected EnvironmentProbeControl() { + /** + * Untag spatial as part of the environment for this + * EnvironmentProbeControl. + * + * @param s + * the spatial + */ + public void untag(Spatial s) { + if (s instanceof Node) { + Node n = (Node) s; + for (Spatial sx : n.getChildren()) { + untag(sx); + } + } else if (s instanceof Geometry) { + s.setUserData("tags.env.env" + uuid, null); + } } - - public EnvironmentProbeControl(AssetManager assetManager,int size) { - this.envMapSize = size; - this.assetManager = assetManager; - this.setAreaType(AreaType.Spherical); - this.getArea().setRadius(Float.MAX_VALUE); + + /** + * Tag spatial as part of the environment for every EnvironmentProbeControl. + * Only tagged spatials will be rendered in the environment map. + * + * @param s + * the spatial + */ + public static void tagGlobal(Spatial s) { + if (s instanceof Node) { + Node n = (Node) s; + for (Spatial sx : n.getChildren()) { + tagGlobal(sx); + } + } else if (s instanceof Geometry) { + s.setUserData("tags.env", true); + } + } + + /** + * Untag spatial as part of the environment for every + * EnvironmentProbeControl. + * + * @param s + */ + public static void untagGlobal(Spatial s) { + if (s instanceof Node) { + Node n = (Node) s; + for (Spatial sx : n.getChildren()) { + untagGlobal(sx); + } + } else if (s instanceof Geometry) { + s.setUserData("tags.env", null); + } } @Override @@ -76,11 +192,24 @@ public Control cloneForSpatial(Spatial spatial) { throw new UnsupportedOperationException(); } - public void setSerializeBakeResults(boolean v) { + /** + * Request savable results from the baking process. This will make the + * baking process slower and more memory intensive but will allow to + * serialize the results with the control. + * + * @param v + * true to enable (default: false) + */ + public void setRequiredSavableResults(boolean v) { serializable = v; } - public boolean isSerializeBakeResults() { + /** + * Return true if savable results are required by this control. + * + * @return true if savable results are required. + */ + public boolean isRequiredSavableResults() { return serializable; } @@ -110,8 +239,43 @@ public void rebake() { bakeNeeded = true; } + /** + * Set the minimum distance to render + * + * @param frustumNear + */ + public void setFrustumNear(float frustumNear) { + this.frustumNear = frustumNear; + } + + /** + * Set the maximum distance to render + */ + public void setFrustumFar(float frustumFar) { + this.frustumFar = frustumFar; + } + + /** + * Get the minimum distance to render + * + * @return frustum near + */ + public float getFrustumNear() { + return frustumNear; + } + + /** + * Get the maximum distance to render + * + * @return frustum far + */ + public float getFrustumFar() { + return frustumFar; + } + /** * Set the asset manager used to load the shaders needed for the baking + * * @param assetManager */ public void setAssetManager(AssetManager assetManager) { @@ -121,25 +285,25 @@ public void setAssetManager(AssetManager assetManager) { void rebakeNow(RenderManager renderManager) { if (assetManager == null) { LOG.log(Level.SEVERE, "AssetManager is null, cannot bake environment. Please use setAssetManager() to set it."); - return; + return; } IBLHybridEnvBakerLight baker; - if(!USE_GL_IR){ + if (!USE_GL_IR) { baker = new IBLHybridEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, envMapSize, envMapSize); } else { baker = new IBLGLEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, envMapSize, envMapSize); } - baker.setTexturePulling(isSerializeBakeResults()); + baker.setTexturePulling(isRequiredSavableResults()); - baker.bakeEnvironment(spatial, Vector3f.ZERO, 0.001f, 1000f, filter); + baker.bakeEnvironment(spatial, getPosition(), frustumNear, frustumFar, filter); baker.bakeSpecularIBL(); baker.bakeSphericalHarmonicsCoefficients(); setPrefilteredMap(baker.getSpecularIBL()); - + int[] mipSizes = getPrefilteredEnvMap().getImage().getMipMapSizes(); setNbMipMaps(mipSizes != null ? mipSizes.length : 1); - + setShCoeffs(baker.getSphericalHarmonicsCoefficients()); setPosition(Vector3f.ZERO); setReady(true); @@ -147,7 +311,6 @@ void rebakeNow(RenderManager renderManager) { baker.clean(); } - @Override public void write(JmeExporter ex) throws IOException { super.write(ex); @@ -155,8 +318,11 @@ public void write(JmeExporter ex) throws IOException { oc.write(envMapSize, "size", 256); oc.write(serializable, "serializable", false); oc.write(bakeNeeded, "bakeNeeded", true); + oc.write(frustumFar, "frustumFar", 1000f); + oc.write(frustumNear, "frustumNear", 0.001f); + oc.write(uuid, "envProbeControlUUID", "none"); } - + @Override public void read(JmeImporter im) throws IOException { super.read(im); @@ -165,6 +331,9 @@ public void read(JmeImporter im) throws IOException { serializable = ic.readBoolean("serializable", false); bakeNeeded = ic.readBoolean("bakeNeeded", true); assetManager = im.getAssetManager(); + frustumFar = ic.readFloat("frustumFar", 1000f); + frustumNear = ic.readFloat("frustumNear", 0.001f); + uuid = ic.readString("envProbeControlUUID", "none"); } } From 4df92a8b02e7c3f324e56a608094feabcaf019d4 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Fri, 3 Nov 2023 20:23:08 +0100 Subject: [PATCH 18/29] formatting and documentation --- .../jme3/environment/LightProbeFactory.java | 31 ++-- .../com/jme3/environment/baker/EnvBaker.java | 30 ++-- .../environment/baker/GenericEnvBaker.java | 79 ++++++---- .../jme3/environment/baker/IBLEnvBaker.java | 10 +- .../environment/baker/IBLEnvBakerLight.java | 6 +- .../jme3/environment/baker/IBLGLEnvBaker.java | 144 +++++++++--------- .../environment/baker/IBLGLEnvBakerLight.java | 56 +++---- .../baker/IBLHybridEnvBakerLight.java | 48 +++--- .../jme3test/light/pbr/TestPBRLighting.java | 4 +- .../jme3test/light/pbr/TestPBRSimple.java | 33 +++- 10 files changed, 256 insertions(+), 185 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java index eac66502d0..7becab82e8 100644 --- a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java +++ b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java @@ -45,36 +45,31 @@ /** * Creates LightProbes within a scene, given an EnvironmentCamera. * - * Since this process can take a long time, you can provide a - * JobProgressListener that will be notified of the ongoing generation process - * when calling the makeProbe method. + * Since this process can take a long time, you can provide a JobProgressListener that + * will be notified of the ongoing generation process when calling the makeProbe method. * - * The process is as follows: 1. Create an EnvironmentCamera 2. give it a - * position in the scene 3. call - * {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Spatial)} - * 4. add the created LightProbe to a node with the - * {@link Node#addLight(com.jme3.light.Light) } method. + * The process is as follows: + * 1. Create an EnvironmentCamera + * 2. give it a position in the scene + * 3. call {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Spatial)} + * 4. add the created LightProbe to a node with the {@link Node#addLight(com.jme3.light.Light) } method. * * Optionally for step 3 call * {@link #makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Spatial, com.jme3.environment.generation.JobProgressListener)} - * with a {@link JobProgressListener} to be notified of the progress of the - * generation process. + * with a {@link JobProgressListener} to be notified of the progress of the generation process. * - * The generation will be split in several threads for faster generation. + * The generation will be split in several threads for faster generation. * - * This class is entirely thread safe and can be called from any thread. + * This class is entirely thread safe and can be called from any thread. * * Note that in case you are using a {@link JobProgressListener}, all its - * methods will be called inside an app.enqueue callable. This means that it's - * completely safe to modify the scenegraph within the Listener method, but also - * means that the event will be delayed until next update loop. + * methods will be called inside an app.enqueue callable. + * This means that it's completely safe to modify the scenegraph within the + * Listener method, but also means that the event will be delayed until next update loop. * - * @deprecated Use LightProbeFactory2 or EnvironmentProbeControl whenever possible. * @see EnvironmentCamera * @author bouquet */ - -@Deprecated public class LightProbeFactory { /** diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java index be20e86197..bc80196c32 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java @@ -33,7 +33,6 @@ package com.jme3.environment.baker; import java.util.function.Function; - import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; @@ -47,38 +46,43 @@ public interface EnvBaker { /** * Bake the environment - * @param scene The scene to bake - * @param position The position of the camera - * @param frustumNear The near frustum - * @param frustumFar The far frustum - * @param filter A filter to select which geometries to bake + * + * @param scene + * The scene to bake + * @param position + * The position of the camera + * @param frustumNear + * The near frustum + * @param frustumFar + * The far frustum + * @param filter + * A filter to select which geometries to bake */ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar, Function filter); - + /** * Get the environment map + * * @return The environment map */ public TextureCubeMap getEnvMap(); /** - * Clean the environment baker - * This method should be called when the baker is no longer needed - * It will clean up all the resources + * Clean the environment baker This method should be called when the baker + * is no longer needed It will clean up all the resources */ public void clean(); - - /** * Set if textures should be pulled from the GPU + * * @param v */ public void setTexturePulling(boolean v); - /** * Get if textures should be pulled from the GPU + * * @return */ public boolean isTexturePulling(); diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java index 2dd7874f5b..3d75eb9fe2 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java @@ -39,7 +39,6 @@ import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; - import com.jme3.asset.AssetManager; import com.jme3.math.ColorRGBA; import com.jme3.math.Quaternion; @@ -66,6 +65,7 @@ * @author Riccardo Balbo */ public abstract class GenericEnvBaker implements EnvBaker { + private static final Logger LOG = Logger.getLogger(GenericEnvBaker.class.getName()); protected static Vector3f[] axisX = new Vector3f[6]; protected static Vector3f[] axisY = new Vector3f[6]; @@ -103,13 +103,10 @@ public abstract class GenericEnvBaker implements EnvBaker { protected final RenderManager renderManager; protected final AssetManager assetManager; protected final Camera cam; - protected boolean texturePulling=false; + protected boolean texturePulling = false; protected List bos = new ArrayList<>(); - private static final Logger LOG=Logger.getLogger(GenericEnvBaker.class.getName()); - - - public GenericEnvBaker(RenderManager rm, AssetManager am, Format colorFormat, Format depthFormat, int env_size) { + protected GenericEnvBaker(RenderManager rm, AssetManager am, Format colorFormat, Format depthFormat, int env_size) { this.depthFormat = depthFormat; renderManager = rm; @@ -138,17 +135,35 @@ public TextureCubeMap getEnvMap() { return env; } - Camera getCam(int id, int w, int h, Vector3f position, float frustumNear, float frustumFar) { + /** + * Update the internal camera to face the given cubemap face + * and return it + * + * @param faceId + * the id of the face (0-5) + * @param w + * width of the camera + * @param h + * height of the camera + * @param position + * position of the camera + * @param frustumNear + * near frustum + * @param frustumFar + * far frustum + * @return The updated camera + */ + protected Camera updateAndGetInternalCamera(int faceId, 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])); + cam.setRotation(new Quaternion().fromAxes(axisX[faceId], axisY[faceId], axisZ[faceId])); return cam; } @Override public void clean() { - + } @Override @@ -161,15 +176,12 @@ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, envbakers[i].addColorTarget(FrameBufferTarget.newTarget(env).face(TextureCubeMap.Face.values()[i])); } - - - if(isTexturePulling())startPulling(); - + if (isTexturePulling()) startPulling(); for (int i = 0; i < 6; i++) { FrameBuffer envbaker = envbakers[i]; - ViewPort viewPort = new ViewPort("EnvBaker", getCam(i, envbaker.getWidth(), envbaker.getHeight(), position, frustumNear, frustumFar)); + ViewPort viewPort = new ViewPort("EnvBaker", updateAndGetInternalCamera(i, envbaker.getWidth(), envbaker.getHeight(), position, frustumNear, frustumFar)); viewPort.setClearFlags(true, true, true); viewPort.setBackgroundColor(ColorRGBA.Pink); @@ -187,16 +199,17 @@ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, renderManager.setRenderFilter(ofilter); if (isTexturePulling()) pull(envbaker, env, i); - + } if (isTexturePulling()) endPulling(env); + env.getImage().clearUpdateNeeded(); + for (int i = 0; i < 6; i++) { envbakers[i].dispose(); } } - /** * Starts pulling the data from the framebuffer into the texture @@ -206,11 +219,15 @@ protected void startPulling() { } /** - * Pulls the data from the framebuffer into the texture - * Nb. mipmaps must be pulled sequentially on the same faceId - * @param fb the framebuffer to pull from - * @param env the texture to pull into - * @param faceId id of face if cubemap or 0 otherwise + * Pulls the data from the framebuffer into the texture Nb. mipmaps must be + * pulled sequentially on the same faceId + * + * @param fb + * the framebuffer to pull from + * @param env + * the texture to pull into + * @param faceId + * id of face if cubemap or 0 otherwise * @return */ protected ByteBuffer pull(FrameBuffer fb, Texture env, int faceId) { @@ -222,7 +239,10 @@ protected ByteBuffer pull(FrameBuffer fb, Texture env, int faceId) { renderManager.getRenderer().readFrameBufferWithFormat(fb, face, fb.getColorTarget().getFormat()); face.rewind(); - while (bos.size() <= faceId) bos.add(null); + while (bos.size() <= faceId) { + bos.add(null); + } + ByteArrayOutputStream bo = bos.get(faceId); if (bo == null) bos.set(faceId, bo = new ByteArrayOutputStream()); try { @@ -235,26 +255,27 @@ protected ByteBuffer pull(FrameBuffer fb, Texture env, int faceId) { return face; } - /** * Ends pulling the data into the texture - * @param tx the texture to pull into + * + * @param tx + * the texture to pull into */ protected void endPulling(Texture tx) { for (int i = 0; i < bos.size(); i++) { ByteArrayOutputStream bo = bos.get(i); - if (bo == null) { + if (bo != null) { + ByteBuffer faceMip = ByteBuffer.wrap(bo.toByteArray()); + tx.getImage().setData(i, faceMip); + } else { LOG.log(Level.SEVERE, "Missing face {0}. Pulling incomplete!", i); - continue; } - ByteBuffer faceMip = ByteBuffer.wrap(bo.toByteArray()); - tx.getImage().setData(i, faceMip); } bos.clear(); tx.getImage().clearUpdateNeeded(); } - protected int limitMips(int nbMipMaps, int baseW, int baseH,RenderManager rm) { + protected int limitMips(int nbMipMaps, int baseW, int baseH, RenderManager rm) { if (nbMipMaps > 6) nbMipMaps = 6; return nbMipMaps; } diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java index 36de5c35bc..84d5bcd35c 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java @@ -30,7 +30,6 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package com.jme3.environment.baker; import com.jme3.texture.Texture2D; @@ -44,10 +43,11 @@ public interface IBLEnvBaker extends EnvBaker { /** * Generate the BRDF texture + * * @return The BRDF texture */ - public Texture2D genBRTF() ; - + public Texture2D genBRTF(); + /** * Bake the irradiance map */ @@ -56,16 +56,18 @@ public interface IBLEnvBaker extends EnvBaker { /** * Bake the specular IBL map */ - public void bakeSpecularIBL() ; + public void bakeSpecularIBL(); /** * Get the specular IBL map + * * @return The specular IBL map */ public TextureCubeMap getSpecularIBL(); /** * Get the irradiance map + * * @return The irradiance map */ public TextureCubeMap getIrradiance(); diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBakerLight.java index 76b2fdeb2c..19275d514e 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBakerLight.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBakerLight.java @@ -30,7 +30,6 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package com.jme3.environment.baker; import com.jme3.math.Vector3f; @@ -38,13 +37,16 @@ /** * 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(); } \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java index fe2b08411f..f023f0e41d 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java @@ -30,13 +30,11 @@ * 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; @@ -56,65 +54,69 @@ 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 final Logger LOGGER=Logger.getLogger(IBLHybridEnvBakerLight.class.getName()); + private final Logger LOGGER = Logger.getLogger(IBLHybridEnvBakerLight.class.getName()); 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); - - irradiance=new TextureCubeMap(irradiance_size,irradiance_size,format); + + /** + * 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 = 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 0) { mat.setFloat("RemapMaxValue", remapMaxValue); } else { mat.clearParam("RemapMaxValue"); } - Texture2D shCoefTx[] = { - new Texture2D(NUM_SH_COEFFICIENT, 1, 1, format), - new Texture2D(NUM_SH_COEFFICIENT, 1, 1, format) - }; - + Texture2D shCoefTx[] = { new Texture2D(NUM_SH_COEFFICIENT, 1, 1, format), new Texture2D(NUM_SH_COEFFICIENT, 1, 1, format) }; - FrameBuffer shbaker[] = { - new FrameBuffer(NUM_SH_COEFFICIENT, 1, 1), - new FrameBuffer(NUM_SH_COEFFICIENT, 1, 1) - }; + FrameBuffer shbaker[] = { new FrameBuffer(NUM_SH_COEFFICIENT, 1, 1), new FrameBuffer(NUM_SH_COEFFICIENT, 1, 1) }; shbaker[0].setSrgb(false); shbaker[0].addColorTarget(FrameBufferTarget.newTarget(shCoefTx[0])); shbaker[1].setSrgb(false); shbaker[1].addColorTarget(FrameBufferTarget.newTarget(shCoefTx[1])); - int renderOnT = -1; @@ -131,21 +138,18 @@ public void bakeSphericalHarmonicsCoefficients() { screen.updateLogicalState(0); screen.updateGeometricState(); - - renderManager.setCamera(getCam(0, shbaker[renderOnT].getWidth(), shbaker[renderOnT].getHeight(), Vector3f.ZERO, 1, 1000), false); + + renderManager.setCamera(updateAndGetInternalCamera(0, shbaker[renderOnT].getWidth(), shbaker[renderOnT].getHeight(), Vector3f.ZERO, 1, 1000), false); renderManager.getRenderer().setFrameBuffer(shbaker[renderOnT]); renderManager.renderGeometry(screen); } - - ByteBuffer shCoefRaw = BufferUtils.createByteBuffer( - NUM_SH_COEFFICIENT * 1 * ( shbaker[renderOnT].getColorTarget().getFormat().getBitsPerPixel() / 8) - ); + ByteBuffer shCoefRaw = BufferUtils.createByteBuffer(NUM_SH_COEFFICIENT * 1 * (shbaker[renderOnT].getColorTarget().getFormat().getBitsPerPixel() / 8)); renderManager.getRenderer().readFrameBufferWithFormat(shbaker[renderOnT], shCoefRaw, shbaker[renderOnT].getColorTarget().getFormat()); shCoefRaw.rewind(); Image img = new Image(format, NUM_SH_COEFFICIENT, 1, shCoefRaw, ColorSpace.Linear); - ImageRaster imgr=ImageRaster.create(img); + ImageRaster imgr = ImageRaster.create(img); shCoef = new Vector3f[NUM_SH_COEFFICIENT]; float weightAccum = 0.0f; @@ -159,14 +163,14 @@ else if (weightAccum != c.a) { } } - + if (remapMaxValue > 0) weightAccum /= remapMaxValue; for (int i = 0; i < NUM_SH_COEFFICIENT; ++i) { - if (remapMaxValue > 0) shCoef[i].divideLocal(remapMaxValue); + if (remapMaxValue > 0) shCoef[i].divideLocal(remapMaxValue); shCoef[i].multLocal(4.0f * FastMath.PI / weightAccum); } - + img.dispose(); } diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java index bce13230b9..6d80c5d089 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java @@ -35,7 +35,6 @@ import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; - import com.jme3.asset.AssetManager; import com.jme3.environment.util.EnvMapUtils; import com.jme3.material.Material; @@ -62,13 +61,28 @@ * @author Riccardo Balbo */ public class IBLHybridEnvBakerLight extends GenericEnvBaker implements IBLEnvBakerLight { - private final Logger LOGGER=Logger.getLogger(IBLHybridEnvBakerLight.class.getName()); + private final Logger LOGGER = Logger.getLogger(IBLHybridEnvBakerLight.class.getName()); protected TextureCubeMap specular; protected Vector3f[] shCoef; - public IBLHybridEnvBakerLight(RenderManager rm, AssetManager am, Format format, Format depthFormat, int env_size, int specular_size - - ) { + /** + * 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) + */ + public IBLHybridEnvBakerLight(RenderManager rm, AssetManager am, Format format, Format depthFormat, int env_size, int specular_size) { super(rm, am, format, depthFormat, env_size); specular = new TextureCubeMap(specular_size, specular_size, format); @@ -78,7 +92,7 @@ public IBLHybridEnvBakerLight(RenderManager rm, AssetManager am, Format format, 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); + nbMipMaps = limitMips(nbMipMaps, specular.getImage().getWidth(), specular.getImage().getHeight(), rm); int[] sizes = new int[nbMipMaps]; for (int i = 0; i < nbMipMaps; i++) { @@ -89,7 +103,7 @@ public IBLHybridEnvBakerLight(RenderManager rm, AssetManager am, Format format, specular.getImage().setMipmapsGenerated(true); } - + @Override public boolean isTexturePulling() { // always pull textures from gpu return true; @@ -116,7 +130,7 @@ private void bakeSpecularIBL(int mip, float roughness, Material mat, Geometry sc screen.updateLogicalState(0); screen.updateGeometricState(); - renderManager.setCamera(getCam(i, specularbaker.getWidth(), specularbaker.getHeight(), Vector3f.ZERO, 1, 1000), false); + renderManager.setCamera(updateAndGetInternalCamera(i, specularbaker.getWidth(), specularbaker.getHeight(), Vector3f.ZERO, 1, 1000), false); renderManager.getRenderer().setFrameBuffer(specularbaker); renderManager.renderGeometry(screen); @@ -146,30 +160,30 @@ public void bakeSpecularIBL() { 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); + 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); + + 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)"); + 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); + LOGGER.log(Level.FINE, "Error while recomputing mip level 0", e); } } } - + if (isTexturePulling()) endPulling(specular); specular.getImage().clearUpdateNeeded(); - + } @Override diff --git a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java index bd6d9f5b59..50fb4a482b 100644 --- a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java +++ b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java @@ -34,7 +34,7 @@ import com.jme3.app.SimpleApplication; import com.jme3.environment.EnvironmentCamera; import com.jme3.environment.LightProbeFactory; -import com.jme3.environment.LightProbeFactory2; +import com.jme3.environment.FastLightProbeFactory; import com.jme3.environment.generation.JobProgressAdapter; import com.jme3.environment.util.EnvMapUtils; import com.jme3.environment.util.LightsDebugState; @@ -204,7 +204,7 @@ public void simpleUpdate(float tpf) { LightProbe probe; if (USE_ACCELERATED_BAKING) { - probe = LightProbeFactory2.makeProbe(renderManager, assetManager, RESOLUTION, Vector3f.ZERO, 1f, 1000f, rootNode); + probe = FastLightProbeFactory.makeProbe(renderManager, assetManager, RESOLUTION, Vector3f.ZERO, 1f, 1000f, rootNode); } else { probe = LightProbeFactory.makeProbe(stateManager.getState(EnvironmentCamera.class), rootNode, new JobProgressAdapter() { diff --git a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java index 21e2102dd7..b39c95f91d 100644 --- a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java +++ b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java @@ -1,3 +1,34 @@ +/* + * Copyright (c) 2009-2023 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 jme3test.light.pbr; @@ -49,7 +80,7 @@ public void simpleInitApp() { rootNode.addControl(envProbe); // Tag the sky, only the tagged spatials will be rendered in the env map - EnvironmentProbeControl.tag(sky); + envProbe.tag(sky); From a77eb2c881143b207c8822df0ec3c33fa3d39990 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Fri, 3 Nov 2023 20:31:35 +0100 Subject: [PATCH 19/29] Remove new line --- .../main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java index d41f11806e..5fab803c5a 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java @@ -33,7 +33,6 @@ import java.nio.ByteBuffer; import java.util.logging.Logger; - import com.jme3.asset.AssetManager; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; From da2e3b3353db26a44c22ae7971e7c9053d2a852a Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Wed, 8 Nov 2023 12:25:12 +0100 Subject: [PATCH 20/29] Add AbstractControl functionalities to EnvironmentProbeControl --- .../environment/EnvironmentProbeControl.java | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java index a52c219dd2..c3d60cfe4c 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -90,6 +90,7 @@ public class EnvironmentProbeControl extends LightProbe implements Control { private boolean serializable = false; private float frustumNear = 0.001f, frustumFar = 1000f; private String uuid = "none"; + private boolean enabled = true; private Function filter = (s) -> { return s.getUserData("tags.env") != null || s.getUserData("tags.env.env" + uuid) != null; @@ -215,8 +216,11 @@ public boolean isRequiredSavableResults() { @Override public void setSpatial(Spatial spatial) { - spatial.addLight(this); + if (this.spatial != null && spatial != null && spatial != this.spatial) { + throw new IllegalStateException("This control has already been added to a Spatial"); + } this.spatial = spatial; + if (spatial != null) spatial.addLight(this); } @Override @@ -226,6 +230,7 @@ public void update(float tpf) { @Override public void render(RenderManager rm, ViewPort vp) { + if (!isEnabled()) return; if (bakeNeeded) { bakeNeeded = false; rebakeNow(rm); @@ -284,14 +289,17 @@ public void setAssetManager(AssetManager assetManager) { void rebakeNow(RenderManager renderManager) { if (assetManager == null) { - LOG.log(Level.SEVERE, "AssetManager is null, cannot bake environment. Please use setAssetManager() to set it."); + LOG.log(Level.SEVERE, + "AssetManager is null, cannot bake environment. Please use setAssetManager() to set it."); return; } IBLHybridEnvBakerLight baker; if (!USE_GL_IR) { - baker = new IBLHybridEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, envMapSize, envMapSize); + baker = new IBLHybridEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, + envMapSize, envMapSize); } else { - baker = new IBLGLEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, envMapSize, envMapSize); + baker = new IBLGLEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, + envMapSize, envMapSize); } baker.setTexturePulling(isRequiredSavableResults()); @@ -310,11 +318,25 @@ void rebakeNow(RenderManager renderManager) { baker.clean(); } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isEnabled() { + return enabled; + } + + public Spatial getSpatial() { + return spatial; + } @Override public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule oc = ex.getCapsule(this); + oc.write(enabled, "enabled", true); + oc.write(spatial, "spatial", null); oc.write(envMapSize, "size", 256); oc.write(serializable, "serializable", false); oc.write(bakeNeeded, "bakeNeeded", true); @@ -327,6 +349,8 @@ public void write(JmeExporter ex) throws IOException { public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule ic = im.getCapsule(this); + enabled = ic.readBoolean("enabled", true); + spatial = (Spatial) ic.readSavable("spatial", null); envMapSize = ic.readInt("size", 256); serializable = ic.readBoolean("serializable", false); bakeNeeded = ic.readBoolean("bakeNeeded", true); From a846a637875a80e80898b46ad414a65953e3234f Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Wed, 8 Nov 2023 12:27:10 +0100 Subject: [PATCH 21/29] Rename serializable to requiredSavableResults --- .../com/jme3/environment/EnvironmentProbeControl.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java index c3d60cfe4c..0d928e2f72 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -87,7 +87,7 @@ public class EnvironmentProbeControl extends LightProbe implements Control { private boolean bakeNeeded = true; private int envMapSize = 256; private Spatial spatial; - private boolean serializable = false; + private boolean requiredSavableResults = false; private float frustumNear = 0.001f, frustumFar = 1000f; private String uuid = "none"; private boolean enabled = true; @@ -202,7 +202,7 @@ public Control cloneForSpatial(Spatial spatial) { * true to enable (default: false) */ public void setRequiredSavableResults(boolean v) { - serializable = v; + requiredSavableResults = v; } /** @@ -211,7 +211,7 @@ public void setRequiredSavableResults(boolean v) { * @return true if savable results are required. */ public boolean isRequiredSavableResults() { - return serializable; + return requiredSavableResults; } @Override @@ -338,7 +338,7 @@ public void write(JmeExporter ex) throws IOException { oc.write(enabled, "enabled", true); oc.write(spatial, "spatial", null); oc.write(envMapSize, "size", 256); - oc.write(serializable, "serializable", false); + oc.write(requiredSavableResults, "requiredSavableResults", false); oc.write(bakeNeeded, "bakeNeeded", true); oc.write(frustumFar, "frustumFar", 1000f); oc.write(frustumNear, "frustumNear", 0.001f); @@ -352,7 +352,7 @@ public void read(JmeImporter im) throws IOException { enabled = ic.readBoolean("enabled", true); spatial = (Spatial) ic.readSavable("spatial", null); envMapSize = ic.readInt("size", 256); - serializable = ic.readBoolean("serializable", false); + requiredSavableResults = ic.readBoolean("requiredSavableResults", false); bakeNeeded = ic.readBoolean("bakeNeeded", true); assetManager = im.getAssetManager(); frustumFar = ic.readFloat("frustumFar", 1000f); From 8314499aa6181c37d7e1cd54e739c414cf51c6a5 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Wed, 8 Nov 2023 12:30:48 +0100 Subject: [PATCH 22/29] Replace Function with Predicate --- .../com/jme3/environment/EnvironmentProbeControl.java | 4 ++-- .../main/java/com/jme3/environment/baker/EnvBaker.java | 4 ++-- .../com/jme3/environment/baker/GenericEnvBaker.java | 6 +++--- .../src/main/java/com/jme3/renderer/RenderManager.java | 10 +++++----- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java index 0d928e2f72..c1b02a8d88 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -33,7 +33,7 @@ import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Function; +import java.util.function.Predicate; import java.util.logging.Level; import java.util.logging.Logger; import com.jme3.asset.AssetManager; @@ -92,7 +92,7 @@ public class EnvironmentProbeControl extends LightProbe implements Control { private String uuid = "none"; private boolean enabled = true; - private Function filter = (s) -> { + private Predicate filter = (s) -> { return s.getUserData("tags.env") != null || s.getUserData("tags.env.env" + uuid) != null; }; diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java index bc80196c32..d7f8cc5d29 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java @@ -32,7 +32,7 @@ package com.jme3.environment.baker; -import java.util.function.Function; +import java.util.function.Predicate; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; @@ -58,7 +58,7 @@ public interface EnvBaker { * @param filter * A filter to select which geometries to bake */ - public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar, Function filter); + public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar, Predicate filter); /** * Get the environment map diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java index 3d75eb9fe2..fadcb9e39a 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java @@ -36,7 +36,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; -import java.util.function.Function; +import java.util.function.Predicate; import java.util.logging.Level; import java.util.logging.Logger; import com.jme3.asset.AssetManager; @@ -167,7 +167,7 @@ public void clean() { } @Override - public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar, Function filter) { + public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar, Predicate filter) { FrameBuffer envbakers[] = new FrameBuffer[6]; for (int i = 0; i < 6; i++) { envbakers[i] = new FrameBuffer(env.getImage().getWidth(), env.getImage().getHeight(), 1); @@ -192,7 +192,7 @@ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, scene.updateLogicalState(0); scene.updateGeometricState(); - Function ofilter = renderManager.getRenderFilter(); + Predicate ofilter = renderManager.getRenderFilter(); renderManager.setRenderFilter(filter); renderManager.renderViewPort(viewPort, 0.16f); diff --git a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java index d556737cf0..aa50608acc 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java @@ -66,7 +66,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.function.Function; +import java.util.function.Predicate; import java.util.logging.Logger; /** @@ -105,7 +105,7 @@ public class RenderManager { private TechniqueDef.LightMode preferredLightMode = TechniqueDef.LightMode.MultiPass; private int singlePassLightBatchSize = 1; private MatParamOverride boundDrawBufferId=new MatParamOverride(VarType.Int,"BoundDrawBuffer",0); - private Function renderFilter; + private Predicate renderFilter; /** @@ -628,7 +628,7 @@ public void updateUniformBindings(Shader shader) { * @see com.jme3.material.Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) */ public void renderGeometry(Geometry geom) { - if (renderFilter != null && !renderFilter.apply(geom)) return; + if (renderFilter != null && !renderFilter.test(geom)) return; this.renderer.pushDebugGroup(geom.getName()); if (geom.isIgnoreTransform()) { setWorldMatrix(Matrix4f.IDENTITY); @@ -1345,7 +1345,7 @@ public void setPassDrawBufferTargetIdToShaders(boolean v) { * * @param filter */ - public void setRenderFilter(Function filter) { + public void setRenderFilter(Predicate filter) { renderFilter = filter; } @@ -1355,7 +1355,7 @@ public void setRenderFilter(Function filter) { * @param filter * the render filter */ - public Function getRenderFilter() { + public Predicate getRenderFilter() { return renderFilter; } From cbb2166d0ac2d132edf3dec838ac814f15a0d6a0 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Fri, 10 Nov 2023 09:15:24 +0100 Subject: [PATCH 23/29] improvements --- .../environment/EnvironmentProbeControl.java | 36 ++++++---------- .../com/jme3/environment/baker/EnvBaker.java | 2 +- .../environment/baker/GenericEnvBaker.java | 18 +++++--- .../jme3/environment/baker/IBLGLEnvBaker.java | 43 ++++++++++++++----- .../environment/baker/IBLGLEnvBakerLight.java | 2 +- .../baker/IBLHybridEnvBakerLight.java | 14 ++++-- .../jme3test/light/pbr/TestPBRSimple.java | 2 +- 7 files changed, 70 insertions(+), 47 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java index c1b02a8d88..f410347b2b 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -34,8 +34,6 @@ import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; -import java.util.logging.Level; -import java.util.logging.Logger; import com.jme3.asset.AssetManager; import com.jme3.environment.baker.IBLGLEnvBakerLight; import com.jme3.environment.baker.IBLHybridEnvBakerLight; @@ -79,8 +77,6 @@ * @author Riccardo Balbo */ public class EnvironmentProbeControl extends LightProbe implements Control { - private final boolean USE_GL_IR = true; - private static final Logger LOG = Logger.getLogger(EnvironmentProbeControl.class.getName()); private static AtomicInteger instanceCounter = new AtomicInteger(0); private AssetManager assetManager; @@ -97,7 +93,10 @@ public class EnvironmentProbeControl extends LightProbe implements Control { }; protected EnvironmentProbeControl() { + super(); uuid = System.currentTimeMillis() + "_" + instanceCounter.getAndIncrement(); + this.setAreaType(AreaType.Spherical); + this.getArea().setRadius(Float.MAX_VALUE); } /** @@ -112,9 +111,7 @@ protected EnvironmentProbeControl() { public EnvironmentProbeControl(AssetManager assetManager, int size) { this(); this.envMapSize = size; - this.assetManager = assetManager; - this.setAreaType(AreaType.Spherical); - this.getArea().setRadius(Float.MAX_VALUE); + this.assetManager = assetManager; } /** @@ -175,7 +172,7 @@ public static void tagGlobal(Spatial s) { * Untag spatial as part of the environment for every * EnvironmentProbeControl. * - * @param s + * @param s the spatial */ public static void untagGlobal(Spatial s) { if (s instanceof Node) { @@ -247,7 +244,7 @@ public void rebake() { /** * Set the minimum distance to render * - * @param frustumNear + * @param frustumNear the minimum distance to render */ public void setFrustumNear(float frustumNear) { this.frustumNear = frustumNear; @@ -255,6 +252,8 @@ public void setFrustumNear(float frustumNear) { /** * Set the maximum distance to render + * + * @param frustumFar the maximum distance to render */ public void setFrustumFar(float frustumFar) { this.frustumFar = frustumFar; @@ -281,28 +280,17 @@ public float getFrustumFar() { /** * Set the asset manager used to load the shaders needed for the baking * - * @param assetManager + * @param assetManager the asset manager */ public void setAssetManager(AssetManager assetManager) { this.assetManager = assetManager; } void rebakeNow(RenderManager renderManager) { - if (assetManager == null) { - LOG.log(Level.SEVERE, - "AssetManager is null, cannot bake environment. Please use setAssetManager() to set it."); - return; - } - IBLHybridEnvBakerLight baker; - if (!USE_GL_IR) { - baker = new IBLHybridEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, - envMapSize, envMapSize); - } else { - baker = new IBLGLEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, - envMapSize, envMapSize); - } + IBLHybridEnvBakerLight baker = new IBLGLEnvBakerLight(renderManager, assetManager, Format.RGB16F, Format.Depth, + envMapSize, envMapSize); + baker.setTexturePulling(isRequiredSavableResults()); - baker.bakeEnvironment(spatial, getPosition(), frustumNear, frustumFar, filter); baker.bakeSpecularIBL(); baker.bakeSphericalHarmonicsCoefficients(); diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java index d7f8cc5d29..b1b74e0ec4 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java @@ -39,7 +39,7 @@ import com.jme3.texture.TextureCubeMap; /** - * An environment baker. It bakes the environment. + * An environment baker to bake a 3d environment into a cubemap * * @author Riccardo Balbo */ diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java index fadcb9e39a..489b70a7b6 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java @@ -176,7 +176,9 @@ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, envbakers[i].addColorTarget(FrameBufferTarget.newTarget(env).face(TextureCubeMap.Face.values()[i])); } - if (isTexturePulling()) startPulling(); + if (isTexturePulling()) { + startPulling(); + } for (int i = 0; i < 6; i++) { FrameBuffer envbaker = envbakers[i]; @@ -198,11 +200,15 @@ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, renderManager.renderViewPort(viewPort, 0.16f); renderManager.setRenderFilter(ofilter); - if (isTexturePulling()) pull(envbaker, env, i); + if (isTexturePulling()) { + pull(envbaker, env, i); + } } - if (isTexturePulling()) endPulling(env); + if (isTexturePulling()) { + endPulling(env); + } env.getImage().clearUpdateNeeded(); @@ -228,7 +234,7 @@ protected void startPulling() { * the texture to pull into * @param faceId * id of face if cubemap or 0 otherwise - * @return + * @return the ByteBuffer containing the pulled data */ protected ByteBuffer pull(FrameBuffer fb, Texture env, int faceId) { @@ -276,7 +282,9 @@ protected void endPulling(Texture tx) { } protected int limitMips(int nbMipMaps, int baseW, int baseH, RenderManager rm) { - if (nbMipMaps > 6) nbMipMaps = 6; + if (nbMipMaps > 6) { + nbMipMaps = 6; + } return nbMipMaps; } diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java index f023f0e41d..59b45f636b 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java @@ -60,7 +60,7 @@ * @author Riccardo Balbo */ public class IBLGLEnvBaker extends GenericEnvBaker implements IBLEnvBaker { - private final Logger LOGGER = Logger.getLogger(IBLHybridEnvBakerLight.class.getName()); + private static final Logger LOGGER = Logger.getLogger(IBLHybridEnvBakerLight.class.getName()); protected Texture2D brtf; protected TextureCubeMap irradiance; @@ -142,7 +142,9 @@ private void bakeSpecularIBL(int mip, float roughness, Material mat, Geometry sc renderManager.getRenderer().setFrameBuffer(specularbaker); renderManager.renderGeometry(screen); - if (isTexturePulling()) pull(specularbaker, specular, i); + if (isTexturePulling()) { + pull(specularbaker, specular, i); + } } for (int i = 0; i < 6; i++) { @@ -160,7 +162,9 @@ public void bakeSpecularIBL() { mat.setTexture("EnvMap", env); screen.setMaterial(mat); - if (isTexturePulling()) startPulling(); + if (isTexturePulling()) { + startPulling(); + } int mip = 0; for (; mip < specular.getImage().getMipMapSizes().length; mip++) { @@ -189,7 +193,9 @@ public void bakeSpecularIBL() { } } - if (isTexturePulling()) endPulling(specular); + if (isTexturePulling()) { + endPulling(specular); + } specular.getImage().clearUpdateNeeded(); } @@ -205,7 +211,9 @@ public Texture2D genBRTF() { brtfbaker.setSrgb(false); brtfbaker.addColorTarget(FrameBufferTarget.newTarget(brtf)); - if (isTexturePulling()) startPulling(); + if (isTexturePulling()) { + startPulling(); + } Camera envcam = updateAndGetInternalCamera(0, brtf.getImage().getWidth(), brtf.getImage().getHeight(), Vector3f.ZERO, 1, 1000); @@ -220,11 +228,15 @@ public Texture2D genBRTF() { screen.updateGeometricState(); renderManager.renderGeometry(screen); - if (isTexturePulling()) pull(brtfbaker, brtf, 0); + if (isTexturePulling()) { + pull(brtfbaker, brtf, 0); + } brtfbaker.dispose(); - if (isTexturePulling()) endPulling(brtf); + if (isTexturePulling()) { + endPulling(brtf); + } brtf.getImage().clearUpdateNeeded(); return brtf; @@ -239,9 +251,14 @@ public void bakeIrradiance() { FrameBuffer irradiancebaker = new FrameBuffer(irradiance.getImage().getWidth(), irradiance.getImage().getHeight(), 1); irradiancebaker.setSrgb(false); - if (isTexturePulling()) startPulling(); + if (isTexturePulling()) { + startPulling(); + } - for (int i = 0; i < 6; i++) irradiancebaker.addColorTarget(FrameBufferTarget.newTarget(irradiance).face(TextureCubeMap.Face.values()[i])); + 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); @@ -260,12 +277,16 @@ public void bakeIrradiance() { renderManager.getRenderer().setFrameBuffer(irradiancebaker); renderManager.renderGeometry(screen); - if (isTexturePulling()) pull(irradiancebaker, irradiance, i); + if (isTexturePulling()) { + pull(irradiancebaker, irradiance, i); + } } irradiancebaker.dispose(); - if (isTexturePulling()) endPulling(irradiance); + if (isTexturePulling()) { + endPulling(irradiance); + } irradiance.getImage().clearUpdateNeeded(); } diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java index 5fab803c5a..b31c3ff2f4 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java @@ -61,7 +61,7 @@ * @author Riccardo Balbo */ public class IBLGLEnvBakerLight extends IBLHybridEnvBakerLight { - public final static int NUM_SH_COEFFICIENT = 9; + private static final int NUM_SH_COEFFICIENT = 9; private static final Logger LOG = Logger.getLogger(IBLGLEnvBakerLight.class.getName()); /** diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java index 6d80c5d089..c09b05a588 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java @@ -61,7 +61,7 @@ * @author Riccardo Balbo */ public class IBLHybridEnvBakerLight extends GenericEnvBaker implements IBLEnvBakerLight { - private final Logger LOGGER = Logger.getLogger(IBLHybridEnvBakerLight.class.getName()); + private static final Logger LOGGER = Logger.getLogger(IBLHybridEnvBakerLight.class.getName()); protected TextureCubeMap specular; protected Vector3f[] shCoef; @@ -134,7 +134,9 @@ private void bakeSpecularIBL(int mip, float roughness, Material mat, Geometry sc renderManager.getRenderer().setFrameBuffer(specularbaker); renderManager.renderGeometry(screen); - if (isTexturePulling()) pull(specularbaker, specular, i); + if (isTexturePulling()) { + pull(specularbaker, specular, i); + } } for (int i = 0; i < 6; i++) { @@ -152,7 +154,9 @@ public void bakeSpecularIBL() { mat.setTexture("EnvMap", env); screen.setMaterial(mat); - if (isTexturePulling()) startPulling(); + if (isTexturePulling()) { + startPulling(); + } int mip = 0; for (; mip < specular.getImage().getMipMapSizes().length; mip++) { @@ -181,7 +185,9 @@ public void bakeSpecularIBL() { } } - if (isTexturePulling()) endPulling(specular); + if (isTexturePulling()) { + endPulling(specular); + } specular.getImage().clearUpdateNeeded(); } diff --git a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java index b39c95f91d..4070c2a401 100644 --- a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java +++ b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java @@ -43,7 +43,7 @@ import com.jme3.util.mikktspace.MikktspaceTangentGenerator; /** - * TestPBRSimple + * A simpler PBR example that uses EnvironmentProbeControl to bake the environment */ public class TestPBRSimple extends SimpleApplication { private boolean REALTIME_BAKING = false; From 7b87ff59e54038c3933beac5c1eb631ebb2ca52f Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Fri, 10 Nov 2023 09:17:31 +0100 Subject: [PATCH 24/29] Rename env -> envMap --- .../environment/baker/GenericEnvBaker.java | 24 +++++++++---------- .../jme3/environment/baker/IBLGLEnvBaker.java | 4 ++-- .../environment/baker/IBLGLEnvBakerLight.java | 4 ++-- .../baker/IBLHybridEnvBakerLight.java | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java index 489b70a7b6..f12c30aabc 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java @@ -97,7 +97,7 @@ public abstract class GenericEnvBaker implements EnvBaker { axisZ[5] = Vector3f.UNIT_Z.mult(-1.0F); } - protected TextureCubeMap env; + protected TextureCubeMap envMap; protected Format depthFormat; protected final RenderManager renderManager; @@ -114,11 +114,11 @@ protected GenericEnvBaker(RenderManager rm, AssetManager am, Format colorFormat, 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); + envMap = new TextureCubeMap(env_size, env_size, colorFormat); + envMap.setMagFilter(MagFilter.Bilinear); + envMap.setMinFilter(MinFilter.BilinearNoMipMaps); + envMap.setWrap(WrapMode.EdgeClamp); + envMap.getImage().setColorSpace(ColorSpace.Linear); } @Override @@ -132,7 +132,7 @@ public boolean isTexturePulling() { } public TextureCubeMap getEnvMap() { - return env; + return envMap; } /** @@ -170,10 +170,10 @@ public void clean() { public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar, Predicate filter) { FrameBuffer envbakers[] = new FrameBuffer[6]; for (int i = 0; i < 6; i++) { - envbakers[i] = new FrameBuffer(env.getImage().getWidth(), env.getImage().getHeight(), 1); + envbakers[i] = new FrameBuffer(envMap.getImage().getWidth(), envMap.getImage().getHeight(), 1); envbakers[i].setDepthTarget(FrameBufferTarget.newTarget(depthFormat)); envbakers[i].setSrgb(false); - envbakers[i].addColorTarget(FrameBufferTarget.newTarget(env).face(TextureCubeMap.Face.values()[i])); + envbakers[i].addColorTarget(FrameBufferTarget.newTarget(envMap).face(TextureCubeMap.Face.values()[i])); } if (isTexturePulling()) { @@ -201,16 +201,16 @@ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, renderManager.setRenderFilter(ofilter); if (isTexturePulling()) { - pull(envbaker, env, i); + pull(envbaker, envMap, i); } } if (isTexturePulling()) { - endPulling(env); + endPulling(envMap); } - env.getImage().clearUpdateNeeded(); + envMap.getImage().clearUpdateNeeded(); for (int i = 0; i < 6; i++) { envbakers[i].dispose(); diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java index 59b45f636b..0a28664e0d 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java @@ -159,7 +159,7 @@ public void bakeSpecularIBL() { Material mat = new Material(assetManager, "Common/IBL/IBLKernels.j3md"); mat.setBoolean("UseSpecularIBL", true); - mat.setTexture("EnvMap", env); + mat.setTexture("EnvMap", envMap); screen.setMaterial(mat); if (isTexturePulling()) { @@ -262,7 +262,7 @@ public void bakeIrradiance() { Material mat = new Material(assetManager, "Common/IBL/IBLKernels.j3md"); mat.setBoolean("UseIrradiance", true); - mat.setTexture("EnvMap", env); + mat.setTexture("EnvMap", envMap); screen.setMaterial(mat); for (int i = 0; i < 6; i++) { diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java index b31c3ff2f4..8daa62ef40 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java @@ -96,8 +96,8 @@ public void bakeSphericalHarmonicsCoefficients() { Geometry screen = new Geometry("BakeBox", boxm); Material mat = new Material(assetManager, "Common/IBLSphH/IBLSphH.j3md"); - mat.setTexture("Texture", env); - mat.setVector2("Resolution", new Vector2f(env.getImage().getWidth(), env.getImage().getHeight())); + mat.setTexture("Texture", envMap); + mat.setVector2("Resolution", new Vector2f(envMap.getImage().getWidth(), envMap.getImage().getHeight())); screen.setMaterial(mat); float remapMaxValue = 0; diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java index c09b05a588..d72e0dc30f 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java @@ -151,7 +151,7 @@ public void bakeSpecularIBL() { Material mat = new Material(assetManager, "Common/IBL/IBLKernels.j3md"); mat.setBoolean("UseSpecularIBL", true); - mat.setTexture("EnvMap", env); + mat.setTexture("EnvMap", envMap); screen.setMaterial(mat); if (isTexturePulling()) { From 4665aaac98703fb2d91bb18da3566bec3812275b Mon Sep 17 00:00:00 2001 From: SceneMax3D Date: Sun, 21 Jan 2024 22:09:48 +0200 Subject: [PATCH 25/29] JavaDoc corrections --- .../environment/EnvironmentProbeControl.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java index f410347b2b..fadd35f4a7 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -100,7 +100,7 @@ protected EnvironmentProbeControl() { } /** - * Create a new environment probe control. + * Creates a new environment probe control. * * @param assetManager * the asset manager used to load the shaders needed for the @@ -115,7 +115,7 @@ public EnvironmentProbeControl(AssetManager assetManager, int size) { } /** - * Tag spatial as part of the environment for this EnvironmentProbeControl. + * Tags the specified spatial as part of the environment for this EnvironmentProbeControl. * Only tagged spatials will be rendered in the environment map. * * @param s @@ -133,7 +133,7 @@ public void tag(Spatial s) { } /** - * Untag spatial as part of the environment for this + * Untags the specified spatial as part of the environment for this * EnvironmentProbeControl. * * @param s @@ -151,7 +151,7 @@ public void untag(Spatial s) { } /** - * Tag spatial as part of the environment for every EnvironmentProbeControl. + * Tags the specified spatial as part of the environment for every EnvironmentProbeControl. * Only tagged spatials will be rendered in the environment map. * * @param s @@ -169,7 +169,7 @@ public static void tagGlobal(Spatial s) { } /** - * Untag spatial as part of the environment for every + * Untags the specified spatial as part of the environment for every * EnvironmentProbeControl. * * @param s the spatial @@ -191,7 +191,7 @@ public Control cloneForSpatial(Spatial spatial) { } /** - * Request savable results from the baking process. This will make the + * Requests savable results from the baking process. This will make the * baking process slower and more memory intensive but will allow to * serialize the results with the control. * @@ -203,7 +203,7 @@ public void setRequiredSavableResults(boolean v) { } /** - * Return true if savable results are required by this control. + * Returns true if savable results are required by this control. * * @return true if savable results are required. */ @@ -235,14 +235,14 @@ public void render(RenderManager rm, ViewPort vp) { } /** - * Schedule a rebake of the environment map. + * Schedules a rebake of the environment map. */ public void rebake() { bakeNeeded = true; } /** - * Set the minimum distance to render + * Sets the minimum distance to render. * * @param frustumNear the minimum distance to render */ @@ -251,7 +251,7 @@ public void setFrustumNear(float frustumNear) { } /** - * Set the maximum distance to render + * Sets the maximum distance to render. * * @param frustumFar the maximum distance to render */ @@ -269,7 +269,7 @@ public float getFrustumNear() { } /** - * Get the maximum distance to render + * Gets the maximum distance to render * * @return frustum far */ @@ -278,7 +278,7 @@ public float getFrustumFar() { } /** - * Set the asset manager used to load the shaders needed for the baking + * Sets the asset manager used to load the shaders needed for the baking * * @param assetManager the asset manager */ From aae61d38312e9b47469d5c489f48d996ce14dc6b Mon Sep 17 00:00:00 2001 From: SceneMax3D Date: Mon, 22 Jan 2024 18:23:34 +0200 Subject: [PATCH 26/29] JavaDoc fixes --- .../java/com/jme3/environment/baker/EnvBaker.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java index b1b74e0ec4..65ee9805f1 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/EnvBaker.java @@ -45,7 +45,7 @@ */ public interface EnvBaker { /** - * Bake the environment + * Bakes the environment. * * @param scene * The scene to bake @@ -61,29 +61,29 @@ public interface EnvBaker { public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar, Predicate filter); /** - * Get the environment map + * Gets the environment map. * * @return The environment map */ public TextureCubeMap getEnvMap(); /** - * Clean the environment baker This method should be called when the baker - * is no longer needed It will clean up all the resources + * Cleans the environment baker This method should be called when the baker + * is no longer needed It will clean up all the resources. */ public void clean(); /** - * Set if textures should be pulled from the GPU + * Specifies whether textures should be pulled from the GPU. * * @param v */ public void setTexturePulling(boolean v); /** - * Get if textures should be pulled from the GPU + * Gets if textures should be pulled from the GPU. * * @return */ public boolean isTexturePulling(); -} \ No newline at end of file +} From 8663388fb4f9da76c38190d7d64182610b2d6153 Mon Sep 17 00:00:00 2001 From: SceneMax3D Date: Mon, 22 Jan 2024 18:30:39 +0200 Subject: [PATCH 27/29] JavaDoc fixes --- .../jme3/environment/baker/GenericEnvBaker.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java index f12c30aabc..6831914945 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java @@ -136,8 +136,8 @@ public TextureCubeMap getEnvMap() { } /** - * Update the internal camera to face the given cubemap face - * and return it + * Updates the internal camera to face the given cubemap face + * and return it. * * @param faceId * the id of the face (0-5) @@ -218,7 +218,7 @@ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, } /** - * Starts pulling the data from the framebuffer into the texture + * Starts pulling the data from the framebuffer into the texture. */ protected void startPulling() { bos.clear(); @@ -226,7 +226,7 @@ protected void startPulling() { /** * Pulls the data from the framebuffer into the texture Nb. mipmaps must be - * pulled sequentially on the same faceId + * pulled sequentially on the same faceId. * * @param fb * the framebuffer to pull from @@ -250,7 +250,9 @@ protected ByteBuffer pull(FrameBuffer fb, Texture env, int faceId) { } ByteArrayOutputStream bo = bos.get(faceId); - if (bo == null) bos.set(faceId, bo = new ByteArrayOutputStream()); + if (bo == null) { + bos.set(faceId, bo = new ByteArrayOutputStream()); + } try { byte array[] = new byte[face.limit()]; face.get(array); @@ -288,4 +290,4 @@ protected int limitMips(int nbMipMaps, int baseW, int baseH, RenderManager rm) { return nbMipMaps; } -} \ No newline at end of file +} From d4ff52c04bf74992684ba6938669890fc31f496e Mon Sep 17 00:00:00 2001 From: SceneMax3D Date: Mon, 22 Jan 2024 18:35:04 +0200 Subject: [PATCH 28/29] JavaDocs Fixes --- .../com/jme3/environment/baker/IBLEnvBaker.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java index 84d5bcd35c..982ccc79df 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLEnvBaker.java @@ -36,39 +36,39 @@ import com.jme3.texture.TextureCubeMap; /** - * An environment baker, but this one is for Imaged Base Lighting + * An environment baker, but this one is for Imaged Base Lighting. * * @author Riccardo Balbo */ public interface IBLEnvBaker extends EnvBaker { /** - * Generate the BRDF texture + * Generates the BRDF texture. * * @return The BRDF texture */ public Texture2D genBRTF(); /** - * Bake the irradiance map + * Bakes the irradiance map. */ public void bakeIrradiance(); /** - * Bake the specular IBL map + * Bakes the specular IBL map. */ public void bakeSpecularIBL(); /** - * Get the specular IBL map + * Gets the specular IBL map. * * @return The specular IBL map */ public TextureCubeMap getSpecularIBL(); /** - * Get the irradiance map + * Gets the irradiance map. * * @return The irradiance map */ public TextureCubeMap getIrradiance(); -} \ No newline at end of file +} From 7a77c148dba89b3098f7caaeceb165ebcb27ced9 Mon Sep 17 00:00:00 2001 From: SceneMax3D Date: Mon, 22 Jan 2024 18:56:31 +0200 Subject: [PATCH 29/29] JavaDocs fixes --- .../java/com/jme3/environment/EnvironmentProbeControl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java index fadd35f4a7..d07a039995 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -260,7 +260,7 @@ public void setFrustumFar(float frustumFar) { } /** - * Get the minimum distance to render + * Gets the minimum distance to render. * * @return frustum near */ @@ -269,7 +269,7 @@ public float getFrustumNear() { } /** - * Gets the maximum distance to render + * Gets the maximum distance to render. * * @return frustum far */ @@ -278,7 +278,7 @@ public float getFrustumFar() { } /** - * Sets the asset manager used to load the shaders needed for the baking + * Sets the asset manager used to load the shaders needed for the baking. * * @param assetManager the asset manager */