Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apparent emitter brightness #3

Closed
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
34 changes: 22 additions & 12 deletions chunky/src/java/se/llbit/chunky/renderer/scene/PathTracer.java
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ public static boolean pathTrace(Scene scene, Ray ray, WorkerState state, int add
if (random.nextFloat() < pDiffuse) {
// Diffuse reflection.

boolean wasFirstReflection = firstReflection;
firstReflection = false;

if (!scene.kill(ray.depth + 1, random)) {
Expand All @@ -169,12 +170,20 @@ public static boolean pathTrace(Scene scene, Ray ray, WorkerState state, int add

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

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;
if (wasFirstReflection) {
ray.apparentBrightness.x = ray.color.x * ray.color.x * currentMat.apparentBrightness * scene.apparentEmitterBrightness * scene.emitterIntensity;
ray.apparentBrightness.y = ray.color.y * ray.color.y * currentMat.apparentBrightness * scene.apparentEmitterBrightness * scene.emitterIntensity;
ray.apparentBrightness.z = ray.color.z * ray.color.z * currentMat.apparentBrightness * scene.apparentEmitterBrightness * scene.emitterIntensity;

} else {
ray.emittance.x = ray.color.x * ray.color.x *
currentMat.emittance * scene.emitterLightIntensity * scene.emitterIntensity;
ray.emittance.y = ray.color.y * ray.color.y *
currentMat.emittance * scene.emitterLightIntensity * scene.emitterIntensity;
ray.emittance.z = ray.color.z * ray.color.z *
currentMat.emittance * scene.emitterLightIntensity * scene.emitterIntensity;
}

hit = true;
} else if(scene.emittersEnabled && scene.emitterSamplingStrategy != EmitterSamplingStrategy.NONE && scene.getEmitterGrid() != null) {
// Sample emitter
Expand Down Expand Up @@ -232,11 +241,11 @@ public static boolean pathTrace(Scene scene, Ray ray, WorkerState state, int add
reflected.diffuseReflection(ray, random);
hit = pathTrace(scene, reflected, state, 0, false) || hit;
if (hit) {
ray.color.x = (addEmitted * ray.emittance.x) + ray.color.x * (directLightR * scene.sun.emittance.x + (
ray.color.x = (addEmitted * ray.apparentBrightness.x) + (addEmitted * ray.emittance.x) + ray.color.x * (directLightR * scene.sun.emittance.x + (
reflected.color.x + reflected.emittance.x) + (indirectEmitterColor.x));
ray.color.y = (addEmitted * ray.emittance.y) + ray.color.y * (directLightG * scene.sun.emittance.y + (
ray.color.y = (addEmitted * ray.apparentBrightness.y) + (addEmitted * ray.emittance.y) + ray.color.y * (directLightG * scene.sun.emittance.y + (
reflected.color.y + reflected.emittance.y) + (indirectEmitterColor.y));
ray.color.z = (addEmitted * ray.emittance.z) + ray.color.z * (directLightB * scene.sun.emittance.z + (
ray.color.z = (addEmitted * ray.apparentBrightness.z) + (addEmitted * ray.emittance.z) + ray.color.z * (directLightB * scene.sun.emittance.z + (
reflected.color.z + reflected.emittance.z) + (indirectEmitterColor.z));
} else if(indirectEmitterColor.x > Ray.EPSILON || indirectEmitterColor.y > Ray.EPSILON || indirectEmitterColor.z > Ray.EPSILON) {
hit = true;
Expand All @@ -251,11 +260,11 @@ public static boolean pathTrace(Scene scene, Ray ray, WorkerState state, int add
hit = pathTrace(scene, reflected, state, 0, false) || hit;
if (hit) {
ray.color.x =
(addEmitted * ray.emittance.x) + ray.color.x * ((reflected.color.x + reflected.emittance.x) + (indirectEmitterColor.x));
(addEmitted * ray.apparentBrightness.x) + (addEmitted * ray.emittance.x) + ray.color.x * ((reflected.color.x + reflected.emittance.x) + (indirectEmitterColor.x));
ray.color.y =
(addEmitted * ray.emittance.y) + ray.color.y * ((reflected.color.y + reflected.emittance.y) + (indirectEmitterColor.y));
(addEmitted * ray.apparentBrightness.y) + (addEmitted * ray.emittance.y) + ray.color.y * ((reflected.color.y + reflected.emittance.y) + (indirectEmitterColor.y));
ray.color.z =
(addEmitted * ray.emittance.z) + ray.color.z * ((reflected.color.z + reflected.emittance.z) + (indirectEmitterColor.z));
(addEmitted * ray.apparentBrightness.z) + (addEmitted * ray.emittance.z) + ray.color.z * ((reflected.color.z + reflected.emittance.z) + (indirectEmitterColor.z));
} else if(indirectEmitterColor.x > Ray.EPSILON || indirectEmitterColor.y > Ray.EPSILON || indirectEmitterColor.z > Ray.EPSILON) {
hit = true;
ray.color.x *= indirectEmitterColor.x;
Expand Down Expand Up @@ -468,6 +477,7 @@ private static void sampleEmitterFace(Scene scene, Ray ray, Grid.EmitterPosition
e *= pos.block.surfaceArea(face);
e *= emitterRay.getCurrentMaterial().emittance;
e *= scene.emitterIntensity;
e *= scene.emitterLightIntensity;
e *= scaler;

result.scaleAdd(e, emitterRay.color);
Expand Down
88 changes: 86 additions & 2 deletions chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public class Scene implements JsonSerializable, Refreshable {
/**
* Default emitter intensity.
*/
public static final double DEFAULT_EMITTER_INTENSITY = 13;
public static final double DEFAULT_EMITTER_INTENSITY = 1;

/**
* Minimum emitter intensity.
Expand All @@ -130,6 +130,36 @@ public class Scene implements JsonSerializable, Refreshable {
*/
public static final double MAX_EMITTER_INTENSITY = 1000;

/**
* Default emitter light intensity.
*/
public static final double DEFAULT_EMITTER_LIGHT_INTENSITY = 13;

/**
* Minimum emitter light intensity.
*/
public static final double MIN_EMITTER_LIGHT_INTENSITY = 0.01;

/**
* Maximum emitter light intensity.
*/
public static final double MAX_EMITTER_LIGHT_INTENSITY = 50;

/**
* Default apparent emitter brightness.
*/
public static final double DEFAULT_APPARENT_EMITTER_BRIGHTNESS = 1;

/**
* Minimum apparent emitter brightness.
*/
public static final double MIN_APPARENT_EMITTER_BRIGHTNESS = 0;

/**
* Maximum apparent emitter brightness.
*/
public static final double MAX_APPARENT_EMITTER_BRIGHTNESS = 50;

/**
* Default exposure.
*/
Expand Down Expand Up @@ -195,6 +225,8 @@ public class Scene implements JsonSerializable, Refreshable {
protected boolean saveSnapshots = false;
protected boolean emittersEnabled = DEFAULT_EMITTERS_ENABLED;
protected double emitterIntensity = DEFAULT_EMITTER_INTENSITY;
protected double emitterLightIntensity = DEFAULT_EMITTER_LIGHT_INTENSITY;
protected double apparentEmitterBrightness = DEFAULT_APPARENT_EMITTER_BRIGHTNESS;
protected EmitterSamplingStrategy emitterSamplingStrategy = EmitterSamplingStrategy.NONE;

protected SunSamplingStrategy sunSamplingStrategy = SunSamplingStrategy.FAST;
Expand Down Expand Up @@ -459,6 +491,8 @@ public synchronized void copyState(Scene other, boolean copyChunks) {
sunSamplingStrategy = other.sunSamplingStrategy;
emittersEnabled = other.emittersEnabled;
emitterIntensity = other.emitterIntensity;
emitterLightIntensity = other.emitterLightIntensity;
apparentEmitterBrightness = other.apparentEmitterBrightness;
emitterSamplingStrategy = other.emitterSamplingStrategy;
preventNormalEmitterWithSampling = other.preventNormalEmitterWithSampling;
transparentSky = other.transparentSky;
Expand Down Expand Up @@ -1882,6 +1916,36 @@ public void setEmitterIntensity(double value) {
refresh();
}

/**
* @return The current emitter light intensity
*/
public double getEmitterLightIntensity() {
return emitterLightIntensity;
}

/**
* Set the emitter light intensity.
*/
public void setEmitterLightIntensity(double value) {
emitterLightIntensity = value;
refresh();
}

/**
* @return The current apparent emitter brightness
*/
public double getApparentEmitterBrightness() {
return apparentEmitterBrightness;
}

/**
* Set the apparent emitter brightness.
*/
public void setApparentEmitterBrightness(double value) {
apparentEmitterBrightness = value;
refresh();
}

/**
* Set the transparent sky option.
*/
Expand Down Expand Up @@ -2729,6 +2793,8 @@ public void setUseCustomWaterColor(boolean value) {
json.add("saveSnapshots", saveSnapshots);
json.add("emittersEnabled", emittersEnabled);
json.add("emitterIntensity", emitterIntensity);
json.add("emitterLightIntensity", emitterLightIntensity);
json.add("apparentEmitterBrightness", apparentEmitterBrightness);
json.add("sunSamplingStrategy", sunSamplingStrategy.getId());
json.add("stillWater", stillWater);
json.add("waterOpacity", waterOpacity);
Expand Down Expand Up @@ -3011,7 +3077,15 @@ public synchronized void importFromJson(JsonObject json) {
dumpFrequency = json.get("dumpFrequency").intValue(dumpFrequency);
saveSnapshots = json.get("saveSnapshots").boolValue(saveSnapshots);
emittersEnabled = json.get("emittersEnabled").boolValue(emittersEnabled);
emitterIntensity = json.get("emitterIntensity").doubleValue(emitterIntensity);
if (json.get("emitterLightIntensity").isUnknown()) {
// load equivalent values in scenes saved in older versions.
emitterIntensity = 1;
emitterLightIntensity = json.get("emitterIntensity").doubleValue(emitterIntensity);
} else {
emitterIntensity = json.get("emitterIntensity").doubleValue(emitterIntensity);
emitterLightIntensity = json.get("emitterLightIntensity").doubleValue(emitterLightIntensity);
}
apparentEmitterBrightness = json.get("apparentEmitterBrightness").doubleValue(apparentEmitterBrightness);

if (json.get("sunSamplingStrategy").isUnknown()) {
boolean sunSampling = json.get("sunEnabled").boolValue(false);
Expand Down Expand Up @@ -3321,6 +3395,16 @@ public void setEmittance(String materialName, float value) {
refresh(ResetReason.MATERIALS_CHANGED);
}

/**
* Modifies the apparent brightness property for the given material.
*/
public void setApparentBrightness(String materialName, float value) {
JsonObject material = materials.getOrDefault(materialName, new JsonObject()).object();
material.set("apparentBrightness", Json.of(value));
materials.put(materialName, material);
refresh(ResetReason.MATERIALS_CHANGED);
}

/**
* Modifies the specular coefficient property for the given material.
*/
Expand Down
20 changes: 19 additions & 1 deletion chunky/src/java/se/llbit/chunky/ui/render/tabs/LightingTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public class LightingTab extends ScrollPane implements RenderControlsTab, Initia
@FXML private DoubleAdjuster skyIntensity;
@FXML private DoubleAdjuster apparentSkyBrightness;
@FXML private DoubleAdjuster emitterIntensity;
@FXML private DoubleAdjuster emitterLightIntensity;
@FXML private DoubleAdjuster apparentEmitterBrightness;
@FXML private DoubleAdjuster sunIntensity;
@FXML private CheckBox drawSun;
@FXML private ComboBox<SunSamplingStrategy> sunSamplingStrategy;
Expand Down Expand Up @@ -104,12 +106,26 @@ public LightingTab() throws IOException {
(observable, oldValue, newValue) -> scene.setEmittersEnabled(newValue));

emitterIntensity.setName("Emitter intensity");
emitterIntensity.setTooltip("Modifies the intensity of emitter light.");
emitterIntensity.setTooltip("Changes the brightness of emitters.");
emitterIntensity.setRange(Scene.MIN_EMITTER_INTENSITY, Scene.MAX_EMITTER_INTENSITY);
emitterIntensity.makeLogarithmic();
emitterIntensity.clampMin();
emitterIntensity.onValueChange(value -> scene.setEmitterIntensity(value));

emitterLightIntensity.setName("Emitter light intensity");
emitterLightIntensity.setTooltip("Modifies the intensity of emitter light.");
emitterLightIntensity.setRange(Scene.MIN_EMITTER_LIGHT_INTENSITY, Scene.MAX_EMITTER_LIGHT_INTENSITY);
emitterLightIntensity.makeLogarithmic();
emitterLightIntensity.clampMin();
emitterLightIntensity.onValueChange(value -> scene.setEmitterLightIntensity(value));

apparentEmitterBrightness.setName("Apparent emitter brightness");
apparentEmitterBrightness.setTooltip("Modifies the apparent brightness of emitters.");
apparentEmitterBrightness.setRange(Scene.MIN_APPARENT_EMITTER_BRIGHTNESS, Scene.MAX_APPARENT_EMITTER_BRIGHTNESS);
apparentEmitterBrightness.makeLogarithmic();
apparentEmitterBrightness.clampMin();
apparentEmitterBrightness.onValueChange(value -> scene.setApparentEmitterBrightness(value));

emitterSamplingStrategy.getItems().addAll(EmitterSamplingStrategy.values());
emitterSamplingStrategy.getSelectionModel().selectedItemProperty()
.addListener((observable, oldvalue, newvalue) -> {
Expand Down Expand Up @@ -194,6 +210,8 @@ public void setController(RenderControlsFxController controller) {
skyIntensity.set(scene.sky().getSkyLight());
apparentSkyBrightness.set(scene.sky().getApparentSkyLight());
emitterIntensity.set(scene.getEmitterIntensity());
emitterLightIntensity.set(scene.getEmitterLightIntensity());
apparentEmitterBrightness.set(scene.getApparentEmitterBrightness());
sunIntensity.set(scene.sun().getIntensity());
sunLuminosity.set(scene.sun().getLuminosity());
apparentSunBrightness.set(scene.sun().getApparentBrightness());
Expand Down
17 changes: 16 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 @@ -50,6 +50,7 @@ public class MaterialsTab extends HBox implements RenderControlsTab, Initializab
private Scene scene;

private final DoubleAdjuster emittance = new DoubleAdjuster();
private final DoubleAdjuster apparentBrightness = new DoubleAdjuster();
private final DoubleAdjuster specular = new DoubleAdjuster();
private final DoubleAdjuster ior = new DoubleAdjuster();
private final DoubleAdjuster perceptualSmoothness = new DoubleAdjuster();
Expand All @@ -60,9 +61,15 @@ public MaterialsTab() {
emittance.setName("Emittance");
emittance.setRange(0, 100);
emittance.setTooltip("Intensity of the light emitted from the selected material.");
emittance.clampMin();
apparentBrightness.setName("Apparent Brightness");
apparentBrightness.setRange(0, 100);
apparentBrightness.setTooltip("Apparent brightness of the texture of the selected material.");
apparentBrightness.clampMin();
specular.setName("Specular");
specular.setRange(0, 1);
specular.setTooltip("Reflectivity of the selected material.");
specular.clampBoth();
ior.setName("IoR");
ior.setRange(0, 5);
ior.setTooltip("Index of Refraction of the selected material.");
Expand All @@ -72,6 +79,7 @@ public MaterialsTab() {
metalness.setName("Metalness");
metalness.setRange(0, 1);
metalness.setTooltip("Metalness (texture-tinted reflectivity) of the selected material.");
metalness.clampBoth();
ObservableList<String> blockIds = FXCollections.observableArrayList();
blockIds.addAll(MaterialStore.collections.keySet());
blockIds.addAll(ExtraMaterials.idMap.keySet());
Expand All @@ -87,7 +95,7 @@ public MaterialsTab() {
settings.setSpacing(10);
settings.getChildren().addAll(
new Label("Material Properties"),
emittance, specular, perceptualSmoothness, ior, metalness,
emittance, apparentBrightness, specular, perceptualSmoothness, ior, metalness,
new Label("(set to zero to disable)"));
setPadding(new Insets(10));
setSpacing(15);
Expand All @@ -113,19 +121,22 @@ private void updateSelectedMaterial(String materialName) {
boolean materialExists = false;
if (MaterialStore.collections.containsKey(materialName)) {
double emAcc = 0;
double apparentBrightnessAcc = 0;
double specAcc = 0;
double iorAcc = 0;
double perceptualSmoothnessAcc = 0;
double metalnessAcc = 0;
Collection<Block> blocks = MaterialStore.collections.get(materialName);
for (Block block : blocks) {
emAcc += block.emittance;
apparentBrightnessAcc += block.apparentBrightness;
specAcc += block.specular;
iorAcc += block.ior;
perceptualSmoothnessAcc += block.getPerceptualSmoothness();
metalnessAcc += block.metalness;
}
emittance.set(emAcc / blocks.size());
apparentBrightness.set(apparentBrightnessAcc / blocks.size());
specular.set(specAcc / blocks.size());
ior.set(iorAcc / blocks.size());
perceptualSmoothness.set(perceptualSmoothnessAcc / blocks.size());
Expand All @@ -135,6 +146,7 @@ private void updateSelectedMaterial(String materialName) {
Material material = ExtraMaterials.idMap.get(materialName);
if (material != null) {
emittance.set(material.emittance);
apparentBrightness.set(material.apparentBrightness);
specular.set(material.specular);
ior.set(material.ior);
perceptualSmoothness.set(material.getPerceptualSmoothness());
Expand All @@ -145,6 +157,7 @@ private void updateSelectedMaterial(String materialName) {
Block block = new MinecraftBlock(materialName.substring(10), Texture.air);
scene.getPalette().applyMaterial(block);
emittance.set(block.emittance);
apparentBrightness.set(block.apparentBrightness);
specular.set(block.specular);
ior.set(block.ior);
perceptualSmoothness.set(block.getPerceptualSmoothness());
Expand All @@ -153,12 +166,14 @@ private void updateSelectedMaterial(String materialName) {
}
if (materialExists) {
emittance.onValueChange(value -> scene.setEmittance(materialName, value.floatValue()));
apparentBrightness.onValueChange(value -> scene.setApparentBrightness(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()));
metalness.onValueChange(value -> scene.setMetalness(materialName, value.floatValue()));
} else {
emittance.onValueChange(value -> {});
apparentBrightness.onValueChange(value -> {});
specular.onValueChange(value -> {});
ior.onValueChange(value -> {});
perceptualSmoothness.onValueChange(value -> {});
Expand Down
7 changes: 7 additions & 0 deletions chunky/src/java/se/llbit/chunky/world/Material.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ public abstract class Material {
*/
public float emittance = 0;

/**
* The apparent brightness of the material.
*/
public float apparentBrightness = 1;

/**
* The (linear) roughness controlling how rough a shiny block appears. A value of 0 makes the
* surface perfectly specular, a value of 1 makes it diffuse.
Expand Down Expand Up @@ -103,6 +108,7 @@ public void restoreDefaults() {
solid = true;
specular = 0;
emittance = 0;
apparentBrightness = 1;
roughness = 0;
subSurfaceScattering = false;
}
Expand All @@ -123,6 +129,7 @@ public void loadMaterialProperties(JsonObject json) {
ior = json.get("ior").floatValue(ior);
specular = json.get("specular").floatValue(specular);
emittance = json.get("emittance").floatValue(emittance);
apparentBrightness = json.get("apparentBrightness").floatValue(apparentBrightness);
roughness = json.get("roughness").floatValue(roughness);
metalness = json.get("metalness").floatValue(metalness);
}
Expand Down
Loading