Просмотр исходного кода

Merge pull request #2502 from capdevon/capdevon-FilterPostProcessor

Refactor: FilterPostProcessor javadoc
codex 2 дней назад
Родитель
Сommit
d9639865c4
1 измененных файлов с 248 добавлено и 118 удалено
  1. 248 118
      jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java

+ 248 - 118
jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2022 jMonkeyEngine
+ * Copyright (c) 2009-2025 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -32,18 +32,28 @@
 package com.jme3.post;
 
 import com.jme3.asset.AssetManager;
-import com.jme3.export.*;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
 import com.jme3.material.Material;
-import com.jme3.profile.*;
-import com.jme3.renderer.*;
+import com.jme3.profile.AppProfiler;
+import com.jme3.profile.SpStep;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
 import com.jme3.renderer.queue.RenderQueue;
 import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.FrameBuffer.FrameBufferTarget;
 import com.jme3.texture.Image.Format;
 import com.jme3.texture.Texture;
 import com.jme3.texture.Texture2D;
-import com.jme3.texture.FrameBuffer.FrameBufferTarget;
 import com.jme3.ui.Picture;
 import com.jme3.util.SafeArrayList;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -52,13 +62,22 @@ import java.util.Iterator;
 import java.util.List;
 
 /**
- * A FilterPostProcessor is a processor that can apply several {@link Filter}s to a rendered scene<br>
- * It manages a list of filters that will be applied in the order in which they've been added to the list
- * @author Rémy Bouquet aka Nehon
+ * A `FilterPostProcessor` is a {@link SceneProcessor} that can apply several
+ * {@link Filter}s to a rendered scene. It manages a list of filters that will be
+ * applied in the order in which they have been added. This processor handles
+ * rendering the main scene to an offscreen framebuffer, then applying each enabled
+ * filter sequentially, optionally with anti-aliasing (multisampling) and depth texture
+ * support.
+ *
+ * @author Nehon
  */
 public class FilterPostProcessor implements SceneProcessor, Savable {
 
+    /**
+     * The simple name of this class, used for profiling.
+     */
     public static final String FPP = FilterPostProcessor.class.getSimpleName();
+
     private RenderManager renderManager;
     private Renderer renderer;
     private ViewPort viewPort;
@@ -89,23 +108,28 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
     private Format depthFormat = Format.Depth;
 
     /**
-     * Create a FilterProcessor
-     * @param assetManager the assetManager
+     * Creates a new `FilterPostProcessor`.
+     *
+     * @param assetManager The asset manager to be used by filters for loading assets.
      */
     public FilterPostProcessor(AssetManager assetManager) {
         this.assetManager = assetManager;
     }
 
     /**
-     * Don't use this constructor, use {@link #FilterPostProcessor(AssetManager assetManager)}<br>
-     * This constructor is used for serialization only
+     * Serialization-only constructor. Do not use this constructor directly;
+     * use {@link #FilterPostProcessor(AssetManager)}.
      */
     protected FilterPostProcessor() {
     }
 
     /**
-     * Adds a filter to the filters list<br>
-     * @param filter the filter to add
+     * Adds a filter to the list of filters to be applied. Filters are applied
+     * in the order they are added. If the processor is already initialized,
+     * the filter is immediately initialized as well.
+     *
+     * @param filter The filter to add (not null).
+     * @throws IllegalArgumentException If the provided filter is null.
      */
     public void addFilter(Filter filter) {
         if (filter == null) {
@@ -118,13 +142,14 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
         }
 
         setFilterState(filter, filter.isEnabled());
-
     }
 
     /**
-     * removes this filters from the filters list
+     * Removes a specific filter from the list. The filter's `cleanup` method
+     * is called upon removal.
      *
-     * @param filter the Filter to remove (not null)
+     * @param filter The filter to remove (not null).
+     * @throws IllegalArgumentException If the provided filter is null.
      */
     public void removeFilter(Filter filter) {
         if (filter == null) {
@@ -135,10 +160,22 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
         updateLastFilterIndex();
     }
 
+    /**
+     * Returns an iterator over the filters currently managed by this processor.
+     *
+     * @return An `Iterator` of {@link Filter} objects.
+     */
     public Iterator<Filter> getFilterIterator() {
         return filters.iterator();
     }
 
+    /**
+     * Initializes the `FilterPostProcessor`. This method is called by the
+     * `RenderManager` when the processor is added to a viewport.
+     *
+     * @param rm The `RenderManager` instance.
+     * @param vp The `ViewPort` this processor is attached to.
+     */
     @Override
     public void initialize(RenderManager rm, ViewPort vp) {
         renderManager = rm;
@@ -148,10 +185,11 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
         fsQuad.setWidth(1);
         fsQuad.setHeight(1);
 
+        // Determine optimal framebuffer format based on renderer capabilities
         if (!renderer.getCaps().contains(Caps.PackedFloatTexture)) {
-            if(renderer.getCaps().contains(Caps.FloatColorBufferRGB)){
+            if (renderer.getCaps().contains(Caps.FloatColorBufferRGB)) {
                 fbFormat = Format.RGB16F;
-            } else if(renderer.getCaps().contains(Caps.FloatColorBufferRGBA)){
+            } else if (renderer.getCaps().contains(Caps.FloatColorBufferRGBA)) {
                 fbFormat = Format.RGBA16F;
             } else {
                 fbFormat = Format.RGB8;
@@ -160,34 +198,47 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
 
         Camera cam = vp.getCamera();
 
-        //save view port dimensions
+        // Save original viewport dimensions
         left = cam.getViewPortLeft();
         right = cam.getViewPortRight();
         top = cam.getViewPortTop();
         bottom = cam.getViewPortBottom();
         originalWidth = cam.getWidth();
         originalHeight = cam.getHeight();
-        //first call to reshape
+
+        // First call to reshape to set up internal framebuffers and textures
         reshape(vp, cam.getWidth(), cam.getHeight());
     }
 
+    /**
+     * Returns the default color buffer format used for the internal rendering
+     * passes of the filters. This format is determined during initialization
+     * based on the renderer's capabilities.
+     *
+     * @return The default `Format` for the filter pass textures.
+     */
     public Format getDefaultPassTextureFormat() {
         return fbFormat;
     }
 
     /**
-     * init the given filter
-     * @param filter
-     * @param vp
+     * Initializes a single filter. This method is called when a filter is added
+     * or when the post-processor is initialized/reshaped. It sets the processor
+     * for the filter, handles depth texture requirements, and calls the filter's
+     * `init` method.
+     *
+     * @param filter The {@link Filter} to initialize.
+     * @param vp The `ViewPort` associated with this processor.
      */
     private void initFilter(Filter filter, ViewPort vp) {
         filter.setProcessor(this);
         if (filter.isRequiresDepthTexture()) {
             if (!computeDepth && renderFrameBuffer != null) {
+                // If depth texture is required and not yet created, create it
                 depthTexture = new Texture2D(width, height, depthFormat);
                 renderFrameBuffer.setDepthTarget(FrameBufferTarget.newTarget(depthTexture));
             }
-            computeDepth = true;
+            computeDepth = true; // Mark that depth texture is needed
             filter.init(assetManager, renderManager, vp, width, height);
             filter.setDepthTexture(depthTexture);
         } else {
@@ -196,45 +247,52 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
     }
 
     /**
-     * renders a filter on a fullscreen quad
-     * @param r
-     * @param buff
-     * @param mat
+     * Renders a filter's material onto a full-screen quad. This method
+     * handles setting up the rendering context (framebuffer, camera, material)
+     * for a filter pass. It correctly resizes the camera and adjusts material
+     * states based on whether the target buffer is the final output buffer or an
+     * intermediate filter buffer.
+     *
+     * @param r The `Renderer` instance.
+     * @param buff The `FrameBuffer` to render to.
+     * @param mat The `Material` to use for rendering the filter.
      */
     private void renderProcessing(Renderer r, FrameBuffer buff, Material mat) {
+        // Adjust camera and viewport based on target framebuffer
         if (buff == outputBuffer) {
             viewPort.getCamera().resize(originalWidth, originalHeight, false);
             viewPort.getCamera().setViewPort(left, right, bottom, top);
-            // update is redundant because resize and setViewPort will both
-            // run the appropriate (and same) onXXXChange methods.
-            // Also, update() updates some things that don't need to be updated.
-            //viewPort.getCamera().update();
-            renderManager.setCamera( viewPort.getCamera(), false);
+            // viewPort.getCamera().update(); // Redundant as resize and setViewPort call onXXXChange
+            renderManager.setCamera(viewPort.getCamera(), false);
+            // Disable depth test/write for final pass to prevent artifacts
             if (mat.getAdditionalRenderState().isDepthWrite()) {
                 mat.getAdditionalRenderState().setDepthTest(false);
                 mat.getAdditionalRenderState().setDepthWrite(false);
             }
         } else {
+            // Rendering to an intermediate framebuffer for a filter pass
             viewPort.getCamera().resize(buff.getWidth(), buff.getHeight(), false);
             viewPort.getCamera().setViewPort(0, 1, 0, 1);
-            // update is redundant because resize and setViewPort will both
-            // run the appropriate (and same) onXXXChange methods.
-            // Also, update() updates some things that don't need to be updated.
-            //viewPort.getCamera().update();
-            renderManager.setCamera( viewPort.getCamera(), false);
+            // viewPort.getCamera().update(); // Redundant as resize and setViewPort call onXXXChange
+            renderManager.setCamera(viewPort.getCamera(), false);
+            // Enable depth test/write for intermediate passes if material needs it
             mat.getAdditionalRenderState().setDepthTest(true);
             mat.getAdditionalRenderState().setDepthWrite(true);
         }
 
-
         fsQuad.setMaterial(mat);
         fsQuad.updateGeometricState();
 
         r.setFrameBuffer(buff);
-        r.clearBuffers(true, true, true);
+        r.clearBuffers(true, true, true); // Clear color, depth, and stencil buffers
         renderManager.renderGeometry(fsQuad);
     }
 
+    /**
+     * Checks if the `FilterPostProcessor` has been initialized.
+     *
+     * @return True if initialized, false otherwise.
+     */
     @Override
     public boolean isInitialized() {
         return viewPort != null;
@@ -244,30 +302,44 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
     public void postQueue(RenderQueue rq) {
         for (Filter filter : filters.getArray()) {
             if (filter.isEnabled()) {
-                if (prof != null) prof.spStep(SpStep.ProcPostQueue, FPP, filter.getName());
+                if (prof != null) {
+                    prof.spStep(SpStep.ProcPostQueue, FPP, filter.getName());
+                }
                 filter.postQueue(rq);
             }
         }
     }
 
     /**
-     * iterate through the filter list and renders filters
-     * @param r
-     * @param sceneFb
+     * Renders the chain of filters. This method is the core of the post-processing.
+     * It iterates through each enabled filter, handling pre-filter passes,
+     * setting up textures (scene, depth), performing the main filter rendering,
+     * and managing intermediate framebuffers.
+     *
+     * @param r The `Renderer` instance.
+     * @param sceneFb The framebuffer containing the rendered scene (either MS or single-sample).
      */
     private void renderFilterChain(Renderer r, FrameBuffer sceneFb) {
         Texture2D tex = filterTexture;
         FrameBuffer buff = sceneFb;
         boolean msDepth = depthTexture != null && depthTexture.getImage().getMultiSamples() > 1;
+
         for (int i = 0; i < filters.size(); i++) {
             Filter filter = filters.get(i);
-            if (prof != null) prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName());
+            if (prof != null) {
+                prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName());
+            }
+
             if (filter.isEnabled()) {
+                // Handle additional passes a filter might have (e.g., blur passes)
                 if (filter.getPostRenderPasses() != null) {
-                    for (Iterator<Filter.Pass> it1 = filter.getPostRenderPasses().iterator(); it1.hasNext();) {
-                        Filter.Pass pass = it1.next();
-                        if (prof != null) prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName(), pass.toString());
+                    for (Filter.Pass pass : filter.getPostRenderPasses()) {
+                        if (prof != null) {
+                            prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName(), pass.toString());
+                        }
                         pass.beforeRender();
+
+                        // Set scene texture if required by the pass
                         if (pass.requiresSceneAsTexture()) {
                             pass.getPassMaterial().setTexture("Texture", tex);
                             if (tex.getImage().getMultiSamples() > 1) {
@@ -277,6 +349,8 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
 
                             }
                         }
+
+                        // Set depth texture if required by the pass
                         if (pass.requiresDepthAsTexture()) {
                             pass.getPassMaterial().setTexture("DepthTexture", depthTexture);
                             if (msDepth) {
@@ -288,7 +362,9 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
                         renderProcessing(r, pass.getRenderFrameBuffer(), pass.getPassMaterial());
                     }
                 }
-                if (prof != null) prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName(), "postFrame");
+                if (prof != null) {
+                    prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName(), "postFrame");
+                }
                 filter.postFrame(renderManager, viewPort, buff, sceneFb);
 
                 Material mat = filter.getMaterial();
@@ -305,23 +381,31 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
                     }
                 }
 
+                // Apply bilinear filtering if requested by the filter
                 boolean wantsBilinear = filter.isRequiresBilinear();
                 if (wantsBilinear) {
                     tex.setMagFilter(Texture.MagFilter.Bilinear);
                     tex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
                 }
 
+                // Determine target framebuffer and source texture for the next pass
                 buff = outputBuffer;
                 if (i != lastFilterIndex) {
                     buff = filter.getRenderFrameBuffer();
                     tex = filter.getRenderedTexture();
-
                 }
-                if (prof != null) prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName(), "render");
+                if (prof != null) {
+                    prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName(), "render");
+                }
+                // Render the main filter pass
                 renderProcessing(r, buff, mat);
-                if (prof != null) prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName(), "postFilter");
+                if (prof != null) {
+                    prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName(), "postFilter");
+                }
+                // Call filter's postFilter for final adjustments
                 filter.postFilter(r, buff);
 
+                // Revert texture filtering if it was changed
                 if (wantsBilinear) {
                     tex.setMagFilter(Texture.MagFilter.Nearest);
                     tex.setMinFilter(Texture.MinFilter.NearestNoMipMaps);
@@ -339,10 +423,14 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
         } else if (renderFrameBufferMS != null) {
             sceneBuffer = renderFrameBufferMS;
         }
+
+        // Execute the filter chain
         renderFilterChain(renderer, sceneBuffer);
+
+        // Restore the original output framebuffer for the viewport
         renderer.setFrameBuffer(outputBuffer);
 
-        //viewport can be null if no filters are enabled
+        // viewport can be null if no filters are enabled
         if (viewPort != null) {
             renderManager.setCamera(viewPort.getCamera(), false);
         }
@@ -351,40 +439,44 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
     @Override
     public void preFrame(float tpf) {
         if (filters.isEmpty() || lastFilterIndex == -1) {
-            //If the camera is initialized and there are no filter to render, the camera viewport is restored as it was
+            // If no filters are enabled, restore the camera's original viewport
+            // and output framebuffer to bypass the post-processor.
             if (cameraInit) {
                 viewPort.getCamera().resize(originalWidth, originalHeight, true);
                 viewPort.getCamera().setViewPort(left, right, bottom, top);
                 viewPort.setOutputFrameBuffer(outputBuffer);
                 cameraInit = false;
             }
-
         } else {
-           setupViewPortFrameBuffer();
-           //if we are in a multiview situation we need to resize the camera
-           //to the viewport size so that the back buffer is rendered correctly
-           if (multiView) {
+            setupViewPortFrameBuffer();
+            // If in a multi-view situation, resize the camera to the viewport size
+            // so that the back buffer is rendered correctly for filtering.
+            if (multiView) {
                 viewPort.getCamera().resize(width, height, false);
                 viewPort.getCamera().setViewPort(0, 1, 0, 1);
                 viewPort.getCamera().update();
                 renderManager.setCamera(viewPort.getCamera(), false);
-           }
+            }
         }
 
+        // Call preFrame on all enabled filters
         for (Filter filter : filters.getArray()) {
             if (filter.isEnabled()) {
-                if (prof != null) prof.spStep(SpStep.ProcPreFrame, FPP, filter.getName());
+                if (prof != null) {
+                    prof.spStep(SpStep.ProcPreFrame, FPP, filter.getName());
+                }
                 filter.preFrame(tpf);
             }
         }
-
     }
 
     /**
-     * sets the filter to enabled or disabled
+     * Sets the enabled state of a specific filter. If the filter is part of
+     * this processor's list, its `enabled` flag is updated, and the
+     * `lastFilterIndex` is recomputed.
      *
-     * @param filter the Filter to modify (not null)
-     * @param enabled true to enable, false to disable
+     * @param filter The {@link Filter} to modify (not null).
+     * @param enabled True to enable the filter, false to disable it.
      */
     protected void setFilterState(Filter filter, boolean enabled) {
         if (filters.contains(filter)) {
@@ -394,26 +486,27 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
     }
 
     /**
-     * compute the index of the last filter to render
+     * Computes the index of the last enabled filter in the list. This is used
+     * to determine which filter should render to the final output framebuffer
+     * and which should render to intermediate framebuffers. If no filters are
+     * enabled, the viewport's output framebuffer is restored to its original.
      */
     private void updateLastFilterIndex() {
         lastFilterIndex = -1;
         for (int i = filters.size() - 1; i >= 0 && lastFilterIndex == -1; i--) {
             if (filters.get(i).isEnabled()) {
                 lastFilterIndex = i;
-                // The FPP is initialized, but the viewport framebuffer is the
-                // original out framebuffer, so we must recover from a situation
-                // where no filter was enabled. So we set the correct framebuffer
-                // on the viewport.
-                if(isInitialized() && viewPort.getOutputFrameBuffer()==outputBuffer){
+                // If the FPP is initialized but the viewport framebuffer is the
+                // original output framebuffer (meaning no filter was enabled
+                // previously), then redirect it to the FPP's internal framebuffer.
+                if (isInitialized() && viewPort.getOutputFrameBuffer() == outputBuffer) {
                     setupViewPortFrameBuffer();
                 }
                 return;
             }
         }
+        // If no filters are enabled, restore the original framebuffer to the viewport.
         if (isInitialized() && lastFilterIndex == -1) {
-            //There is no enabled filter, we restore the original framebuffer
-            //to the viewport to bypass the fpp.
             viewPort.setOutputFrameBuffer(outputBuffer);
         }
     }
@@ -421,40 +514,56 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
     @Override
     public void cleanup() {
         if (viewPort != null) {
-            //reset the viewport camera viewport to its initial value
+            // Reset the viewport camera and output framebuffer to their initial values
             viewPort.getCamera().resize(originalWidth, originalHeight, true);
             viewPort.getCamera().setViewPort(left, right, bottom, top);
             viewPort.setOutputFrameBuffer(outputBuffer);
             viewPort = null;
 
-            if(renderFrameBuffer != null){
+            // Dispose of internal framebuffers and textures
+            if (renderFrameBuffer != null) {
                 renderFrameBuffer.dispose();
             }
-            if(depthTexture!=null){
-               depthTexture.getImage().dispose();
+            if (depthTexture != null) {
+                depthTexture.getImage().dispose();
             }
             filterTexture.getImage().dispose();
-            if(renderFrameBufferMS != null){
-               renderFrameBufferMS.dispose();
+            if (renderFrameBufferMS != null) {
+                renderFrameBufferMS.dispose();
             }
             for (Filter filter : filters.getArray()) {
                 filter.cleanup(renderer);
             }
         }
-
     }
 
+    /**
+     * Sets the profiler instance for this processor.
+     *
+     * @param profiler The `AppProfiler` instance to use for performance monitoring.
+     */
     @Override
     public void setProfiler(AppProfiler profiler) {
         this.prof = profiler;
     }
 
+    /**
+     * Reshapes the `FilterPostProcessor` when the viewport or canvas size changes.
+     * This method recalculates internal framebuffer dimensions, creates new
+     * framebuffers and textures if necessary (e.g., for anti-aliasing), and
+     * reinitializes all filters with the new dimensions. It also detects
+     * multi-view scenarios.
+     *
+     * @param vp The `ViewPort` being reshaped.
+     * @param w The new width of the viewport's canvas.
+     * @param h The new height of the viewport's canvas.
+     */
     @Override
     public void reshape(ViewPort vp, int w, int h) {
         Camera cam = vp.getCamera();
-        //this has no effect at first init but is useful when resizing the canvas with multi views
+        // This sets the camera viewport to its full extent (0-1) for rendering to the FPP's internal buffer.
         cam.setViewPort(left, right, bottom, top);
-        //resizing the camera to fit the new viewport and saving original dimensions
+        // Resizing the camera to fit the new viewport and saving original dimensions
         cam.resize(w, h, true);
         left = cam.getViewPortLeft();
         right = cam.getViewPortRight();
@@ -463,16 +572,16 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
         originalWidth = w;
         originalHeight = h;
 
-        //computing real dimension of the viewport and resizing the camera
+        // Computing real dimension of the viewport based on its relative size within the canvas
         width = (int) (w * (Math.abs(right - left)));
         height = (int) (h * (Math.abs(bottom - top)));
         width = Math.max(1, width);
         height = Math.max(1, height);
 
-        //Testing original versus actual viewport dimension.
-        //If they are different we are in a multiview situation and
-        //camera must be handled differently
-        if(originalWidth!=width || originalHeight!=height){
+        // Test if original dimensions differ from actual viewport dimensions.
+        // If they are different, we are in a multiview situation, and the
+        // camera must be handled differently (e.g., resized to the sub-viewport).
+        if (originalWidth != width || originalHeight != height) {
             multiView = true;
         }
 
@@ -485,9 +594,11 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
 
         Collection<Caps> caps = renderer.getCaps();
 
-        //antialiasing on filters only supported in opengl 3 due to depth read problem
+        // antialiasing on filters only supported in opengl 3 due to depth read problem
         if (numSamples > 1 && caps.contains(Caps.FrameBufferMultisample)) {
             renderFrameBufferMS = new FrameBuffer(width, height, numSamples);
+
+            // If OpenGL 3.2+ is supported, multisampled textures can be attached directly
             if (caps.contains(Caps.OpenGL32)) {
                 Texture2D msColor = new Texture2D(width, height, numSamples, fbFormat);
                 Texture2D msDepth = new Texture2D(width, height, numSamples, depthFormat);
@@ -496,11 +607,14 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
                 filterTexture = msColor;
                 depthTexture = msDepth;
             } else {
+                // Otherwise, multisampled framebuffer must use internal texture, which cannot be directly read
                 renderFrameBufferMS.setDepthTarget(FrameBufferTarget.newTarget(depthFormat));
                 renderFrameBufferMS.addColorTarget(FrameBufferTarget.newTarget(fbFormat));
             }
         }
 
+        // Setup single-sampled framebuffer if no multisampling, or if OpenGL 3.2+ is not supported
+        // (because for non-GL32, a single-sampled buffer is still needed to copy MS content into).
         if (numSamples <= 1 || !caps.contains(Caps.OpenGL32) || !caps.contains(Caps.FrameBufferMultisample)) {
             renderFrameBuffer = new FrameBuffer(width, height, 1);
             renderFrameBuffer.setDepthTarget(FrameBufferTarget.newTarget(depthFormat));
@@ -508,6 +622,7 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
             renderFrameBuffer.addColorTarget(FrameBufferTarget.newTarget(filterTexture));
         }
 
+        // Set names for debugging
         if (renderFrameBufferMS != null) {
             renderFrameBufferMS.setName("FilterPostProcessor MS");
         }
@@ -516,6 +631,7 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
             renderFrameBuffer.setName("FilterPostProcessor");
         }
 
+        // Initialize all existing filters with the new dimensions
         for (Filter filter : filters.getArray()) {
             initFilter(filter, vp);
         }
@@ -523,16 +639,16 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
     }
 
     /**
-     * return the number of samples for antialiasing
-     * @return numSamples
+     * Returns the number of samples used for anti-aliasing.
+     *
+     * @return The number of samples.
      */
     public int getNumSamples() {
         return numSamples;
     }
 
     /**
-     *
-     * Removes all the filters from this processor
+     * Removes all filters currently added to this processor.
      */
     public void removeAllFilters() {
         filters.clear();
@@ -540,8 +656,12 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
     }
 
     /**
-     * Sets the number of samples for antialiasing
-     * @param numSamples the number of Samples
+     * Sets the number of samples for anti-aliasing. A value of 1 means no
+     * anti-aliasing. This method should generally be called before the
+     * processor is initialized to have an effect.
+     *
+     * @param numSamples The number of samples. Must be greater than 0.
+     * @throws IllegalArgumentException If `numSamples` is less than or equal to 0.
      */
     public void setNumSamples(int numSamples) {
         if (numSamples <= 0) {
@@ -561,27 +681,30 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
     }
 
     /**
-     * Sets the format to be used for the internal frame buffer's color buffer
+     * Sets the preferred `Image.Format` to be used for the internal frame buffer's
+     * color buffer.
      *
-     * @param fbFormat the format
+     * @param fbFormat The desired `Format` for the color buffer.
      */
     public void setFrameBufferFormat(Format fbFormat) {
         this.fbFormat = fbFormat;
     }
 
     /**
-     * Sets the format to be used for the internal frame buffer's depth buffer
+     * Sets the preferred `Image.Format` to be used for the internal frame buffer's
+     * depth buffer.
      *
-     * @param depthFormat the format
+     * @param depthFormat The desired `Format` for the depth buffer.
      */
     public void setFrameBufferDepthFormat(Format depthFormat) {
         this.depthFormat = depthFormat;
     }
 
     /**
-     * Returns the depth format currently used for the internal frame buffer's depth buffer
-     * 
-     * @return the depth format
+     * Returns the `Image.Format` currently used for the internal frame buffer's
+     * depth buffer.
+     *
+     * @return The current depth `Format`.
      */
     public Format getFrameBufferDepthFormat() {
         return depthFormat;
@@ -609,43 +732,50 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
     }
 
     /**
-     * For internal use only<br>
-     * returns the depth texture of the scene
-     * @return the depth texture
+     * For internal use only.
+     * Returns the depth texture generated from the scene's depth buffer.
+     * This texture is available if any filter requires a depth texture.
+     *
+     * @return The `Texture2D` containing the scene's depth information, or null if not computed.
      */
     public Texture2D getDepthTexture() {
         return depthTexture;
     }
 
     /**
-     * For internal use only<br>
-     * returns the rendered texture of the scene
-     * @return the filter texture
+     * For internal use only.
+     * Returns the color texture that contains the rendered scene or the output
+     * of the previous filter in the chain. This texture serves as input for subsequent filters.
+     *
+     * @return The `Texture2D` containing the scene's color information or the intermediate filter output.
      */
     public Texture2D getFilterTexture() {
         return filterTexture;
     }
 
     /**
-     * returns the first filter in the list assignable from the given type
+     * Returns the first filter in the managed list that is assignable from the
+     * given filter type. Useful for retrieving specific filters to modify their properties.
      *
-     * @param <T> the filter type
-     * @param filterType the filter type
-     * @return a filter assignable from the given type
+     * @param <T> The type of the filter to retrieve.
+     * @param filterType The `Class` object representing the filter type.
+     * @return A filter instance assignable from `filterType`, or null if no such filter is found.
      */
     @SuppressWarnings("unchecked")
     public <T extends Filter> T getFilter(Class<T> filterType) {
-        for (Filter c : filters.getArray()) {
-            if (filterType.isAssignableFrom(c.getClass())) {
-                return (T) c;
+        for (Filter f : filters.getArray()) {
+            if (filterType.isAssignableFrom(f.getClass())) {
+                return (T) f;
             }
         }
         return null;
     }
 
     /**
-     * returns an unmodifiable version of the filter list.
-     * @return the filters list
+     * Returns an unmodifiable version of the list of filters currently
+     * managed by this processor.
+     *
+     * @return An unmodifiable `List` of {@link Filter} objects.
      */
     public List<Filter> getFilterList(){
         return Collections.unmodifiableList(filters);
@@ -658,4 +788,4 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
             viewPort.setOutputFrameBuffer(renderFrameBuffer);
         }
     }
-    }
+}