diff --git a/chunky/src/java/se/llbit/chunky/renderer/SunSamplingStrategy.java b/chunky/src/java/se/llbit/chunky/renderer/SunSamplingStrategy.java index 48773593df..c235191871 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/SunSamplingStrategy.java +++ b/chunky/src/java/se/llbit/chunky/renderer/SunSamplingStrategy.java @@ -20,7 +20,6 @@ public enum SunSamplingStrategy implements Registerable { OFF("Off", "Sun is not sampled with next event estimation.", false, true, false, true), - NON_LUMINOUS("Non-Luminous", "Sun is drawn on the skybox but it does not contribute to the lighting of the scene.", false, false, false, false), FAST("Fast", "Fast sun sampling algorithm. Lower noise but does not correctly model some visual effects.", true, false, false, false), HIGH_QUALITY("High Quality", "High quality sun sampling. More noise but correctly models visual effects such as caustics.", true, true, true, true); 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 a373d6e7b6..097c0f7432 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/scene/PathTracer.java +++ b/chunky/src/java/se/llbit/chunky/renderer/scene/PathTracer.java @@ -73,18 +73,18 @@ public static boolean pathTrace(Scene scene, Ray ray, WorkerState state, int add } else if (ray.depth == 0) { // Direct sky hit. if (!scene.transparentSky()) { - scene.sky.getSkyColorInterpolated(ray); + scene.sky.getSkyColor(ray, true, false); addSkyFog(scene, ray, state, ox, od); hit = true; } } else if (ray.specular) { // Indirect sky hit - specular color. - scene.sky.getSkyColor(ray, true); + scene.sky.getSkyColor(ray, true, false); addSkyFog(scene, ray, state, ox, od); hit = true; } else { // Indirect sky hit - diffuse color. - scene.sky.getSkyColorDiffuseSun(ray, scene.getSunSamplingStrategy().isDiffuseSun()); + scene.sky.getSkyColor(ray, false, scene.getSunSamplingStrategy().isDiffuseSun()); // Skip sky fog - likely not noticeable in diffuse reflection. hit = true; } @@ -201,7 +201,7 @@ public static boolean pathTrace(Scene scene, Ray ray, WorkerState state, int add } } - if (scene.getSunSamplingStrategy().doSunSampling()) { + if (scene.getSunSamplingStrategy().doSunSampling() && scene.sun.sunlightEnabled()) { reflected.set(ray); scene.sun.getRandomSunDirection(reflected, random); diff --git a/chunky/src/java/se/llbit/chunky/renderer/scene/PreviewRayTracer.java b/chunky/src/java/se/llbit/chunky/renderer/scene/PreviewRayTracer.java index 264ec112df..daf2c5fbe7 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/scene/PreviewRayTracer.java +++ b/chunky/src/java/se/llbit/chunky/renderer/scene/PreviewRayTracer.java @@ -54,7 +54,7 @@ public class PreviewRayTracer implements RayTracer { } if (ray.getCurrentMaterial() == Air.INSTANCE) { - scene.sky.getApparentSkyColor(ray, true); + scene.sky.getSkyColor(ray, true, false); } else { scene.sun.flatShading(ray); } 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 19576fde14..895a7ead19 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java +++ b/chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java @@ -3016,15 +3016,7 @@ public synchronized void importFromJson(JsonObject json) { emitterIntensity = json.get("emitterIntensity").doubleValue(emitterIntensity); if (json.get("sunSamplingStrategy").isUnknown()) { - boolean sunSampling = json.get("sunEnabled").boolValue(false); - boolean drawSun = json.get("sun").asObject().get("drawTexture").boolValue(false); - if (drawSun) { - if (sunSampling) { - sunSamplingStrategy = SunSamplingStrategy.FAST; - } else { - sunSamplingStrategy = SunSamplingStrategy.NON_LUMINOUS; - } - } else { + if (json.get("sunEnabled").boolValue(false)) { sunSamplingStrategy = SunSamplingStrategy.FAST; } } else { @@ -3033,6 +3025,7 @@ public synchronized void importFromJson(JsonObject json) { if (json.get("sunEnabled").boolValue(false)) { sunSamplingStrategy = SunSamplingStrategy.FAST; + sun.setEnableSunlight(true); } else { sunSamplingStrategy = SunSamplingStrategy.valueOf(json.get("sunSamplingStrategy").asString(SunSamplingStrategy.FAST.getId())); } diff --git a/chunky/src/java/se/llbit/chunky/renderer/scene/Sky.java b/chunky/src/java/se/llbit/chunky/renderer/scene/Sky.java index d76200b14d..7448e70692 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/scene/Sky.java +++ b/chunky/src/java/se/llbit/chunky/renderer/scene/Sky.java @@ -170,6 +170,8 @@ public static SkyMode get(String name) { /** Current sky rendering mode. */ private SkyMode mode = SkyMode.DEFAULT; + private boolean enableSkymapInterpolation = true; + /** Simulated skies. */ public final static List skies = new ArrayList<>(); @@ -254,12 +256,13 @@ public void set(Sky other) { if (simulatedSkyMode.updateSun(scene.sun, horizonOffset)) { skyCache.precalculateSky(); } + enableSkymapInterpolation = other.enableSkymapInterpolation; } /** * Calculate sky color for the ray, based on sky mode. */ - public void getSkyDiffuseColorInner(Ray ray) { + private void getSkyDiffuseColorInner(Ray ray) { switch (mode) { case SOLID_COLOR: { ray.color.set(color.x, color.y, color.z, 1); @@ -360,26 +363,30 @@ public void getSkyDiffuseColorInner(Ray ray) { /** * Panoramic skymap color. */ - public void getSkyColor(Ray ray, boolean drawSun) { - getSkyDiffuseColorInner(ray); - ray.color.scale(skyExposure); - ray.color.scale(skyLightModifier); - if (drawSun) addSunColor(ray); - ray.color.w = 1; - } + public void getSkyColor(Ray ray, boolean isApparentColor, boolean isDiffuseSun) { + if (enableSkymapInterpolation) { + getSkyColorInterpolated(ray); + } else { + getSkyDiffuseColorInner(ray); + } - public void getApparentSkyColor(Ray ray, boolean drawSun) { - getSkyDiffuseColorInner(ray); ray.color.scale(skyExposure); - ray.color.scale(apparentSkyLightModifier); - if (drawSun) addSunColor(ray); + if (isApparentColor) { + ray.color.scale(apparentSkyLightModifier); + addSunColor(ray, false); + } else { + ray.color.scale(skyLightModifier); + if (isDiffuseSun) { + addSunColor(ray, true); + } + } ray.color.w = 1; } /** * Bilinear interpolated panoramic skymap color. */ - public void getSkyColorInterpolated(Ray ray) { + private void getSkyColorInterpolated(Ray ray) { switch (mode) { case SKYMAP_EQUIRECTANGULAR: { double x = rotation.transformX(ray.d); @@ -449,9 +456,6 @@ public void getSkyColorInterpolated(Ray ray) { getSkyDiffuseColorInner(ray); } } - ray.color.scale(skyExposure); - ray.color.scale(apparentSkyLightModifier); - addSunColor(ray); ray.color.w = 1; } @@ -459,39 +463,27 @@ public void getSkyColorInterpolated(Ray ray) { * Add sun color contribution. This does not alpha blend the sun color * because the Minecraft sun texture has no alpha channel. */ - private void addSunColor(Ray ray) { - double r = ray.color.x; - double g = ray.color.y; - double b = ray.color.z; - if (scene.sun().intersect(ray)) { - - // Blend sun color with current color. - ray.color.x = ray.color.x + r; - ray.color.y = ray.color.y + g; - ray.color.z = ray.color.z + b; - } - } - - public void getSkyColorDiffuseSun(Ray ray, boolean diffuseSun) { - getSkyDiffuseColorInner(ray); - ray.color.scale(skyExposure); - ray.color.scale(skyLightModifier); - if (diffuseSun) addSunColorDiffuseSun(ray); - ray.color.w = 1; - } - - public void addSunColorDiffuseSun(Ray ray) { + private void addSunColor(Ray ray, boolean isDiffuseSun) { double r = ray.color.x; double g = ray.color.y; double b = ray.color.z; + if (isDiffuseSun) { + if (scene.sun().intersectDiffuse(ray)) { + double mult = scene.sun().getLuminosity(); + + // Blend sun color with current color. + ray.color.x = ray.color.x * mult + r; + ray.color.y = ray.color.y * mult + g; + ray.color.z = ray.color.z * mult + b; + } + } else { + if (scene.sun().intersect(ray)) { - if (scene.sun().intersectDiffuse(ray)) { - double mult = scene.sun().getLuminosity(); - - // Blend sun color with current color. - ray.color.x = ray.color.x * mult + r; - ray.color.y = ray.color.y * mult + g; - ray.color.z = ray.color.z * mult + b; + // Blend sun color with current color. + ray.color.x = ray.color.x + r; + ray.color.y = ray.color.y + g; + ray.color.z = ray.color.z + b; + } } } @@ -673,6 +665,7 @@ public void setSkyCacheResolution(int resolution) { break; } } + sky.add("enableSkymapInterpolation", enableSkymapInterpolation); return sky; } @@ -749,6 +742,7 @@ public void importFromJson(JsonObject json) { default: break; } + enableSkymapInterpolation = json.get("enableSkymapInterpolation").boolValue(enableSkymapInterpolation); } private void updateTransform() { @@ -1178,4 +1172,13 @@ public void setColor(Vector3 color) { public Vector3 getColor() { return color; } + + public void setEnableSkymapInterpolation(boolean value) { + enableSkymapInterpolation = value; + scene.refresh(); + } + + public boolean getEnableSkymapInterpolation() { + return enableSkymapInterpolation; + } } diff --git a/chunky/src/java/se/llbit/chunky/renderer/scene/Sun.java b/chunky/src/java/se/llbit/chunky/renderer/scene/Sun.java index a368620aa4..0934a20ac8 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/scene/Sun.java +++ b/chunky/src/java/se/llbit/chunky/renderer/scene/Sun.java @@ -160,6 +160,8 @@ public class Sun implements JsonSerializable { private final Vector3 apparentColor = new Vector3(1, 1, 1); + private boolean enableSunlight = true; + private boolean drawTexture = true; private double chroma(double turb, double turb2, double sunTheta, double[][] matrix) { @@ -195,6 +197,7 @@ public void set(Sun other) { altitude = other.altitude; color.set(other.color); apparentColor.set(other.apparentColor); + enableSunlight = other.enableSunlight; drawTexture = other.drawTexture; intensity = other.intensity; luminosity = other.luminosity; @@ -281,33 +284,18 @@ public double getAzimuth() { * @return true if the ray intersects the sun model */ public boolean intersect(Ray ray) { - if (!drawTexture || ray.d.dot(sw) < .5) { - return false; - } - - double width = radius * 4; - double width2 = width * 2; - double a; - a = Math.PI / 2 - FastMath.acos(ray.d.dot(su)) + width; - if (a >= 0 && a < width2) { - double b = Math.PI / 2 - FastMath.acos(ray.d.dot(sv)) + width; - if (b >= 0 && b < width2) { - texture.getColor(a / width2, b / width2, ray.color); - ray.color.x *= apparentTextureBrightness.x * 10; - ray.color.y *= apparentTextureBrightness.y * 10; - ray.color.z *= apparentTextureBrightness.z * 10; - return true; - } - } - - return false; + return doIntersect(ray, apparentTextureBrightness, false); } /** * Used with SSS: OFF and SSS: HIGH_QUALITY. */ public boolean intersectDiffuse(Ray ray) { - if (ray.d.dot(sw) < .5) { + return doIntersect(ray, color, true); + } + + private boolean doIntersect(Ray ray, Vector3 color, boolean isDiffuse) { + if ((isDiffuse && !enableSunlight) || (!isDiffuse && !drawTexture) || ray.d.dot(sw) < .5) { return false; } @@ -475,6 +463,7 @@ public void getRandomSunDirection(Ray reflected, Random random) { apparentColorObj.add("green", apparentColor.y); apparentColorObj.add("blue", apparentColor.z); sun.add("apparentColor", apparentColorObj); + sun.add("enableSunlight", enableSunlight); sun.add("drawTexture", drawTexture); return sun; } @@ -502,6 +491,10 @@ public void importFromJson(JsonObject json) { apparentColor.z = apparentColorObj.get("blue").doubleValue(1); } + if (!json.get("enableSunlight").isUnknown()) { + enableSunlight = json.get("enableSunlight").boolValue(enableSunlight); + } + drawTexture = json.get("drawTexture").boolValue(drawTexture); initSun(); @@ -518,6 +511,17 @@ public Vector3 getApparentColor() { return apparentColor; } + public void setEnableSunlight(boolean value) { + if (value != enableSunlight) { + enableSunlight = value; + scene.refresh(); + } + } + + public boolean sunlightEnabled() { + return enableSunlight; + } + public void setDrawTexture(boolean value) { if (value != drawTexture) { drawTexture = value; 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 b739198452..e0837e0ce0 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 @@ -67,6 +67,7 @@ public class AdvancedTab extends ScrollPane implements RenderControlsTab, Initia @FXML private Button mergeRenderDump; @FXML private CheckBox shutdown; @FXML private CheckBox fastFog; + @FXML private CheckBox enableSkymapInterpolation; @FXML private IntegerAdjuster cacheResolution; @FXML private DoubleAdjuster animationTime; @FXML private ChoiceBox outputMode; @@ -141,6 +142,8 @@ public PictureExportFormat fromString(String string) { fastFog.setTooltip(new Tooltip("Enable faster fog rendering algorithm.")); fastFog.selectedProperty() .addListener((observable, oldValue, newValue) -> scene.setFastFog(newValue)); + enableSkymapInterpolation.setTooltip(new Tooltip("Enable interpolation of the skymap / skybox texture, if a skymap / skybox is being used.")); + enableSkymapInterpolation.selectedProperty().addListener((observable, oldValue, newValue) -> scene.sky().setEnableSkymapInterpolation(newValue)); cacheResolution.setName("Sky cache resolution"); cacheResolution.setTooltip("Resolution of the sky cache. Lower values will use less memory and improve performance but can cause sky artifacts."); cacheResolution.setRange(1, 4096); @@ -301,6 +304,7 @@ public boolean shutdownAfterCompletedRender() { public void update(Scene scene) { outputMode.getSelectionModel().select(scene.getOutputMode()); fastFog.setSelected(scene.fog.fastFog()); + enableSkymapInterpolation.setSelected(scene.sky().getEnableSkymapInterpolation()); renderThreads.set(PersistentSettings.getNumThreads()); cpuLoad.set(PersistentSettings.getCPULoad()); rayDepth.set(scene.getRayDepth()); diff --git a/chunky/src/java/se/llbit/chunky/ui/render/tabs/LightingTab.java b/chunky/src/java/se/llbit/chunky/ui/render/tabs/LightingTab.java index bedf302107..12dd3a8f6c 100644 --- a/chunky/src/java/se/llbit/chunky/ui/render/tabs/LightingTab.java +++ b/chunky/src/java/se/llbit/chunky/ui/render/tabs/LightingTab.java @@ -54,6 +54,7 @@ public class LightingTab extends ScrollPane implements RenderControlsTab, Initia @FXML private DoubleAdjuster emitterIntensity; @FXML private DoubleAdjuster sunIntensity; @FXML private CheckBox drawSun; + @FXML private CheckBox enableSunlight; @FXML private ComboBox sunSamplingStrategy; @FXML private DoubleAdjuster sunLuminosity; @FXML private DoubleAdjuster apparentSunBrightness; @@ -129,9 +130,16 @@ public LightingTab() throws IOException { }); emitterSamplingStrategy.setTooltip(new Tooltip("Determine how emitters are sampled at each bounce.")); + enableSunlight.selectedProperty().addListener((observable, oldValue, newValue) -> { + scene.sun().setEnableSunlight(newValue); + sunSamplingStrategy.setDisable(!newValue); + }); + enableSunlight.setTooltip(new Tooltip("Changes whether the sun emits light.")); + drawSun.selectedProperty().addListener((observable, oldValue, newValue) -> scene.sun().setDrawTexture(newValue)); drawSun.setTooltip(new Tooltip("Draws the sun texture on top of the skymap.")); + sunSamplingStrategy.setDisable(!enableSunlight.isSelected()); sunSamplingStrategy.getItems().addAll(SunSamplingStrategy.values()); sunSamplingStrategy.getSelectionModel().selectedItemProperty().addListener( (observable, oldValue, newValue) -> scene.setSunSamplingStrategy(newValue)); @@ -203,6 +211,7 @@ public void setController(RenderControlsFxController controller) { sunAltitude.set(QuickMath.radToDeg(scene.sun().getAltitude())); enableEmitters.setSelected(scene.getEmittersEnabled()); sunSamplingStrategy.getSelectionModel().select(scene.getSunSamplingStrategy()); + enableSunlight.setSelected(scene.sun().sunlightEnabled()); drawSun.setSelected(scene.sun().drawTexture()); sunColor.colorProperty().removeListener(sunColorListener); sunColor.setColor(ColorUtil.toFx(scene.sun().getColor())); 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 cf56592182..2057f4134d 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 @@ -23,6 +23,7 @@ + diff --git a/chunky/src/res/se/llbit/chunky/ui/render/tabs/LightingTab.fxml b/chunky/src/res/se/llbit/chunky/ui/render/tabs/LightingTab.fxml index 63708d2812..9e53cf8d1b 100644 --- a/chunky/src/res/se/llbit/chunky/ui/render/tabs/LightingTab.fxml +++ b/chunky/src/res/se/llbit/chunky/ui/render/tabs/LightingTab.fxml @@ -26,6 +26,7 @@ +