diff --git a/chunky/src/java/se/llbit/chunky/renderer/scene/PathTracer.java b/chunky/src/java/se/llbit/chunky/renderer/scene/PathTracer.java
index da75ea168a..ded7801cdb 100644
--- a/chunky/src/java/se/llbit/chunky/renderer/scene/PathTracer.java
+++ b/chunky/src/java/se/llbit/chunky/renderer/scene/PathTracer.java
@@ -104,7 +104,7 @@ public static boolean pathTrace(Scene scene, Ray ray, WorkerState state, int add
float pSpecular = currentMat.specular;
- double pDiffuse = 1 - Math.sqrt(1 - ray.color.w);
+ double pDiffuse = scene.fancierTranslucency ? 1 - Math.sqrt(1 - ray.color.w) : ray.color.w;
float n1 = prevMat.ior;
float n2 = currentMat.ior;
@@ -425,50 +425,57 @@ private static boolean doTransmission(Ray ray, Ray next, Vector4 cumulativeColor
}
private static void translucentRayColor(Scene scene, Ray ray, Ray next, Vector4 cumulativeColor, double opacity) {
- // Color-based transmission value
- double colorTrans = (ray.color.x + ray.color.y + ray.color.z)/3;
- // Total amount of light we want to transmit (overall transparency of texture)
- double shouldTrans = 1 - opacity;
- // Amount of each color to transmit - default to overall transparency if RGB values add to 0 (e.g. regular glass)
- Vector3 rgbTrans = new Vector3(shouldTrans, shouldTrans, shouldTrans);
- if(colorTrans > 0) {
- // Amount to transmit of each color is scaled so the total transmitted amount matches the texture's transparency
- rgbTrans.set(ray.color.toVec3());
- rgbTrans.scale(shouldTrans / colorTrans);
- }
- double transmissivityCap = scene.transmissivityCap;
- // Determine the color with the highest transmissivity
- double maxTrans = Math.max(rgbTrans.x, Math.max(rgbTrans.y, rgbTrans.z));
- if(maxTrans > transmissivityCap) {
- if (maxTrans == rgbTrans.x) {
- // Give excess transmission from red to green and blue
- double gTransNew = reassignTransmissivity(rgbTrans.x, rgbTrans.y, rgbTrans.z, shouldTrans, transmissivityCap);
- rgbTrans.z = reassignTransmissivity(rgbTrans.x, rgbTrans.z, rgbTrans.y, shouldTrans, transmissivityCap);
- rgbTrans.y = gTransNew;
- rgbTrans.x = transmissivityCap;
- } else if (maxTrans == rgbTrans.y) {
- // Give excess transmission from green to red and blue
- double rTransNew = reassignTransmissivity(rgbTrans.y, rgbTrans.x, rgbTrans.z, shouldTrans, transmissivityCap);
- rgbTrans.z = reassignTransmissivity(rgbTrans.y, rgbTrans.z, rgbTrans.x, shouldTrans, transmissivityCap);
- rgbTrans.x = rTransNew;
- rgbTrans.y = transmissivityCap;
- } else if (maxTrans == rgbTrans.z) {
- // Give excess transmission from blue to green and red
- double gTransNew = reassignTransmissivity(rgbTrans.z, rgbTrans.y, rgbTrans.x, shouldTrans, transmissivityCap);
- rgbTrans.x = reassignTransmissivity(rgbTrans.z, rgbTrans.x, rgbTrans.y, shouldTrans, transmissivityCap);
- rgbTrans.y = gTransNew;
- rgbTrans.z = transmissivityCap;
+ Vector3 rgbTrans;
+ if(scene.fancierTranslucency) {
+ // Color-based transmission value
+ double colorTrans = (ray.color.x + ray.color.y + ray.color.z) / 3;
+ // Total amount of light we want to transmit (overall transparency of texture)
+ double shouldTrans = 1 - opacity;
+ // Amount of each color to transmit - default to overall transparency if RGB values add to 0 (e.g. regular glass)
+ rgbTrans = new Vector3(shouldTrans, shouldTrans, shouldTrans);
+ if (colorTrans > 0) {
+ // Amount to transmit of each color is scaled so the total transmitted amount matches the texture's transparency
+ rgbTrans.set(ray.color.toVec3());
+ rgbTrans.scale(shouldTrans / colorTrans);
}
- }
- // Don't need to check for energy gain if transmissivity cap is 1
- if(transmissivityCap > 1) {
- double currentEnergy = rgbTrans.x * next.color.x + rgbTrans.y * next.color.y + rgbTrans.z * next.color.z;
- double nextEnergy = next.color.x + next.color.y + next.color.z;
- double energyRatio = nextEnergy / currentEnergy;
- // Normalize if there is net energy gain across all channels (more likely for higher transmissivityCap combined with high-saturation light source)
- if(energyRatio < 1) {
- rgbTrans.scale(energyRatio);
+ double transmissivityCap = scene.transmissivityCap;
+ // Determine the color with the highest transmissivity
+ double maxTrans = Math.max(rgbTrans.x, Math.max(rgbTrans.y, rgbTrans.z));
+ if (maxTrans > transmissivityCap) {
+ if (maxTrans == rgbTrans.x) {
+ // Give excess transmission from red to green and blue
+ double gTransNew = reassignTransmissivity(rgbTrans.x, rgbTrans.y, rgbTrans.z, shouldTrans, transmissivityCap);
+ rgbTrans.z = reassignTransmissivity(rgbTrans.x, rgbTrans.z, rgbTrans.y, shouldTrans, transmissivityCap);
+ rgbTrans.y = gTransNew;
+ rgbTrans.x = transmissivityCap;
+ } else if (maxTrans == rgbTrans.y) {
+ // Give excess transmission from green to red and blue
+ double rTransNew = reassignTransmissivity(rgbTrans.y, rgbTrans.x, rgbTrans.z, shouldTrans, transmissivityCap);
+ rgbTrans.z = reassignTransmissivity(rgbTrans.y, rgbTrans.z, rgbTrans.x, shouldTrans, transmissivityCap);
+ rgbTrans.x = rTransNew;
+ rgbTrans.y = transmissivityCap;
+ } else if (maxTrans == rgbTrans.z) {
+ // Give excess transmission from blue to green and red
+ double gTransNew = reassignTransmissivity(rgbTrans.z, rgbTrans.y, rgbTrans.x, shouldTrans, transmissivityCap);
+ rgbTrans.x = reassignTransmissivity(rgbTrans.z, rgbTrans.x, rgbTrans.y, shouldTrans, transmissivityCap);
+ rgbTrans.y = gTransNew;
+ rgbTrans.z = transmissivityCap;
+ }
}
+ // Don't need to check for energy gain if transmissivity cap is 1
+ if (transmissivityCap > 1) {
+ double currentEnergy = rgbTrans.x * next.color.x + rgbTrans.y * next.color.y + rgbTrans.z * next.color.z;
+ double nextEnergy = next.color.x + next.color.y + next.color.z;
+ double energyRatio = nextEnergy / currentEnergy;
+ // Normalize if there is net energy gain across all channels (more likely for higher transmissivityCap combined with high-saturation light source)
+ if (energyRatio < 1) {
+ rgbTrans.scale(energyRatio);
+ }
+ }
+ } else {
+ // Old method (see https://github.com/chunky-dev/chunky/pull/1513)
+ rgbTrans = new Vector3(1 - opacity, 1 - opacity, 1 - opacity);
+ rgbTrans.scaleAdd(opacity, ray.color.toVec3());
}
// Scale color based on next ray
Vector4 outputColor = new Vector4(0, 0, 0, 0);
diff --git a/chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java b/chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java
index 013d2c5a8d..245c67b531 100644
--- a/chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java
+++ b/chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java
@@ -211,6 +211,7 @@ public class Scene implements JsonSerializable, Refreshable {
protected boolean emittersEnabled = DEFAULT_EMITTERS_ENABLED;
protected double emitterIntensity = DEFAULT_EMITTER_INTENSITY;
protected EmitterSamplingStrategy emitterSamplingStrategy = EmitterSamplingStrategy.NONE;
+ protected boolean fancierTranslucency = true;
protected double transmissivityCap = DEFAULT_TRANSMISSIVITY_CAP;
protected SunSamplingStrategy sunSamplingStrategy = SunSamplingStrategy.FAST;
@@ -451,6 +452,7 @@ public synchronized void copyState(Scene other, boolean copyChunks) {
emitterIntensity = other.emitterIntensity;
emitterSamplingStrategy = other.emitterSamplingStrategy;
preventNormalEmitterWithSampling = other.preventNormalEmitterWithSampling;
+ fancierTranslucency = other.fancierTranslucency;
transmissivityCap = other.transmissivityCap;
transparentSky = other.transparentSky;
yClipMin = other.yClipMin;
@@ -2643,6 +2645,7 @@ public void setUseCustomWaterColor(boolean value) {
json.add("saveSnapshots", saveSnapshots);
json.add("emittersEnabled", emittersEnabled);
json.add("emitterIntensity", emitterIntensity);
+ json.add("fancierTranslucency", fancierTranslucency);
json.add("transmissivityCap", transmissivityCap);
json.add("sunSamplingStrategy", sunSamplingStrategy.getId());
json.add("stillWater", stillWater);
@@ -2902,6 +2905,7 @@ public synchronized void importFromJson(JsonObject json) {
saveSnapshots = json.get("saveSnapshots").boolValue(saveSnapshots);
emittersEnabled = json.get("emittersEnabled").boolValue(emittersEnabled);
emitterIntensity = json.get("emitterIntensity").doubleValue(emitterIntensity);
+ fancierTranslucency = json.get("fancierTranslucency").boolValue(fancierTranslucency);
transmissivityCap = json.get("transmissivityCap").doubleValue(transmissivityCap);
if (json.get("sunSamplingStrategy").isUnknown()) {
@@ -3410,6 +3414,14 @@ public boolean getHideUnknownBlocks() {
public void setHideUnknownBlocks(boolean hideUnknownBlocks) {
this.hideUnknownBlocks = hideUnknownBlocks;
}
+ public boolean getFancierTranslucency() {
+ return fancierTranslucency;
+ }
+
+ public void setFancierTranslucency(boolean value) {
+ fancierTranslucency = value;
+ refresh();
+ }
public double getTransmissivityCap() {
return transmissivityCap;
diff --git a/chunky/src/java/se/llbit/chunky/ui/render/tabs/AdvancedTab.java b/chunky/src/java/se/llbit/chunky/ui/render/tabs/AdvancedTab.java
index 083df12710..8dc587d111 100644
--- a/chunky/src/java/se/llbit/chunky/ui/render/tabs/AdvancedTab.java
+++ b/chunky/src/java/se/llbit/chunky/ui/render/tabs/AdvancedTab.java
@@ -68,6 +68,7 @@ public class AdvancedTab extends ScrollPane implements RenderControlsTab, Initia
@FXML private Button mergeRenderDump;
@FXML private CheckBox shutdown;
@FXML private CheckBox fastFog;
+ @FXML private CheckBox fancierTranslucency;
@FXML private DoubleAdjuster transmissivityCap;
@FXML private IntegerAdjuster cacheResolution;
@FXML private DoubleAdjuster animationTime;
@@ -155,6 +156,16 @@ public PictureExportFormat fromString(String string) {
fastFog.setTooltip(new Tooltip("Enable faster fog rendering algorithm."));
fastFog.selectedProperty()
.addListener((observable, oldValue, newValue) -> scene.setFastFog(newValue));
+ fancierTranslucency.setTooltip(new Tooltip("Enable more sophisticated algorithm for computing color changes through translucent materials."));
+ fancierTranslucency.selectedProperty()
+ .addListener((observable, oldValue, newValue) -> {
+ scene.setFancierTranslucency(newValue);
+ transmissivityCap.setVisible(newValue);
+ transmissivityCap.setManaged(newValue);
+ });
+ boolean tcapVisible = scene != null && scene.getFancierTranslucency();
+ transmissivityCap.setVisible(tcapVisible);
+ transmissivityCap.setManaged(tcapVisible);
transmissivityCap.setName("Transmissivity cap");
transmissivityCap.setRange(Scene.MIN_TRANSMISSIVITY_CAP, Scene.MAX_TRANSMISSIVITY_CAP);
transmissivityCap.clampBoth();
@@ -322,6 +333,7 @@ public boolean shutdownAfterCompletedRender() {
public void update(Scene scene) {
outputMode.getSelectionModel().select(scene.getOutputMode());
fastFog.setSelected(scene.fog.fastFog());
+ fancierTranslucency.setSelected(scene.getFancierTranslucency());
transmissivityCap.set(scene.getTransmissivityCap());
renderThreads.set(PersistentSettings.getNumThreads());
cpuLoad.set(PersistentSettings.getCPULoad());
diff --git a/chunky/src/res/se/llbit/chunky/ui/render/tabs/AdvancedTab.fxml b/chunky/src/res/se/llbit/chunky/ui/render/tabs/AdvancedTab.fxml
index f700dc7ac0..6c9b960236 100644
--- a/chunky/src/res/se/llbit/chunky/ui/render/tabs/AdvancedTab.fxml
+++ b/chunky/src/res/se/llbit/chunky/ui/render/tabs/AdvancedTab.fxml
@@ -24,6 +24,7 @@
+