diff --git a/src/chunky-1/java/de/lemaik/chunky/denoiser/AlbedoTracer.java b/src/chunky-1/java/de/lemaik/chunky/denoiser/AlbedoTracer.java deleted file mode 100644 index 556008c..0000000 --- a/src/chunky-1/java/de/lemaik/chunky/denoiser/AlbedoTracer.java +++ /dev/null @@ -1,42 +0,0 @@ -package de.lemaik.chunky.denoiser; - -import se.llbit.chunky.block.Block; -import se.llbit.chunky.renderer.WorkerState; -import se.llbit.chunky.renderer.scene.PreviewRayTracer; -import se.llbit.chunky.renderer.scene.RayTracer; -import se.llbit.chunky.renderer.scene.Scene; -import se.llbit.math.Ray; - -public class AlbedoTracer implements RayTracer { - @Override - public void trace(Scene scene, WorkerState state) { - Ray ray = state.ray; - if (scene.isInWater(ray)) { - ray.setCurrentMaterial(Block.WATER, 0); - } else { - ray.setCurrentMaterial(Block.AIR, 0); - } - - while (true) { - if (!PreviewRayTracer.nextIntersection(scene, ray)) { - if (ray.getPrevMaterial().isWater()) { - // set water color to white - ray.color.set(1, 1, 1, 1); - } else if (ray.depth == 0) { - // direct sky hit - if (!scene.transparentSky()) { - scene.sky().getSkyColorInterpolated(ray); - } - } - // ignore indirect sky hits - break; - } - - if (!Block.AIR.isSameMaterial(ray.getCurrentMaterial()) && ray.color.w > 0.0D) { - break; - } - - ray.o.scaleAdd(1.0E-4D, ray.d); - } - } -} diff --git a/src/chunky-1/java/de/lemaik/chunky/denoiser/DenoiserTabImpl.java b/src/chunky-1/java/de/lemaik/chunky/denoiser/DenoiserTabImpl.java deleted file mode 100644 index 892ee5e..0000000 --- a/src/chunky-1/java/de/lemaik/chunky/denoiser/DenoiserTabImpl.java +++ /dev/null @@ -1,29 +0,0 @@ -package de.lemaik.chunky.denoiser; - -import javafx.fxml.FXMLLoader; -import javafx.scene.control.Tab; -import se.llbit.chunky.renderer.scene.Scene; -import se.llbit.chunky.ui.render.RenderControlsTab; - -import java.io.IOException; - -public class DenoiserTabImpl implements RenderControlsTab { - @Override - public void update(Scene scene) { - - } - - @Override - public Tab getTab() { - Tab tab = new Tab("Denoiser"); - - try { - FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/denoiser-tab.fxml")); - fxmlLoader.setController(new DenoiserTab()); - tab.setContent(fxmlLoader.load()); - return tab; - } catch (IOException e) { - throw new RuntimeException("Could not initialize denoiser plugin", e); - } - } -} diff --git a/src/chunky-1/resources/plugin.json b/src/chunky-1/resources/plugin.json deleted file mode 100644 index 75765fc..0000000 --- a/src/chunky-1/resources/plugin.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "DenoiserPlugin", - "author": "leMaik", - "main": "de.lemaik.chunky.denoiser.DenoiserPlugin", - "version": "0.3.2", - "targetVersion": "1.4.5", - "description": "Renders normal and albedo maps to pfm files for use with de-noisers." -} diff --git a/src/chunky-2/java/de/lemaik/chunky/denoiser/AlbedoTracer.java b/src/chunky-2/java/de/lemaik/chunky/denoiser/AlbedoTracer.java deleted file mode 100644 index 1fc8c58..0000000 --- a/src/chunky-2/java/de/lemaik/chunky/denoiser/AlbedoTracer.java +++ /dev/null @@ -1,43 +0,0 @@ -package de.lemaik.chunky.denoiser; - -import se.llbit.chunky.block.Air; -import se.llbit.chunky.block.Water; -import se.llbit.chunky.renderer.WorkerState; -import se.llbit.chunky.renderer.scene.PreviewRayTracer; -import se.llbit.chunky.renderer.scene.RayTracer; -import se.llbit.chunky.renderer.scene.Scene; -import se.llbit.math.Ray; - -public class AlbedoTracer implements RayTracer { - @Override - public void trace(Scene scene, WorkerState state) { - Ray ray = state.ray; - if (scene.isInWater(ray)) { - ray.setCurrentMaterial(Water.INSTANCE, 0); - } else { - ray.setCurrentMaterial(Air.INSTANCE, 0); - } - - while (true) { - if (!PreviewRayTracer.nextIntersection(scene, ray)) { - if (ray.getPrevMaterial().isWater()) { - // set water color to white - ray.color.set(1, 1, 1, 1); - } else if (ray.depth == 0) { - // direct sky hit - if (!scene.transparentSky()) { - scene.sky().getSkyColorInterpolated(ray); - } - } - // ignore indirect sky hits - break; - } - - if (ray.getCurrentMaterial() != Air.INSTANCE && ray.color.w > 0.0D) { - break; - } - - ray.o.scaleAdd(1.0E-4D, ray.d); - } - } -} diff --git a/src/main/java/de/lemaik/chunky/denoiser/AlbedoRenderer.java b/src/main/java/de/lemaik/chunky/denoiser/AlbedoRenderer.java new file mode 100644 index 0000000..6e3214a --- /dev/null +++ b/src/main/java/de/lemaik/chunky/denoiser/AlbedoRenderer.java @@ -0,0 +1,55 @@ +package de.lemaik.chunky.denoiser; + +import se.llbit.chunky.block.Air; +import se.llbit.chunky.block.Water; +import se.llbit.chunky.renderer.PathTracingRenderer; +import se.llbit.chunky.renderer.WorkerState; +import se.llbit.chunky.renderer.scene.PreviewRayTracer; +import se.llbit.chunky.renderer.scene.RayTracer; +import se.llbit.chunky.renderer.scene.Scene; +import se.llbit.math.Ray; + +public class AlbedoRenderer extends PathTracingRenderer { + + public static final String ID = "ALBEDO"; + + public AlbedoRenderer() { + super(ID, "Albedo map", "Renderer for albedo maps (used for denoising)", + new AlbedoTracer()); + } + + private static class AlbedoTracer implements RayTracer { + + @Override + public void trace(Scene scene, WorkerState state) { + Ray ray = state.ray; + if (scene.isInWater(ray)) { + ray.setCurrentMaterial(Water.INSTANCE, 0); + } else { + ray.setCurrentMaterial(Air.INSTANCE, 0); + } + + while (true) { + if (!PreviewRayTracer.nextIntersection(scene, ray)) { + if (ray.getPrevMaterial().isWater()) { + // set water color to white + ray.color.set(1, 1, 1, 1); + } else if (ray.depth == 0) { + // direct sky hit + if (!scene.transparentSky()) { + scene.sky().getSkyColorInterpolated(ray); + } + } + // ignore indirect sky hits + break; + } + + if (ray.getCurrentMaterial() != Air.INSTANCE && ray.color.w > 0.0D) { + break; + } + + ray.o.scaleAdd(1.0E-4D, ray.d); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/de/lemaik/chunky/denoiser/BetterRenderManager.java b/src/main/java/de/lemaik/chunky/denoiser/BetterRenderManager.java index 351b195..d8d311d 100644 --- a/src/main/java/de/lemaik/chunky/denoiser/BetterRenderManager.java +++ b/src/main/java/de/lemaik/chunky/denoiser/BetterRenderManager.java @@ -1,185 +1,228 @@ package de.lemaik.chunky.denoiser; import de.lemaik.chunky.denoiser.pfm.PortableFloatMap; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteOrder; import se.llbit.chunky.PersistentSettings; +import se.llbit.chunky.renderer.DefaultRenderManager; +import se.llbit.chunky.renderer.PathTracingRenderer; import se.llbit.chunky.renderer.RenderContext; -import se.llbit.chunky.renderer.RenderManager; import se.llbit.chunky.renderer.RenderMode; import se.llbit.chunky.renderer.RenderStatusListener; -import se.llbit.chunky.renderer.scene.PathTracer; +import se.llbit.chunky.renderer.SnapshotControl; +import se.llbit.chunky.renderer.postprocessing.PixelPostProcessingFilter; +import se.llbit.chunky.renderer.postprocessing.PostProcessingFilter; import se.llbit.chunky.renderer.scene.Scene; import se.llbit.chunky.resources.BitmapImage; import se.llbit.log.Log; import se.llbit.png.PngFileWriter; import se.llbit.util.TaskTracker; -import java.io.*; -import java.nio.ByteOrder; +public class BetterRenderManager extends DefaultRenderManager { -public class BetterRenderManager extends RenderManager { - public static int ALBEDO_SPP = 16; - public static int NORMAL_SPP = 16; - public static boolean ENABLE_ALBEDO = true; - public static boolean ENABLE_NORMAL = true; - public static boolean NORMAL_WATER_DISPLACEMENT = true; - - private final RenderContext context; - private final CombinedRayTracer rayTracer; - private boolean isFirst = true; - private RenderMode mode = RenderMode.PREVIEW; - - public BetterRenderManager(RenderContext context, boolean headless, CombinedRayTracer rayTracer) { - super(context, headless); - this.context = context; - this.rayTracer = rayTracer; - this.addRenderListener(new RenderStatusListener() { - int oldTargetSpp = 0; - - @Override - public void setSpp(int spp) { - if (mode == RenderMode.PREVIEW) { - return; - } + public static int ALBEDO_SPP = 16; + public static int NORMAL_SPP = 16; + public static boolean ENABLE_ALBEDO = true; + public static boolean ENABLE_NORMAL = true; + public static boolean NORMAL_WATER_DISPLACEMENT = true; - if (!isFirst && spp >= getBufferedScene().getTargetSpp()) { - Scene scene = context.getChunky().getSceneManager().getScene(); - if (rayTracer.getRayTracer() instanceof NormalTracer) { - try (OutputStream out = new BufferedOutputStream(context.getSceneFileOutputStream(scene.name + ".normal.pfm"))) { - writeNormalPfmImage(out); - } catch (IOException e) { - Log.error("Saving the normal PFM failed", e); - } - if (ENABLE_ALBEDO) { - renderAlbedoMap(); - } else { - renderImage(oldTargetSpp); - } - } else if (rayTracer.getRayTracer() instanceof AlbedoTracer) { - try (OutputStream out = new BufferedOutputStream(context.getSceneFileOutputStream(scene.name + ".albedo.pfm"))) { - writePfmImage(out, false); - } catch (IOException e) { - Log.error("Saving the albedo PFM failed", e); - } - renderImage(oldTargetSpp); - } else if (rayTracer.getRayTracer() instanceof PathTracer) { - try (OutputStream out = new BufferedOutputStream(context.getSceneFileOutputStream(scene.name + ".pfm"))) { - writePfmImage(out, true); - } catch (IOException e) { - Log.error("Saving the render PFM failed", e); - } - - String denoiserPath = PersistentSettings.settings.getString("oidnPath", null); - if (denoiserPath != null) { - File denoisedPfm = new File(context.getSceneDirectory(), scene.name + ".denoised.pfm"); - try { - OidnBinaryDenoiser.denoise(denoiserPath, - new File(context.getSceneDirectory(), scene.name + ".pfm"), - ENABLE_ALBEDO ? new File(context.getSceneDirectory(), scene.name + ".albedo.pfm") : null, - ENABLE_NORMAL ? new File(context.getSceneDirectory(), scene.name + ".normal.pfm") : null, - denoisedPfm - ); - BitmapImage img = PortableFloatMap.readToRgbImage(new FileInputStream(denoisedPfm)); - try (PngFileWriter pngWriter = new PngFileWriter(new File(context.getSceneDirectory(), scene.name + "-" + spp + ".denoised.png"))) { - pngWriter.write(img.data, img.width, img.height, TaskTracker.Task.NONE); - } - } catch (IOException | InterruptedException e) { - e.printStackTrace(); - } - } - } - } else if (isFirst) { - isFirst = false; - oldTargetSpp = getBufferedScene().getTargetSpp(); - if (ENABLE_NORMAL) { - renderNormalMap(); - } else if (ENABLE_ALBEDO) { - renderAlbedoMap(); - } else { - renderImage(oldTargetSpp); - } - } - } + private final RenderContext context; + private boolean isFirst = true; + private RenderMode mode = RenderMode.PREVIEW; - @Override - public void setRenderTime(long l) { - } + public BetterRenderManager(RenderContext context, boolean headless) { + super(context, headless); + this.context = context; - @Override - public void setSamplesPerSecond(int i) { - } + this.addRenderListener(new RenderStatusListener() { + int oldTargetSpp = 0; - @Override - public void renderStateChanged(RenderMode renderMode) { - RenderMode oldMode = BetterRenderManager.this.mode; - BetterRenderManager.this.mode = renderMode; + @Override + public void setSpp(int spp) { + if (mode == RenderMode.PREVIEW) { + return; + } - if (renderMode == RenderMode.RENDERING && oldMode != RenderMode.PAUSED) { - isFirst = true; - oldTargetSpp = context.getChunky().getSceneManager().getScene().getTargetSpp(); - } + if (!isFirst && spp >= bufferedScene.getTargetSpp()) { + Scene scene = context.getChunky().getSceneManager().getScene(); + if (getRenderer() instanceof NormalRenderer) { + try (OutputStream out = new BufferedOutputStream( + context.getSceneFileOutputStream(scene.name + ".normal.pfm"))) { + writeNormalPfmImage(out); + } catch (IOException e) { + Log.error("Saving the normal PFM failed", e); + } + if (ENABLE_ALBEDO) { + renderAlbedoMap(); + } else { + renderImage(oldTargetSpp); + } + } else if (getRenderer() instanceof AlbedoRenderer) { + try (OutputStream out = new BufferedOutputStream( + context.getSceneFileOutputStream(scene.name + ".albedo.pfm"))) { + writePfmImage(out, false); + } catch (IOException e) { + Log.error("Saving the albedo PFM failed", e); + } + renderImage(oldTargetSpp); + } else if (getRenderer() instanceof PathTracingRenderer) { + try (OutputStream out = new BufferedOutputStream( + context.getSceneFileOutputStream(scene.name + ".pfm"))) { + writePfmImage(out, true); + } catch (IOException e) { + Log.error("Saving the render PFM failed", e); } - }); - } - - private void renderAlbedoMap() { - rayTracer.setRayTracer(new AlbedoTracer()); - Scene scene = this.context.getChunky().getSceneManager().getScene(); - scene.haltRender(); - scene.setTargetSpp(ALBEDO_SPP); - scene.startRender(); - } - - private void renderNormalMap() { - rayTracer.setRayTracer(new NormalTracer()); - Scene scene = context.getChunky().getSceneManager().getScene(); - scene.haltRender(); - scene.setTargetSpp(NORMAL_SPP); - scene.startRender(); - - } - - private void renderImage(int spp) { - rayTracer.setRayTracer(new PathTracer()); - Scene scene = this.context.getChunky().getSceneManager().getScene(); - scene.haltRender(); - scene.setTargetSpp(spp); - scene.startRender(); - } - private void writePfmImage(OutputStream out, boolean postProcess) throws IOException { - Scene scene = getBufferedScene(); - double[] samples = scene.getSampleBuffer(); - double[] pixels = new double[samples.length]; - - for (int y = 0; y < scene.height; y++) { - for (int x = 0; x < scene.width; x++) { - double[] result = new double[3]; - if (postProcess) { - scene.postProcessPixel(x, y, result); - } else { - result[0] = samples[(y * scene.width + x) * 3 + 0]; - result[1] = samples[(y * scene.width + x) * 3 + 1]; - result[2] = samples[(y * scene.width + x) * 3 + 2]; + String denoiserPath = PersistentSettings.settings.getString("oidnPath", null); + if (denoiserPath != null) { + File denoisedPfm = new File(context.getSceneDirectory(), + scene.name + ".denoised.pfm"); + try { + OidnBinaryDenoiser.denoise(denoiserPath, + new File(context.getSceneDirectory(), scene.name + ".pfm"), + ENABLE_ALBEDO ? new File(context.getSceneDirectory(), + scene.name + ".albedo.pfm") : null, + ENABLE_NORMAL ? new File(context.getSceneDirectory(), + scene.name + ".normal.pfm") : null, + denoisedPfm + ); + BitmapImage img = PortableFloatMap.readToRgbImage(new FileInputStream(denoisedPfm)); + File snapshotsDir = new File(context.getSceneDirectory(), "snapshots"); + snapshotsDir.mkdirs(); + try (PngFileWriter pngWriter = new PngFileWriter( + new File(snapshotsDir, + scene.name + "-" + spp + ".denoised.png"))) { + pngWriter.write(img.data, img.width, img.height, TaskTracker.Task.NONE); } - pixels[(y * scene.width + x) * 3] = Math.min(1.0, result[0]); - pixels[(y * scene.width + x) * 3 + 1] = Math.min(1.0, result[1]); - pixels[(y * scene.width + x) * 3 + 2] = Math.min(1.0, result[2]); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } } + } + } else if (isFirst) { + isFirst = false; + oldTargetSpp = bufferedScene.getTargetSpp(); + + if (ENABLE_NORMAL) { + renderNormalMap(); + } else if (ENABLE_ALBEDO) { + renderAlbedoMap(); + } else { + renderImage(oldTargetSpp); + } } + } - PortableFloatMap.writeImage(pixels, scene.width, scene.height, ByteOrder.LITTLE_ENDIAN, out); - } + @Override + public void setRenderTime(long l) { + } - private void writeNormalPfmImage(OutputStream out) throws IOException { - Scene scene = getBufferedScene(); - double[] samples = scene.getSampleBuffer(); - double[] pixels = NormalTracer.MAP_POSITIVE ? new double[samples.length] : samples; - if (NormalTracer.MAP_POSITIVE) { - for (int i = 0; i < samples.length; i++) { - pixels[i] = Math.min(1.0, samples[i]) * 2 - 1; - } + @Override + public void setSamplesPerSecond(int i) { + } + + @Override + public void renderStateChanged(RenderMode renderMode) { + RenderMode oldMode = BetterRenderManager.this.mode; + BetterRenderManager.this.mode = renderMode; + + if (renderMode == RenderMode.RENDERING && oldMode != RenderMode.PAUSED) { + isFirst = true; + oldTargetSpp = context.getChunky().getSceneManager().getScene().getTargetSpp(); + } + } + }); + } + + @Override + public void setSnapshotControl(SnapshotControl sc) { + super.setSnapshotControl(new SnapshotControl() { + @Override + public boolean saveSnapshot(Scene scene, int nextSpp) { + return !scene.getRenderer().equals(AlbedoRenderer.ID) + && !scene.getRenderer().equals(NormalRenderer.ID) + && sc.saveSnapshot(scene, nextSpp); + } + + @Override + public boolean saveRenderDump(Scene scene, int nextSpp) { + return !scene.getRenderer().equals(AlbedoRenderer.ID) + && !scene.getRenderer().equals(NormalRenderer.ID) + && sc.saveRenderDump(scene, nextSpp); + } + }); + } + + private void renderAlbedoMap() { + Scene scene = this.context.getChunky().getSceneManager().getScene(); + scene.setRenderer(AlbedoRenderer.ID); + scene.haltRender(); + scene.setTargetSpp(ALBEDO_SPP); + scene.startRender(); + } + + private void renderNormalMap() { + Scene scene = this.context.getChunky().getSceneManager().getScene(); + scene.setRenderer(NormalRenderer.ID); + scene.haltRender(); + scene.setTargetSpp(NORMAL_SPP); + scene.startRender(); + + } + + private void renderImage(int spp) { + Scene scene = this.context.getChunky().getSceneManager().getScene(); + scene.setRenderer(DefaultRenderManager.ChunkyPathTracerID); + scene.haltRender(); + scene.setTargetSpp(spp); + scene.startRender(); + } + + private void writePfmImage(OutputStream out, boolean postProcess) throws IOException { + Scene scene = bufferedScene; + double[] samples = scene.getSampleBuffer(); + double[] pixels = new double[samples.length]; + + for (int y = 0; y < scene.height; y++) { + for (int x = 0; x < scene.width; x++) { + double[] result = new double[3]; + if (postProcess) { + PostProcessingFilter filter = scene.getPostProcessingFilter(); + if (filter instanceof PixelPostProcessingFilter) { + ((PixelPostProcessingFilter) filter) + .processPixel(scene.width, scene.height, samples, x, y, scene.getExposure(), + result); + } + } else { + result[0] = samples[(y * scene.width + x) * 3 + 0]; + result[1] = samples[(y * scene.width + x) * 3 + 1]; + result[2] = samples[(y * scene.width + x) * 3 + 2]; } + pixels[(y * scene.width + x) * 3] = Math.min(1.0, result[0]); + pixels[(y * scene.width + x) * 3 + 1] = Math.min(1.0, result[1]); + pixels[(y * scene.width + x) * 3 + 2] = Math.min(1.0, result[2]); + } + } - PortableFloatMap.writeImage(pixels, scene.width, scene.height, ByteOrder.LITTLE_ENDIAN, out); + PortableFloatMap.writeImage(pixels, scene.width, scene.height, ByteOrder.LITTLE_ENDIAN, out); + } + + private void writeNormalPfmImage(OutputStream out) throws IOException { + Scene scene = bufferedScene; + double[] samples = scene.getSampleBuffer(); + double[] pixels; + if (NormalRenderer.MAP_POSITIVE) { + pixels = new double[samples.length]; + for (int i = 0; i < samples.length; i++) { + pixels[i] = Math.min(1.0, samples[i]) * 2 - 1; + } + } else { + pixels = samples; } + PortableFloatMap.writeImage(pixels, scene.width, scene.height, ByteOrder.LITTLE_ENDIAN, out); + } } diff --git a/src/main/java/de/lemaik/chunky/denoiser/CombinedRayTracer.java b/src/main/java/de/lemaik/chunky/denoiser/CombinedRayTracer.java deleted file mode 100644 index cb9e64e..0000000 --- a/src/main/java/de/lemaik/chunky/denoiser/CombinedRayTracer.java +++ /dev/null @@ -1,23 +0,0 @@ -package de.lemaik.chunky.denoiser; - -import se.llbit.chunky.renderer.WorkerState; -import se.llbit.chunky.renderer.scene.PathTracer; -import se.llbit.chunky.renderer.scene.RayTracer; -import se.llbit.chunky.renderer.scene.Scene; - -public class CombinedRayTracer implements RayTracer { - private RayTracer rayTracer = new PathTracer(); - - @Override - public void trace(Scene scene, WorkerState workerState) { - this.rayTracer.trace(scene, workerState); - } - - public RayTracer getRayTracer() { - return rayTracer; - } - - public void setRayTracer(RayTracer rayTracer) { - this.rayTracer = rayTracer; - } -} diff --git a/src/main/java/de/lemaik/chunky/denoiser/DenoiserPlugin.java b/src/main/java/de/lemaik/chunky/denoiser/DenoiserPlugin.java index abb9f0e..55fa739 100644 --- a/src/main/java/de/lemaik/chunky/denoiser/DenoiserPlugin.java +++ b/src/main/java/de/lemaik/chunky/denoiser/DenoiserPlugin.java @@ -1,9 +1,10 @@ package de.lemaik.chunky.denoiser; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import se.llbit.chunky.Plugin; import se.llbit.chunky.main.Chunky; import se.llbit.chunky.main.ChunkyOptions; -import se.llbit.chunky.renderer.RendererFactory; import se.llbit.chunky.renderer.SnapshotControl; import se.llbit.chunky.renderer.scene.PathTracer; import se.llbit.chunky.renderer.scene.Scene; @@ -11,80 +12,35 @@ import se.llbit.chunky.ui.render.RenderControlsTabTransformer; import se.llbit.log.Log; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; - /** * This plugin renders normal and albedo maps for use with image de-noisers. */ public class DenoiserPlugin implements Plugin { - @Override - public void attach(Chunky chunky) { - try { - Field f = chunky.getClass().getDeclaredField("headless"); - f.setAccessible(true); - boolean headless = f.getBoolean(chunky); - if (headless) { - Log.warn("The denoiser plugin does not support headless mode and will not be enabled."); - return; - } - } catch (NoSuchFieldException | IllegalAccessException e) { - Log.error("Headless mode check failed. Note that the denoiser plugin does not support headless mode.", e); - } - CombinedRayTracer rayTracer = new CombinedRayTracer(); - try { - Field f = chunky.getClass().getDeclaredField("rendererFactory"); - f.setAccessible(true); - f.set(chunky, (RendererFactory) (context, headless) -> new BetterRenderManager(context, headless, rayTracer)); - } catch (NoSuchFieldException | IllegalAccessException e) { - e.printStackTrace(); - } - - chunky.setRayTracerFactory(() -> rayTracer); - - RenderControlsTabTransformer prev = chunky.getRenderControlsTabTransformer(); - chunky.setRenderControlsTabTransformer(tabs -> { - tabs = prev.apply(tabs); - tabs.add(DenoiserTab.getImplementation()); - return tabs; - }); - - // Replace SnapshotControl.DEFAULT to prevent saving snapshots when rendering normal or albedo maps - try { - SnapshotControl defaultSnapshotControl = SnapshotControl.DEFAULT; - Field f = SnapshotControl.class.getDeclaredField("DEFAULT"); - f.setAccessible(true); - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL); - f.set(null, new SnapshotControl() { - @Override - public boolean saveSnapshot(Scene scene, int nextSpp) { - if (rayTracer.getRayTracer() instanceof PathTracer) { - return defaultSnapshotControl.saveSnapshot(scene, nextSpp); - } - return false; - } - - @Override - public boolean saveRenderDump(Scene scene, int nextSpp) { - if (rayTracer.getRayTracer() instanceof PathTracer) { - return defaultSnapshotControl.saveRenderDump(scene, nextSpp); - } - return false; - } - }); - } catch (NoSuchFieldException | IllegalAccessException e) { - e.printStackTrace(); - } + @Override + public void attach(Chunky chunky) { + if (chunky.isHeadless()) { + Log.warn("The denoiser plugin does not support headless mode and will not be enabled."); + return; } - public static void main(String[] args) throws Exception { - // Start Chunky normally with this plugin attached. - Chunky.loadDefaultTextures(); - Chunky chunky = new Chunky(ChunkyOptions.getDefaults()); - new DenoiserPlugin().attach(chunky); - ChunkyFx.startChunkyUI(chunky); - } + Chunky.addRenderer(new AlbedoRenderer()); + Chunky.addRenderer(new NormalRenderer()); + chunky.setRenderManagerFactory(BetterRenderManager::new); + + RenderControlsTabTransformer prev = chunky.getRenderControlsTabTransformer(); + chunky.setRenderControlsTabTransformer(tabs -> { + tabs = prev.apply(tabs); + tabs.add(DenoiserTab.getImplementation()); + return tabs; + }); + } + + public static void main(String[] args) throws Exception { + // Start Chunky normally with this plugin attached. + Chunky.loadDefaultTextures(); + Chunky chunky = new Chunky(ChunkyOptions.getDefaults()); + new DenoiserPlugin().attach(chunky); + ChunkyFx.startChunkyUI(chunky); + } } diff --git a/src/chunky-2/java/de/lemaik/chunky/denoiser/DenoiserTabImpl.java b/src/main/java/de/lemaik/chunky/denoiser/DenoiserTabImpl.java similarity index 100% rename from src/chunky-2/java/de/lemaik/chunky/denoiser/DenoiserTabImpl.java rename to src/main/java/de/lemaik/chunky/denoiser/DenoiserTabImpl.java diff --git a/src/main/java/de/lemaik/chunky/denoiser/NormalRenderer.java b/src/main/java/de/lemaik/chunky/denoiser/NormalRenderer.java new file mode 100644 index 0000000..d8788c2 --- /dev/null +++ b/src/main/java/de/lemaik/chunky/denoiser/NormalRenderer.java @@ -0,0 +1,48 @@ +package de.lemaik.chunky.denoiser; + +import se.llbit.chunky.model.WaterModel; +import se.llbit.chunky.renderer.PathTracingRenderer; +import se.llbit.chunky.renderer.WorkerState; +import se.llbit.chunky.renderer.scene.PreviewRayTracer; +import se.llbit.chunky.renderer.scene.RayTracer; +import se.llbit.chunky.renderer.scene.Scene; +import se.llbit.math.Ray; +import se.llbit.math.Vector3; + +public class NormalRenderer extends PathTracingRenderer { + + public static final String ID = "NORMAL"; + + /** + * If true, all values are mapped to positive values so that they can be displayed on the rendered + * image. + */ + static final boolean MAP_POSITIVE = false; + + public NormalRenderer() { + super(ID, "Normal map", "Renderer for normal maps (used for denoising)", + new NormalTracer()); + } + + private static class NormalTracer implements RayTracer { + + @Override + public void trace(Scene scene, WorkerState state) { + Ray ray = state.ray; + if (PreviewRayTracer.nextIntersection(scene, ray)) { + if (BetterRenderManager.NORMAL_WATER_DISPLACEMENT && !scene.stillWaterEnabled() + && ray.getCurrentMaterial().isWater()) { + WaterModel.doWaterDisplacement(ray); + } + + if (MAP_POSITIVE) { + Vector3 normal = new Vector3(ray.n); + normal.normalize(); + ray.color.set((normal.x + 1) / 2, (normal.y + 1) / 2, (normal.z + 1) / 2, 1); + } else { + ray.color.set(ray.n.x, ray.n.y, ray.n.z, 1); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/de/lemaik/chunky/denoiser/NormalTracer.java b/src/main/java/de/lemaik/chunky/denoiser/NormalTracer.java deleted file mode 100644 index b3dcb61..0000000 --- a/src/main/java/de/lemaik/chunky/denoiser/NormalTracer.java +++ /dev/null @@ -1,34 +0,0 @@ -package de.lemaik.chunky.denoiser; - -import se.llbit.chunky.model.WaterModel; -import se.llbit.chunky.renderer.WorkerState; -import se.llbit.chunky.renderer.scene.PreviewRayTracer; -import se.llbit.chunky.renderer.scene.RayTracer; -import se.llbit.chunky.renderer.scene.Scene; -import se.llbit.math.Ray; -import se.llbit.math.Vector3; - -public class NormalTracer implements RayTracer { - /** - * If true, all values are mapped to positive values so that they can be displayed on the rendered image. - */ - static final boolean MAP_POSITIVE = false; - - @Override - public void trace(Scene scene, WorkerState state) { - Ray ray = state.ray; - if (PreviewRayTracer.nextIntersection(scene, ray)) { - if (BetterRenderManager.NORMAL_WATER_DISPLACEMENT && !scene.stillWaterEnabled() && ray.getCurrentMaterial().isWater()) { - WaterModel.doWaterDisplacement(ray); - } - - if (MAP_POSITIVE) { - Vector3 normal = new Vector3(ray.n); - normal.normalize(); - ray.color.set((normal.x + 1) / 2, (normal.y + 1) / 2, (normal.z + 1) / 2, 1); - } else { - ray.color.set(ray.n.x, ray.n.y, ray.n.z, 1); - } - } - } -} diff --git a/src/chunky-2/resources/plugin.json b/src/main/resources/plugin.json similarity index 66% rename from src/chunky-2/resources/plugin.json rename to src/main/resources/plugin.json index b07c68a..b61e174 100644 --- a/src/chunky-2/resources/plugin.json +++ b/src/main/resources/plugin.json @@ -2,7 +2,7 @@ "name": "DenoiserPlugin", "author": "leMaik", "main": "de.lemaik.chunky.denoiser.DenoiserPlugin", - "version": "0.3.2", - "targetVersion": "2.0-beta6", - "description": "Renders normal and albedo maps to pfm files for use with de-noisers." + "version": "0.4.0", + "targetVersion": "2.4.0-159-g8ed94bb7", + "description": "Renders normal and albedo maps to pfm files for use with denoisers." }