From 24eda0e31e5e7c9d4f5a3523d9261ddcf0887ad0 Mon Sep 17 00:00:00 2001 From: Justin Barker <46458276+JustinTimeCuber@users.noreply.github.com> Date: Wed, 1 Nov 2023 18:09:42 -0500 Subject: [PATCH] Simplify emitter logic and make it more correct (#1621) * kill ray.emittance (good riddance) * remove special case for torches so they don't look ridiculous * rearrange algebra to improve readability --- .../se/llbit/chunky/chunk/BlockPalette.java | 12 ++-- .../chunky/renderer/scene/PathTracer.java | 61 +++++++------------ chunky/src/java/se/llbit/math/Ray.java | 7 --- 3 files changed, 28 insertions(+), 52 deletions(-) diff --git a/chunky/src/java/se/llbit/chunky/chunk/BlockPalette.java b/chunky/src/java/se/llbit/chunky/chunk/BlockPalette.java index 93394c1338..3a39859f5d 100644 --- a/chunky/src/java/se/llbit/chunky/chunk/BlockPalette.java +++ b/chunky/src/java/se/llbit/chunky/chunk/BlockPalette.java @@ -348,10 +348,10 @@ public static Map> getDefaultMaterialProperties() { } }); materialProperties.put("minecraft:torch", block -> { - block.emittance = 50.0f; + block.emittance = 1.0f; }); materialProperties.put("minecraft:wall_torch", block -> { - block.emittance = 50.0f; + block.emittance = 1.0f; }); materialProperties.put("minecraft:fire", block -> { block.emittance = 1.0f; @@ -449,16 +449,16 @@ public static Map> getDefaultMaterialProperties() { block.emittance = 0.6f; }); materialProperties.put("minecraft:soul_fire_torch", block -> { // MC 20w06a-20w16a - block.emittance = 35.0f; + block.emittance = 0.6f; }); materialProperties.put("minecraft:soul_torch", block -> { // MC >= 20w17a - block.emittance = 35.0f; + block.emittance = 0.6f; }); materialProperties.put("minecraft:soul_fire_wall_torch", block -> { // MC 20w06a-20w16a - block.emittance = 35.0f; + block.emittance = 0.6f; }); materialProperties.put("minecraft:soul_wall_torch", block -> { // MC >= 20w17a - block.emittance = 35.0f; + block.emittance = 0.6f; }); materialProperties.put("minecraft:soul_fire", block -> { block.emittance = 0.6f; 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 1e2127593a..277f8ec3f0 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/scene/PathTracer.java +++ b/chunky/src/java/se/llbit/chunky/renderer/scene/PathTracer.java @@ -45,7 +45,7 @@ public class PathTracer implements RayTracer { } else { ray.setCurrentMaterial(Air.INSTANCE); } - pathTrace(scene, ray, state, 1, true); + pathTrace(scene, ray, state, true); } /** @@ -54,7 +54,7 @@ public class PathTracer implements RayTracer { * @param firstReflection {@code true} if the ray has not yet hit the first * diffuse or specular reflection */ - public static boolean pathTrace(Scene scene, Ray ray, WorkerState state, int addEmitted, + public static boolean pathTrace(Scene scene, Ray ray, WorkerState state, boolean firstReflection) { boolean hit = false; @@ -141,7 +141,7 @@ public static boolean pathTrace(Scene scene, Ray ray, WorkerState state, int add if (doMetal || (pSpecular > Ray.EPSILON && random.nextFloat() < pSpecular)) { hit |= doSpecularReflection(ray, next, cumulativeColor, doMetal, random, state, scene); } else if(random.nextFloat() < pDiffuse) { - hit |= doDiffuseReflection(ray, next, currentMat, cumulativeColor, addEmitted, random, state, scene); + hit |= doDiffuseReflection(ray, next, currentMat, cumulativeColor, random, state, scene); } else if (n1 != n2) { hit |= doRefraction(ray, next, currentMat, prevMat, cumulativeColor, n1, n2, pDiffuse, random, state, scene); } else { @@ -204,10 +204,7 @@ public static boolean pathTrace(Scene scene, Ray ray, WorkerState state, int add private static boolean doSpecularReflection(Ray ray, Ray next, Vector4 cumulativeColor, boolean doMetal, Random random, WorkerState state, Scene scene) { boolean hit = false; next.specularReflection(ray, random); - if (pathTrace(scene, next, state, 1, false)) { - ray.emittance.x = ray.color.x * next.emittance.x; - ray.emittance.y = ray.color.y * next.emittance.y; - ray.emittance.z = ray.color.z * next.emittance.z; + if (pathTrace(scene, next, state, false)) { if (doMetal) { // use the albedo color as specular color @@ -224,20 +221,17 @@ private static boolean doSpecularReflection(Ray ray, Ray next, Vector4 cumulativ return hit; } - private static boolean doDiffuseReflection(Ray ray, Ray next, Material currentMat, Vector4 cumulativeColor, int addEmitted, Random random, WorkerState state, Scene scene) { + private static boolean doDiffuseReflection(Ray ray, Ray next, Material currentMat, Vector4 cumulativeColor, Random random, WorkerState state, Scene scene) { boolean hit = false; - float emittance = 0; + Vector3 emittance = new Vector3(); Vector4 indirectEmitterColor = new Vector4(0, 0, 0, 0); if (scene.emittersEnabled && (!scene.isPreventNormalEmitterWithSampling() || scene.getEmitterSamplingStrategy() == EmitterSamplingStrategy.NONE || ray.depth == 0) && currentMat.emittance > Ray.EPSILON) { - emittance = addEmitted; - ray.emittance.x = ray.color.x * ray.color.x * - currentMat.emittance * scene.emitterIntensity; - ray.emittance.y = ray.color.y * ray.color.y * - currentMat.emittance * scene.emitterIntensity; - ray.emittance.z = ray.color.z * ray.color.z * - currentMat.emittance * scene.emitterIntensity; + // Quadratic emittance mapping, so a pixel that's 50% darker will emit only 25% as much light + // This is arbitrary but gives pretty good results in most cases. + emittance = new Vector3(ray.color.x * ray.color.x, ray.color.y * ray.color.y, ray.color.z * ray.color.z); + emittance.scale(currentMat.emittance * scene.emitterIntensity); hit = true; } else if (scene.emittersEnabled && scene.emitterSamplingStrategy != EmitterSamplingStrategy.NONE && scene.getEmitterGrid() != null) { @@ -294,14 +288,11 @@ private static boolean doDiffuseReflection(Ray ray, Ray next, Material currentMa } next.diffuseReflection(ray, random); - hit = pathTrace(scene, next, state, 0, false) || hit; + hit = pathTrace(scene, next, state, false) || hit; if (hit) { - cumulativeColor.x += ray.color.x * (emittance + directLightR * scene.sun.emittance.x + ( - next.color.x + next.emittance.x) + (indirectEmitterColor.x)); - cumulativeColor.y += ray.color.y * (emittance + directLightG * scene.sun.emittance.y + ( - next.color.y + next.emittance.y) + (indirectEmitterColor.y)); - cumulativeColor.z += ray.color.z * (emittance + directLightB * scene.sun.emittance.z + ( - next.color.z + next.emittance.z) + (indirectEmitterColor.z)); + cumulativeColor.x += emittance.x + ray.color.x * (directLightR * scene.sun.emittance.x + next.color.x + indirectEmitterColor.x); + cumulativeColor.y += emittance.y + ray.color.y * (directLightG * scene.sun.emittance.y + next.color.y + indirectEmitterColor.y); + cumulativeColor.z += emittance.z + ray.color.z * (directLightB * scene.sun.emittance.z + next.color.z + indirectEmitterColor.z); } else if (indirectEmitterColor.x > Ray.EPSILON || indirectEmitterColor.y > Ray.EPSILON || indirectEmitterColor.z > Ray.EPSILON) { hit = true; cumulativeColor.x += ray.color.x * indirectEmitterColor.x; @@ -312,11 +303,11 @@ private static boolean doDiffuseReflection(Ray ray, Ray next, Material currentMa } else { next.diffuseReflection(ray, random); - hit = pathTrace(scene, next, state, 0, false) || hit; + hit = pathTrace(scene, next, state, false) || hit; if (hit) { - cumulativeColor.x += ray.color.x * (emittance + (next.color.x + next.emittance.x) + (indirectEmitterColor.x)); - cumulativeColor.y += ray.color.y * (emittance + (next.color.y + next.emittance.y) + (indirectEmitterColor.y)); - cumulativeColor.z += ray.color.z * (emittance + (next.color.z + next.emittance.z) + (indirectEmitterColor.z)); + cumulativeColor.x += emittance.x + ray.color.x * (next.color.x + indirectEmitterColor.x); + cumulativeColor.y += emittance.y + ray.color.y * (next.color.y + indirectEmitterColor.y); + cumulativeColor.z += emittance.z + ray.color.z * (next.color.z + indirectEmitterColor.z); } else if (indirectEmitterColor.x > Ray.EPSILON || indirectEmitterColor.y > Ray.EPSILON || indirectEmitterColor.z > Ray.EPSILON) { hit = true; cumulativeColor.x += ray.color.x * indirectEmitterColor.x; @@ -338,10 +329,7 @@ private static boolean doRefraction(Ray ray, Ray next, Material currentMat, Mate if (doRefraction && radicand < Ray.EPSILON) { // Total internal reflection. next.specularReflection(ray, random); - if (pathTrace(scene, next, state, 1, false)) { - ray.emittance.x = ray.color.x * next.emittance.x; - ray.emittance.y = ray.color.y * next.emittance.y; - ray.emittance.z = ray.color.z * next.emittance.z; + if (pathTrace(scene, next, state, false)) { cumulativeColor.x += next.color.x; cumulativeColor.y += next.color.y; @@ -362,10 +350,7 @@ private static boolean doRefraction(Ray ray, Ray next, Material currentMat, Mate if (random.nextFloat() < Rtheta) { next.specularReflection(ray, random); - if (pathTrace(scene, next, state, 1, false)) { - ray.emittance.x = ray.color.x * next.emittance.x; - ray.emittance.y = ray.color.y * next.emittance.y; - ray.emittance.z = ray.color.z * next.emittance.z; + if (pathTrace(scene, next, state, false)) { cumulativeColor.x += next.color.x; cumulativeColor.y += next.color.y; @@ -401,7 +386,7 @@ private static boolean doRefraction(Ray ray, Ray next, Material currentMat, Mate next.o.scaleAdd(Ray.OFFSET, next.d); } - if (pathTrace(scene, next, state, 1, false)) { + if (pathTrace(scene, next, state, false)) { // Calculate the color and emittance of the refracted ray translucentRayColor(scene, ray, next, cumulativeColor, pDiffuse); hit = true; @@ -416,7 +401,7 @@ private static boolean doTransmission(Ray ray, Ray next, Vector4 cumulativeColor next.set(ray); next.o.scaleAdd(Ray.OFFSET, next.d); - if (pathTrace(scene, next, state, 1, false)) { + if (pathTrace(scene, next, state, false)) { // Calculate the color and emittance of the refracted ray translucentRayColor(scene, ray, next, cumulativeColor, pDiffuse); hit = true; @@ -481,8 +466,6 @@ private static void translucentRayColor(Scene scene, Ray ray, Ray next, Vector4 Vector4 outputColor = new Vector4(0, 0, 0, 0); outputColor.multiplyEntrywise(new Vector4(rgbTrans, 1), next.color); cumulativeColor.add(outputColor); - // Use emittance from next ray - ray.emittance.multiplyEntrywise(rgbTrans, next.emittance); } private static double reassignTransmissivity(double from, double to, double other, double trans, double cap) { diff --git a/chunky/src/java/se/llbit/math/Ray.java b/chunky/src/java/se/llbit/math/Ray.java index 8539bb9ed7..11deb1e4f4 100644 --- a/chunky/src/java/se/llbit/math/Ray.java +++ b/chunky/src/java/se/llbit/math/Ray.java @@ -68,11 +68,6 @@ public class Ray { */ public Vector4 color = new Vector4(); - /** - * Emittance of previously intersected surface. - */ - public Vector3 emittance = new Vector3(); - /** * Previous material. */ @@ -149,7 +144,6 @@ public void setDefault() { currentMaterial = Air.INSTANCE; depth = 0; color.set(0, 0, 0, 0); - emittance.set(0, 0, 0); specular = true; } @@ -166,7 +160,6 @@ public void set(Ray other) { n.set(other.n); geomN.set(other.geomN); color.set(0, 0, 0, 0); - emittance.set(0, 0, 0); specular = other.specular; }