Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ public class DenoisedPathTracingRenderer extends MultiPassRenderer {
protected final AlbedoTracer albedoTracer = new AlbedoTracer();
protected final NormalTracer normalTracer = new NormalTracer();

private boolean hiddenPasses = false;

public DenoisedPathTracingRenderer(Denoiser denoiser, String id, String name, String description, RayTracer tracer) {
this.denoiser = denoiser;
this.id = id;
Expand All @@ -54,84 +52,71 @@ public String getDescription() {
@Override
public void render(DefaultRenderManager manager) throws InterruptedException {
Scene scene = manager.bufferedScene;
double[] sampleBuffer = scene.getSampleBuffer();
boolean aborted = false;

int originalSpp = scene.spp;
int sceneTarget = scene.getTargetSpp();

DenoiserSettings settings = new DenoiserSettings();
settings.loadFromScene(scene);

int maxSpp = Math.max(sceneTarget, Math.max(settings.albedoSpp.get(), settings.normalSpp.get()));
scene.setTargetSpp(maxSpp);

RayTracer[] tracers = new RayTracer[]{albedoTracer, normalTracer, tracer};
float[][] buffers = new float[][]{
settings.renderAlbedo.get() ? new float[sampleBuffer.length] : null,
settings.renderNormal.get() ? new float[sampleBuffer.length] : null,
null};
boolean[] tracerMask = new boolean[3];
scene.spp = 0;

while (scene.spp < maxSpp) {
tracerMask[0] = settings.renderAlbedo.get() && scene.spp < settings.albedoSpp.get();
tracerMask[1] = settings.renderNormal.get() && scene.spp < settings.normalSpp.get();
tracerMask[2] = scene.spp >= originalSpp && scene.spp < sceneTarget;
hiddenPasses = !tracerMask[2];
renderPass(manager, manager.context.sppPerPass(), tracers, buffers, tracerMask);
if (scene.spp < maxSpp && postRender.getAsBoolean()) {
aborted = true;
break;
double[] sampleBuffer = scene.getSampleBuffer();

boolean albedoEnable = settings.renderAlbedo.get();
int albedoTarget = settings.albedoSpp.get();
int albedoSampleScale = (int) Math.ceil((double) albedoTarget / scene.getTargetSpp());
int albedoSamples = 0;
float[] albedoBuffer = albedoEnable ? new float[sampleBuffer.length] : null;

boolean normalEnable = settings.renderNormal.get();
int normalTarget = settings.normalSpp.get();
int normalSampleScale = (int) Math.ceil((double) normalTarget / scene.getTargetSpp());
int normalSamples = 0;
float[] normalBuffer = normalEnable ? new float[sampleBuffer.length] : null;

while (scene.spp < scene.getTargetSpp()) {
if (albedoEnable && albedoSamples < albedoTarget) {
int samples = Math.min(albedoSampleScale, albedoTarget - albedoSamples);
albedoSamples = this.renderPass(manager, albedoSamples, samples, albedoTracer, albedoBuffer);
}
if (normalEnable && normalSamples < normalTarget) {
int samples = Math.max(normalSampleScale, normalTarget - normalSamples);
normalSamples = this.renderPass(manager, normalSamples, samples, normalTracer, normalBuffer);
}
scene.spp = renderPass(manager, scene.spp, 1, tracer, null);

if (scene.spp < scene.getTargetSpp() && postRender.getAsBoolean()) {
// Canceled
return;
}
}

if (!aborted && settings.saveBeauty.get()) {
if (settings.saveBeauty.get()) {
File out = manager.context.getSceneFile(scene.name + ".beauty.pfm");
scene.saveFrame(out, PortableFloatMap.getPfmExportFormat(), TaskTracker.NONE);
}

if (!aborted && settings.saveAlbedo.get() && buffers[0] != null) {
if (settings.saveAlbedo.get() && albedoBuffer != null) {
File out = manager.context.getSceneFile(scene.name + ".albedo.pfm");
try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(out.toPath()))) {
PortableFloatMap.writeImage(buffers[0], scene.canvasConfig.getWidth(), scene.canvasConfig.getHeight(), ByteOrder.LITTLE_ENDIAN, os);
PortableFloatMap.writeImage(albedoBuffer, scene.canvasConfig.getWidth(), scene.canvasConfig.getHeight(), ByteOrder.LITTLE_ENDIAN, os);
} catch (IOException e) {
Log.error("Failed to save albedo pass", e);
}
}

if (!aborted && settings.saveNormal.get() && buffers[1] != null) {
if (settings.saveNormal.get() && normalBuffer != null) {
File out = manager.context.getSceneFile(scene.name + ".normal.pfm");
try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(out.toPath()))) {
PortableFloatMap.writeImage(buffers[1], scene.canvasConfig.getWidth(), scene.canvasConfig.getHeight(), ByteOrder.LITTLE_ENDIAN, os);
PortableFloatMap.writeImage(normalBuffer, scene.canvasConfig.getWidth(), scene.canvasConfig.getHeight(), ByteOrder.LITTLE_ENDIAN, os);
} catch (IOException e) {
Log.error("Failed to save normal pass", e);
}
}

if (!aborted) {
if (denoiser instanceof OidnBinaryDenoiser)
((OidnBinaryDenoiser) denoiser).loadPath();

try {
denoiser.denoiseDouble(scene.canvasConfig.getWidth(), scene.canvasConfig.getHeight(), sampleBuffer,
buffers[0], buffers[1], sampleBuffer);
} catch (Denoiser.DenoisingFailedException e) {
Log.error("Failed to denoise", e);
}
try {
manager.getRenderTask().update("Denoising", scene.getTargetSpp(), scene.spp);
denoiser.init();
denoiser.denoiseDouble(scene.canvasConfig.getWidth(), scene.canvasConfig.getHeight(), sampleBuffer, albedoBuffer, normalBuffer, sampleBuffer);
} catch (Denoiser.DenoisingFailedException e) {
Log.error("Failed to denoise", e);
}

if (scene.spp < originalSpp) {
scene.spp = originalSpp;
} else if (scene.spp > sceneTarget) {
scene.spp = sceneTarget;
}
scene.setTargetSpp(sceneTarget);
postRender.getAsBoolean();
}

@Override
public boolean autoPostProcess() {
return !hiddenPasses;
}
}
5 changes: 5 additions & 0 deletions src/main/java/de/lemaik/chunky/denoiser/Denoiser.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ default void denoiseDouble(int width, int height, double[] beauty, float[] albed
output[i] = outputF[i];
}

/**
* Initialize this denoiser and prepare it for denoising.
*/
default void init() { }

/**
* Denoising failed for some reason.
*/
Expand Down
77 changes: 39 additions & 38 deletions src/main/java/de/lemaik/chunky/denoiser/DenoiserPassRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,68 +58,69 @@ public boolean autoPostProcess() {
@Override
public void render(DefaultRenderManager manager) throws InterruptedException {
Scene scene = manager.bufferedScene;
double[] sampleBuffer = scene.getSampleBuffer();
boolean aborted = false;

DenoiserSettings settings = new DenoiserSettings();
settings.loadFromScene(scene);

scene.setTargetSpp(Math.max(settings.albedoSpp.get(), settings.normalSpp.get()));

RayTracer[] tracers = new RayTracer[]{albedoTracer, normalTracer};
float[][] buffers = new float[][]{
settings.renderAlbedo.get() ? new float[sampleBuffer.length] : null,
settings.renderNormal.get() ? new float[sampleBuffer.length] : null,
};
boolean[] tracerMask = new boolean[2];
scene.spp = 0;

while (scene.spp < scene.getTargetSpp()) {
tracerMask[0] = settings.renderAlbedo.get() && scene.spp < settings.albedoSpp.get();
tracerMask[1] = settings.renderNormal.get() && scene.spp < settings.normalSpp.get();
renderPass(manager, manager.context.sppPerPass(), tracers, buffers, tracerMask);
if (scene.spp < scene.getTargetSpp() && postRender.getAsBoolean()) {
aborted = true;
break;
double[] sampleBuffer = scene.getSampleBuffer();

boolean albedoEnable = settings.renderAlbedo.get();
int albedoTarget = settings.albedoSpp.get();
float[] albedoBuffer = albedoEnable ? new float[sampleBuffer.length] : null;

boolean normalEnable = settings.renderNormal.get();
int normalTarget = settings.normalSpp.get();
float[] normalBuffer = normalEnable ? new float[sampleBuffer.length] : null;

if (albedoEnable || normalEnable) {
int targetSpp = Math.max(albedoTarget, normalTarget);
scene.spp = 0;

while (scene.spp < targetSpp) {
if (albedoEnable && scene.spp < albedoTarget) {
this.renderPass(manager, scene.spp, 1, albedoTracer, albedoBuffer);
}
if (normalEnable && scene.spp < normalTarget) {
this.renderPass(manager, scene.spp, 1, normalTracer, normalBuffer);
}
scene.spp += 1;
if (scene.spp < targetSpp && postRender.getAsBoolean()) {
// Canceled
return;
}
}
}

if (!aborted && settings.saveBeauty.get()) {
if (settings.saveBeauty.get()) {
File out = manager.context.getSceneFile(scene.name + ".beauty.pfm");
scene.saveFrame(out, PortableFloatMap.getPfmExportFormat(), TaskTracker.NONE);
}

if (!aborted && settings.saveAlbedo.get() && buffers[0] != null) {
if (settings.saveAlbedo.get() && albedoBuffer != null) {
File out = manager.context.getSceneFile(scene.name + ".albedo.pfm");
try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(out.toPath()))) {
PortableFloatMap.writeImage(buffers[0], scene.canvasConfig.getWidth(), scene.canvasConfig.getHeight(), ByteOrder.LITTLE_ENDIAN, os);
PortableFloatMap.writeImage(albedoBuffer, scene.canvasConfig.getWidth(), scene.canvasConfig.getHeight(), ByteOrder.LITTLE_ENDIAN, os);
} catch (IOException e) {
Log.error("Failed to save albedo pass", e);
}
}

if (!aborted && settings.saveNormal.get() && buffers[1] != null) {
if (settings.saveNormal.get() && normalBuffer != null) {
File out = manager.context.getSceneFile(scene.name + ".normal.pfm");
try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(out.toPath()))) {
PortableFloatMap.writeImage(buffers[1], scene.canvasConfig.getWidth(), scene.canvasConfig.getHeight(), ByteOrder.LITTLE_ENDIAN, os);
PortableFloatMap.writeImage(normalBuffer, scene.canvasConfig.getWidth(), scene.canvasConfig.getHeight(), ByteOrder.LITTLE_ENDIAN, os);
} catch (IOException e) {
Log.error("Failed to save normal pass", e);
}
}

if (!aborted) {
if (denoiser instanceof OidnBinaryDenoiser)
((OidnBinaryDenoiser) denoiser).loadPath();

try {
denoiser.denoiseDouble(scene.canvasConfig.getWidth(), scene.canvasConfig.getHeight(), sampleBuffer,
buffers[0], buffers[1], sampleBuffer);

scene.spp = scene.getTargetSpp();
postRender.getAsBoolean();
} catch (Denoiser.DenoisingFailedException e) {
Log.error("Failed to denoise", e);
}
try {
manager.getRenderTask().update("Denoising", scene.getTargetSpp(), scene.spp);
denoiser.init();
denoiser.denoiseDouble(scene.canvasConfig.getWidth(), scene.canvasConfig.getHeight(), sampleBuffer, albedoBuffer, normalBuffer, sampleBuffer);
} catch (Denoiser.DenoisingFailedException e) {
Log.error("Failed to denoise", e);
}

postRender.getAsBoolean();
}
}
2 changes: 1 addition & 1 deletion src/main/java/de/lemaik/chunky/denoiser/DenoiserTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void initialize(URL location, ResourceBundle resources) {

settings.renderAlbedo.addListener(v -> albedoMap.setSelected(v));
albedoMap.selectedProperty().addListener((observable, oldValue, newValue) ->
settings.renderAlbedo.get());
settings.renderAlbedo.set(newValue));

settings.albedoSpp.addListener(v -> albedoSpp.valueProperty().set(v));
albedoSpp.valueProperty().addListener(((observable, oldValue, newValue) ->
Expand Down
51 changes: 17 additions & 34 deletions src/main/java/de/lemaik/chunky/denoiser/MultiPassRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import se.llbit.chunky.renderer.scene.Scene;

public abstract class MultiPassRenderer extends TileBasedRenderer {
protected void renderPass(DefaultRenderManager manager, int passSpp, RayTracer[] tracers, float[][] renderBuffers, boolean[] tracerMask) throws InterruptedException {
protected int renderPass(DefaultRenderManager manager, int spp, int passSamples, RayTracer tracer, float[] buffer) throws InterruptedException {
Scene scene = manager.bufferedScene;
double[] sampleBuffer = scene.getSampleBuffer();
int width = scene.canvasConfig.getWidth();
Expand All @@ -21,58 +21,41 @@ protected void renderPass(DefaultRenderManager manager, int passSpp, RayTracer[]
double halfWidth = fullWidth / (2.0 * fullHeight);
double invHeight = 1.0 / fullHeight;

int spp = scene.spp;
double passinv = 1.0 / passSpp;
double sinv = 1.0 / (passSpp + spp);
double sinv = 1.0 / (passSamples + spp);

submitTiles(manager, (state, pixel) -> {
int sx = pixel.firstInt();
int sy = pixel.secondInt();
int x = sx + cropX;
int y = sy + cropY;

double[] srgb = new double[tracers.length * 3];
double[] srgb = new double[3];

for (int k = 0; k < passSpp; k++) {
for (int k = 0; k < passSamples; k++) {
double ox = state.random.nextDouble();
double oy = state.random.nextDouble();

for (int i = 0; i < tracers.length; i++) {
if (tracerMask[i]) {
cam.calcViewRay(state.ray, state.random,
-halfWidth + (x + ox) * invHeight,
-0.5 + (y + oy) * invHeight);
cam.calcViewRay(state.ray, state.random,
-halfWidth + (x + ox) * invHeight,
-0.5 + (y + oy) * invHeight);

scene.rayTrace(tracers[i], state);
srgb[i * 3 + 0] += state.ray.color.x;
srgb[i * 3 + 1] += state.ray.color.y;
srgb[i * 3 + 2] += state.ray.color.z;
}
}
scene.rayTrace(tracer, state);
srgb[0] += state.ray.color.x;
srgb[1] += state.ray.color.y;
srgb[2] += state.ray.color.z;
}

int offset = 3 * (sy*width + sx);
for (int i = 0; i < tracers.length; i++) {
if (tracerMask[i]) {
float[] buffer = renderBuffers[i];
double r = srgb[i * 3 + 0] * passinv;
double g = srgb[i * 3 + 1] * passinv;
double b = srgb[i * 3 + 2] * passinv;

if (buffer == null) {
sampleBuffer[offset + 0] = (sampleBuffer[offset + 0] * spp + r) * sinv;
sampleBuffer[offset + 1] = (sampleBuffer[offset + 1] * spp + g) * sinv;
sampleBuffer[offset + 2] = (sampleBuffer[offset + 2] * spp + b) * sinv;
} else {
buffer[offset + 0] = (float) ((buffer[offset + 0] * spp + r) * sinv);
buffer[offset + 1] = (float) ((buffer[offset + 1] * spp + g) * sinv);
buffer[offset + 2] = (float) ((buffer[offset + 2] * spp + b) * sinv);
}
for (int i = 0; i < 3; i++) {
if (buffer == null) {
sampleBuffer[offset + i] = (sampleBuffer[offset + i] * spp + srgb[i]) * sinv;
} else {
buffer[offset + i] = (float) ((buffer[offset + i] * spp + srgb[i]) * sinv);
}
}
});

manager.pool.awaitEmpty();
scene.spp += passSpp;
return spp + passSamples;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public void loadPath() {
setOidnPath(PersistentSettings.settings.getString("oidnPath", ""));
}

@Override
public void init() {
this.loadPath();
}

@Override
public float[] denoise(int width, int height, float[] beauty, float[] albedo, float[] normal)
throws DenoisingFailedException{
Expand Down