Skip to content
Draft
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
55 changes: 23 additions & 32 deletions chunky/src/java/se/llbit/chunky/renderer/scene/PathTracer.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
*/
package se.llbit.chunky.renderer.scene;

import org.apache.commons.math3.util.FastMath;
import se.llbit.chunky.block.minecraft.Air;
import se.llbit.chunky.block.minecraft.Water;
import se.llbit.chunky.renderer.EmitterSamplingStrategy;
Expand Down Expand Up @@ -226,6 +225,16 @@ private static boolean doDiffuseReflection(Ray ray, Ray next, Material currentMa
Vector3 emittance = new Vector3();
Vector4 indirectEmitterColor = new Vector4(0, 0, 0, 0);

float pTransmit = currentMat.additionalTransmission;

boolean transmitBack = pTransmit > Ray.EPSILON && random.nextFloat() < pTransmit;

double eventProb = (transmitBack ? pTransmit: 1- pTransmit ) + Ray.EPSILON;

if (transmitBack) {
ray.invertNormal();
}

if (scene.emittersEnabled && (!scene.isPreventNormalEmitterWithSampling() || scene.getEmitterSamplingStrategy() == EmitterSamplingStrategy.NONE || ray.depth == 0) && currentMat.emittance > Ray.EPSILON) {

// Quadratic emittance mapping, so a pixel that's 50% darker will emit only 25% as much light
Expand Down Expand Up @@ -265,8 +274,8 @@ private static boolean doDiffuseReflection(Ray ray, Ray next, Material currentMa
double directLightB = 0;

boolean frontLight = next.d.dot(ray.getNormal()) > 0;

if (frontLight || (currentMat.subSurfaceScattering
//check if normal faces the sun direction, if so do sampling
if (frontLight || (false
&& random.nextFloat() < Scene.fSubSurface)) {

if (!frontLight) {
Expand All @@ -287,7 +296,7 @@ private static boolean doDiffuseReflection(Ray ray, Ray next, Material currentMa
}
}

next.diffuseReflection(ray, random);
next.diffuseLobes(ray, random, transmitBack);
hit = pathTrace(scene, next, state, false) || hit;
if (hit) {
cumulativeColor.x += emittance.x + ray.color.x * (directLightR * scene.sun.emittance.x + next.color.x + indirectEmitterColor.x);
Expand All @@ -301,7 +310,7 @@ private static boolean doDiffuseReflection(Ray ray, Ray next, Material currentMa
}

} else {
next.diffuseReflection(ray, random);
next.diffuseLobes(ray, random, transmitBack);

hit = pathTrace(scene, next, state, false) || hit;
if (hit) {
Expand All @@ -315,6 +324,10 @@ private static boolean doDiffuseReflection(Ray ray, Ray next, Material currentMa
cumulativeColor.z += ray.color.z * indirectEmitterColor.z;
}
}
//fix the normal if inverted for use in other things
if (transmitBack) {
ray.invertNormal();
}
return hit;
}

Expand Down Expand Up @@ -359,31 +372,7 @@ private static boolean doRefraction(Ray ray, Ray next, Material currentMat, Mate
}
} else {
if (doRefraction) {

double t2 = FastMath.sqrt(radicand);
Vector3 n = ray.getNormal();
if (cosTheta > 0) {
next.d.x = n1n2 * ray.d.x + (n1n2 * cosTheta - t2) * n.x;
next.d.y = n1n2 * ray.d.y + (n1n2 * cosTheta - t2) * n.y;
next.d.z = n1n2 * ray.d.z + (n1n2 * cosTheta - t2) * n.z;
} else {
next.d.x = n1n2 * ray.d.x - (-n1n2 * cosTheta - t2) * n.x;
next.d.y = n1n2 * ray.d.y - (-n1n2 * cosTheta - t2) * n.y;
next.d.z = n1n2 * ray.d.z - (-n1n2 * cosTheta - t2) * n.z;
}

next.d.normalize();

// See Ray.specularReflection for information on why this is needed
// This is the same thing but for refraction instead of reflection
// so this time we want the signs of the dot product to be the same
if (QuickMath.signum(next.getGeometryNormal().dot(next.d)) != QuickMath.signum(next.getGeometryNormal().dot(ray.d))) {
double factor = QuickMath.signum(next.getGeometryNormal().dot(ray.d)) * -Ray.EPSILON - next.d.dot(next.getGeometryNormal());
next.d.scaleAdd(factor, next.getGeometryNormal());
next.d.normalize();
}

next.o.scaleAdd(Ray.OFFSET, next.d);
next.specularRefraction(ray, random, radicand, n1n2, cosTheta);
}

if (pathTrace(scene, next, state, false)) {
Expand Down Expand Up @@ -554,12 +543,14 @@ public static void getDirectLightAttenuation(Scene scene, Ray ray, WorkerState s
attenuation.y = 1;
attenuation.z = 1;
attenuation.w = 1;
while (attenuation.w > 0) {
while (attenuation.w > Ray.EPSILON) {
ray.o.scaleAdd(Ray.OFFSET, ray.d);
if (!PreviewRayTracer.nextIntersection(scene, ray)) {
break;
}
double mult = 1 - ray.color.w;
Material mat = ray.getCurrentMaterial();
double pDiffuse = scene.fancierTranslucency ? 1 - Math.sqrt(1 - ray.color.w) : ray.color.w;
double mult = 1 - pDiffuse*(1 -Math.pow(mat.additionalTransmission, 1));
attenuation.x *= ray.color.x * ray.color.w + mult;
attenuation.y *= ray.color.y * ray.color.w + mult;
attenuation.z *= ray.color.z * ray.color.w + mult;
Expand Down
20 changes: 20 additions & 0 deletions chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java
Original file line number Diff line number Diff line change
Expand Up @@ -3201,6 +3201,16 @@ public void setPerceptualSmoothness(String materialName, float value) {
refresh(ResetReason.MATERIALS_CHANGED);
}

/**
* Modifies the transmission roughness property for the given material.
*/
public void setPerceptualTransmissionSmoothness(String materialName, float value) {
JsonObject material = materials.getOrDefault(materialName, new JsonObject()).object();
material.set("transmissionRoughness", Json.of(Math.pow(1 - value, 2)));
materials.put(materialName, material);
refresh(ResetReason.MATERIALS_CHANGED);
}

/**
* Modifies the metalness property for the given material.
*/
Expand All @@ -3211,6 +3221,16 @@ public void setMetalness(String materialName, float value) {
refresh(ResetReason.MATERIALS_CHANGED);
}

/**
* Modifies the additional transmission through diffuse property for the given material.
*/
public void setAdditionalTransmission(String materialName, float value) {
JsonObject material = materials.getOrDefault(materialName, new JsonObject()).object();
material.set("additionalTransmission", Json.of(value));
materials.put(materialName, material);
refresh(ResetReason.MATERIALS_CHANGED);
}

public int getYClipMin() {
return yClipMin;
}
Expand Down
28 changes: 27 additions & 1 deletion chunky/src/java/se/llbit/chunky/ui/render/tabs/MaterialsTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ public class MaterialsTab extends HBox implements RenderControlsTab, Initializab
private final DoubleAdjuster specular = new DoubleAdjuster();
private final DoubleAdjuster ior = new DoubleAdjuster();
private final DoubleAdjuster perceptualSmoothness = new DoubleAdjuster();

private final DoubleAdjuster perceptualTransmissionSmoothness = new DoubleAdjuster();
private final DoubleAdjuster metalness = new DoubleAdjuster();

private final DoubleAdjuster additionalTransmission = new DoubleAdjuster();
private final ListView<String> listView;

public MaterialsTab() {
Expand All @@ -69,9 +73,15 @@ public MaterialsTab() {
perceptualSmoothness.setName("Smoothness");
perceptualSmoothness.setRange(0, 1);
perceptualSmoothness.setTooltip("Smoothness of the selected material.");
perceptualTransmissionSmoothness.setName("Transmission Smoothness");
perceptualTransmissionSmoothness.setRange(0, 1);
perceptualTransmissionSmoothness.setTooltip("Smoothness of refraction though material");
metalness.setName("Metalness");
metalness.setRange(0, 1);
metalness.setTooltip("Metalness (texture-tinted reflectivity) of the selected material.");
additionalTransmission.setName("Additional Transmission");
additionalTransmission.setRange(0, 1);
additionalTransmission.setTooltip("Amount of transmitted light to back in diffuse component");
ObservableList<String> blockIds = FXCollections.observableArrayList();
blockIds.addAll(MaterialStore.collections.keySet());
blockIds.addAll(ExtraMaterials.idMap.keySet());
Expand All @@ -87,7 +97,8 @@ public MaterialsTab() {
settings.setSpacing(10);
settings.getChildren().addAll(
new Label("Material Properties"),
emittance, specular, perceptualSmoothness, ior, metalness,
emittance, specular, perceptualSmoothness, ior,perceptualTransmissionSmoothness, metalness,
additionalTransmission,
new Label("(set to zero to disable)"));
setPadding(new Insets(10));
setSpacing(15);
Expand Down Expand Up @@ -116,20 +127,26 @@ private void updateSelectedMaterial(String materialName) {
double specAcc = 0;
double iorAcc = 0;
double perceptualSmoothnessAcc = 0;
double perceptualTransmissionSmoothnessAcc = 0;
double metalnessAcc = 0;
double additionalTransmissionAcc = 0;
Collection<Block> blocks = MaterialStore.collections.get(materialName);
for (Block block : blocks) {
emAcc += block.emittance;
specAcc += block.specular;
iorAcc += block.ior;
perceptualSmoothnessAcc += block.getPerceptualSmoothness();
perceptualTransmissionSmoothnessAcc += block.getPerceptualTransmissionSmoothness();
metalnessAcc += block.metalness;
additionalTransmissionAcc += block.additionalTransmission;
}
emittance.set(emAcc / blocks.size());
specular.set(specAcc / blocks.size());
ior.set(iorAcc / blocks.size());
perceptualSmoothness.set(perceptualSmoothnessAcc / blocks.size());
perceptualTransmissionSmoothness.set(perceptualTransmissionSmoothnessAcc / blocks.size());
metalness.set(metalnessAcc / blocks.size());
additionalTransmission.set(additionalTransmissionAcc/blocks.size());
materialExists = true;
} else if (ExtraMaterials.idMap.containsKey(materialName)) {
Material material = ExtraMaterials.idMap.get(materialName);
Expand All @@ -138,7 +155,9 @@ private void updateSelectedMaterial(String materialName) {
specular.set(material.specular);
ior.set(material.ior);
perceptualSmoothness.set(material.getPerceptualSmoothness());
perceptualTransmissionSmoothness.set(material.getPerceptualTransmissionSmoothness());
metalness.set(material.metalness);
additionalTransmission.set(material.additionalTransmission);
materialExists = true;
}
} else if (MaterialStore.blockIds.contains(materialName)) {
Expand All @@ -148,21 +167,28 @@ private void updateSelectedMaterial(String materialName) {
specular.set(block.specular);
ior.set(block.ior);
perceptualSmoothness.set(block.getPerceptualSmoothness());
perceptualTransmissionSmoothness.set(block.getPerceptualTransmissionSmoothness());
metalness.set(block.metalness);
additionalTransmission.set(block.additionalTransmission);
materialExists = true;
}
if (materialExists) {
emittance.onValueChange(value -> scene.setEmittance(materialName, value.floatValue()));
specular.onValueChange(value -> scene.setSpecular(materialName, value.floatValue()));
ior.onValueChange(value -> scene.setIor(materialName, value.floatValue()));
perceptualSmoothness.onValueChange(value -> scene.setPerceptualSmoothness(materialName, value.floatValue()));
perceptualTransmissionSmoothness.onValueChange(value -> scene.setPerceptualTransmissionSmoothness(materialName,
value.floatValue()));
metalness.onValueChange(value -> scene.setMetalness(materialName, value.floatValue()));
additionalTransmission.onValueChange(value -> scene.setAdditionalTransmission(materialName, value.floatValue()));
} else {
emittance.onValueChange(value -> {});
specular.onValueChange(value -> {});
ior.onValueChange(value -> {});
perceptualSmoothness.onValueChange(value -> {});
perceptualTransmissionSmoothness.onValueChange(value -> {});
metalness.onValueChange(value -> {});
additionalTransmission.onValueChange(value -> {});
}
}

Expand Down
25 changes: 22 additions & 3 deletions chunky/src/java/se/llbit/chunky/world/Material.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ public abstract class Material {
*/
public float roughness = 0f;

/**
* The (linear) roughness controlling how blurry a refraction/transmission though a block is. A value of 0 makes the
* effect perfectly specular, a value of 1 makes it diffuse.
*/
public float transmissionRoughness = 0f;

/**
* The metalness value controls how metal-y a block appears. In reality this is a boolean value
* but in practice usually a float is used in PBR to allow adding dirt or scratches on metals
Expand All @@ -76,9 +82,11 @@ public abstract class Material {
public float metalness = 0;

/**
* Subsurface scattering property.
* Additional amount of transmission to do for a given mat, for opaque materials this provides a first order
* approximation to subsurface scattering
* #TODO: figure out if to take a chunk of outgoing energy or use up left over energy
*/
public boolean subSurfaceScattering = false;
public float additionalTransmission = 0f;

/**
* Base texture.
Expand All @@ -104,7 +112,8 @@ public void restoreDefaults() {
specular = 0;
emittance = 0;
roughness = 0;
subSurfaceScattering = false;
transmissionRoughness = 0;
additionalTransmission = 0f;
}

public void getColor(Ray ray) {
Expand All @@ -125,6 +134,8 @@ public void loadMaterialProperties(JsonObject json) {
emittance = json.get("emittance").floatValue(emittance);
roughness = json.get("roughness").floatValue(roughness);
metalness = json.get("metalness").floatValue(metalness);
transmissionRoughness = json.get("transmissionRoughness").floatValue(transmissionRoughness);
additionalTransmission = json.get("additionalTransmission").floatValue(additionalTransmission);
}

public boolean isWater() {
Expand All @@ -146,4 +157,12 @@ public double getPerceptualSmoothness() {
public void setPerceptualSmoothness(double perceptualSmoothness) {
roughness = (float) Math.pow(1 - perceptualSmoothness, 2);
}

public double getPerceptualTransmissionSmoothness() {
return 1 - Math.sqrt(transmissionRoughness);
}

public void setPerceptualTransmissionSmoothness(double perceptualSmoothness) {
transmissionRoughness = (float) Math.pow(1 - perceptualSmoothness, 2);
}
}
Loading