4444import com .jme3 .renderer .RenderManager ;
4545import com .jme3 .scene .Geometry ;
4646import com .jme3 .scene .shape .Box ;
47+ import com .jme3 .system .JmeSystem ;
48+ import com .jme3 .system .Platform ;
4749import com .jme3 .texture .FrameBuffer ;
4850import com .jme3 .texture .Image ;
4951import com .jme3 .texture .Texture2D ;
6365 */
6466public class IBLGLEnvBakerLight extends IBLHybridEnvBakerLight {
6567 private static final int NUM_SH_COEFFICIENT = 9 ;
68+ private static final int DEFAULT_FAST_SH_SAMPLE_COUNT = 8192 ;
6669 private static final Logger LOG = Logger .getLogger (IBLGLEnvBakerLight .class .getName ());
6770
71+ /**
72+ * Selects how spherical harmonics coefficients are baked on the GPU.
73+ */
74+ public enum SphericalHarmonicsMode {
75+ /**
76+ * Use full cubemap integration on desktop and the Hammersley fast path on
77+ * Android and iOS.
78+ */
79+ AUTO ,
80+ /**
81+ * Use Hammersley sampling.
82+ */
83+ FAST ,
84+ /**
85+ * Integrate every cubemap texel in a single shader pass.
86+ */
87+ QUALITY
88+ }
89+
90+ private SphericalHarmonicsMode sphericalHarmonicsMode = SphericalHarmonicsMode .AUTO ;
91+ private int sphericalHarmonicsFastPathSampleCount = DEFAULT_FAST_SH_SAMPLE_COUNT ;
92+
6893 /**
6994 * Create a new IBL env baker
7095 *
@@ -91,6 +116,49 @@ public boolean isTexturePulling() {
91116 return this .texturePulling ;
92117 }
93118
119+ /**
120+ * Sets how spherical harmonics coefficients are baked.
121+ *
122+ * @param mode the spherical harmonics bake mode
123+ */
124+ public void setSphericalHarmonicsMode (SphericalHarmonicsMode mode ) {
125+ if (mode == null ) {
126+ throw new IllegalArgumentException ("mode cannot be null" );
127+ }
128+ this .sphericalHarmonicsMode = mode ;
129+ }
130+
131+ /**
132+ * Returns the current spherical harmonics bake mode.
133+ *
134+ * @return the spherical harmonics bake mode
135+ */
136+ public SphericalHarmonicsMode getSphericalHarmonicsMode () {
137+ return sphericalHarmonicsMode ;
138+ }
139+
140+ /**
141+ * Sets the sample count used by the Hammersley spherical harmonics fast path.
142+ *
143+ * @param sampleCount the number of samples, must be positive
144+ */
145+ public void setSphericalHarmonicsFastPathSampleCount (int sampleCount ) {
146+ if (sampleCount <= 0 ) {
147+ throw new IllegalArgumentException ("sampleCount must be greater than zero" );
148+ }
149+ this .sphericalHarmonicsFastPathSampleCount = sampleCount ;
150+ }
151+
152+ /**
153+ * Returns the sample count used by the Hammersley spherical harmonics fast path.
154+ *
155+ * @return the Hammersley sample count
156+ */
157+ public int getSphericalHarmonicsFastPathSampleCount () {
158+ return sphericalHarmonicsFastPathSampleCount ;
159+ }
160+
161+
94162 @ Override
95163 public void bakeSphericalHarmonicsCoefficients () {
96164 Box boxm = new Box (1 , 1 , 1 );
@@ -99,8 +167,25 @@ public void bakeSphericalHarmonicsCoefficients() {
99167 Material mat = new Material (assetManager , "Common/IBLSphH/IBLSphH.j3md" );
100168 mat .setTexture ("Texture" , envMap );
101169 mat .setVector2 ("Resolution" , new Vector2f (envMap .getImage ().getWidth (), envMap .getImage ().getHeight ()));
170+ mat .setInt ("SampleCount" , sphericalHarmonicsFastPathSampleCount );
102171 screen .setMaterial (mat );
103172
173+ switch (sphericalHarmonicsMode ) {
174+ case FAST : {
175+ mat .setBoolean ("UseFastSphericalHarmonics" , true );
176+ break ;
177+ }
178+ case QUALITY : {
179+ mat .setBoolean ("UseFastSphericalHarmonics" , false );
180+ break ;
181+ }
182+ case AUTO : {
183+ Platform .Os os = JmeSystem .getPlatform ().getOs ();
184+ mat .setBoolean ("UseFastSphericalHarmonics" , os == Platform .Os .Android || os == Platform .Os .iOS );
185+ break ;
186+ }
187+ }
188+
104189 float remapMaxValue = 0 ;
105190 Format format = Format .RGBA32F ;
106191 if (!renderManager .getRenderer ().getCaps ().contains (Caps .FloatColorBufferRGBA )) {
@@ -117,38 +202,21 @@ public void bakeSphericalHarmonicsCoefficients() {
117202 mat .clearParam ("RemapMaxValue" );
118203 }
119204
120- Texture2D shCoefTx [] = { new Texture2D (NUM_SH_COEFFICIENT , 1 , 1 , format ), new Texture2D (NUM_SH_COEFFICIENT , 1 , 1 , format ) };
121-
122- FrameBuffer shbaker [] = { new FrameBuffer (NUM_SH_COEFFICIENT , 1 , 1 ), new FrameBuffer (NUM_SH_COEFFICIENT , 1 , 1 ) };
123- shbaker [0 ].setSrgb (false );
124- shbaker [0 ].addColorTarget (FrameBufferTarget .newTarget (shCoefTx [0 ]));
125-
126- shbaker [1 ].setSrgb (false );
127- shbaker [1 ].addColorTarget (FrameBufferTarget .newTarget (shCoefTx [1 ]));
205+ Texture2D shCoefTx = new Texture2D (NUM_SH_COEFFICIENT , 1 , 1 , format );
128206
129- int renderOnT = -1 ;
207+ FrameBuffer shbaker = new FrameBuffer (NUM_SH_COEFFICIENT , 1 , 1 );
208+ shbaker .setSrgb (false );
209+ shbaker .addColorTarget (FrameBufferTarget .newTarget (shCoefTx ));
130210
131- for (int faceId = 0 ; faceId < 6 ; faceId ++) {
132- if (renderOnT != -1 ) {
133- int s = renderOnT ;
134- renderOnT = renderOnT == 0 ? 1 : 0 ;
135- mat .setTexture ("ShCoef" , shCoefTx [s ]);
136- } else {
137- renderOnT = 0 ;
138- }
139-
140- mat .setInt ("FaceId" , faceId );
211+ screen .updateLogicalState (0 );
212+ screen .updateGeometricState ();
141213
142- screen .updateLogicalState (0 );
143- screen .updateGeometricState ();
144-
145- renderManager .setCamera (updateAndGetInternalCamera (0 , shbaker [renderOnT ].getWidth (), shbaker [renderOnT ].getHeight (), Vector3f .ZERO , 1 , 1000 ), false );
146- renderManager .getRenderer ().setFrameBuffer (shbaker [renderOnT ]);
147- renderManager .renderGeometry (screen );
148- }
214+ renderManager .setCamera (updateAndGetInternalCamera (0 , shbaker .getWidth (), shbaker .getHeight (), Vector3f .ZERO , 1 , 1000 ), false );
215+ renderManager .getRenderer ().setFrameBuffer (shbaker );
216+ renderManager .renderGeometry (screen );
149217
150- ByteBuffer shCoefRaw = BufferUtils .createByteBuffer (NUM_SH_COEFFICIENT * 1 * (shbaker [ renderOnT ] .getColorTarget ().getFormat ().getBitsPerPixel () / 8 ));
151- renderManager .getRenderer ().readFrameBufferWithFormat (shbaker [ renderOnT ] , shCoefRaw , shbaker [ renderOnT ] .getColorTarget ().getFormat ());
218+ ByteBuffer shCoefRaw = BufferUtils .createByteBuffer (NUM_SH_COEFFICIENT * 1 * (shbaker .getColorTarget ().getFormat ().getBitsPerPixel () / 8 ));
219+ renderManager .getRenderer ().readFrameBufferWithFormat (shbaker , shCoefRaw , shbaker .getColorTarget ().getFormat ());
152220 shCoefRaw .rewind ();
153221
154222 Image img = new Image (format , NUM_SH_COEFFICIENT , 1 , shCoefRaw , ColorSpace .Linear );
@@ -176,6 +244,7 @@ else if (weightAccum != c.a) {
176244 }
177245 EnvMapUtils .prepareShCoefs (shCoef );
178246 img .dispose ();
247+ shbaker .dispose ();
179248
180249 }
181250}
0 commit comments