Skip to content

Commit

Permalink
Added more sky rendering modes
Browse files Browse the repository at this point in the history
+ Improved cloud rendering
+ Added more controls for sky parameters
+ Added gradient editor
+ Added color picker
+ HDRI textures

fixes #91 (github)
  • Loading branch information
llbit committed Aug 3, 2014
1 parent ab50351 commit fe07dfa
Show file tree
Hide file tree
Showing 51 changed files with 3,840 additions and 513 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -22,6 +22,7 @@
/latest.json
/snapshot.json
/snapshot
/credentials.json

# IntelliJ IDEA Project files
.idea/*
Expand Down
12 changes: 11 additions & 1 deletion ChangeLog.txt
@@ -1,4 +1,14 @@
1.2.4 - TBD
1.3 - TBD
* Clouds can now be moved in all directoions
* HDRI sky textures can now be used, supported formats are:
+ RGBE (.hdr)
+ PFM
* Added new sky rendering modes:
+ gradient
+ spherical skymap
+ skybox
* Added sky light parameter, affecting indirect sky lighting contribution
* The simulated sky has been modified (less pink, more bright)
* Enable "Clear Selection" button after selecting region
* Fixed regular glass blocks not letting light pass through (since 1.2.0).
This could also affect other transparent blocks where the transparent
Expand Down
11 changes: 2 additions & 9 deletions chunky/src/java/se/llbit/chunky/renderer/scene/Camera.java
Expand Up @@ -860,11 +860,7 @@ public double getMaxFoV() {
public JsonObject toJson() {
JsonObject camera = new JsonObject();

JsonObject position = new JsonObject();
position.add("x", pos.x);
position.add("y", pos.y);
position.add("z", pos.z);
camera.add("position", position);
camera.add("position", pos.toJson());

JsonObject orientation = new JsonObject();
orientation.add("roll", roll);
Expand All @@ -885,10 +881,7 @@ public JsonObject toJson() {

@Override
public void fromJson(JsonObject obj) {
JsonObject position = obj.get("position").object();
pos.x = position.get("x").doubleValue(0);
pos.y = position.get("y").doubleValue(0);
pos.z = position.get("z").doubleValue(0);
pos.fromJson(obj.get("position").object());

JsonObject orientation = obj.get("orientation").object();
roll = orientation.get("roll").doubleValue(0);
Expand Down
47 changes: 23 additions & 24 deletions chunky/src/java/se/llbit/chunky/renderer/scene/CameraPreset.java
Expand Up @@ -26,7 +26,7 @@
*/
abstract public class CameraPreset {

public static CameraPreset NONE = new CameraPreset("None") {
public static CameraPreset NONE = new CameraPreset("None", null) {
@Override
public void apply(Camera camera) {
}
Expand All @@ -36,54 +36,49 @@ public ImageIcon getIcon() {
}
};
public static CameraPreset ISO_WEST_NORTH = new Isometric("West-North",
Icon.isoWN.createIcon(), -Math.PI/4, -Math.PI/4);
Icon.isoWN.imageIcon(), -Math.PI/4, -Math.PI/4);
public static CameraPreset ISO_NORTH_EAST = new Isometric("North-East",
Icon.isoNE.createIcon(), -3*Math.PI/4, -Math.PI/4);
Icon.isoNE.imageIcon(), -3*Math.PI/4, -Math.PI/4);
public static CameraPreset ISO_EAST_SOUTH = new Isometric("East-South",
Icon.isoES.createIcon(), -5*Math.PI/4, -Math.PI/4);
Icon.isoES.imageIcon(), -5*Math.PI/4, -Math.PI/4);
public static CameraPreset ISO_SOUTH_WEST = new Isometric("South-West",
Icon.isoSW.createIcon(), -7*Math.PI/4, -Math.PI/4);
public static CameraPreset SKYBOX_EAST = new Skybox("East", Math.PI, -Math.PI/2);
public static CameraPreset SKYBOX_WEST = new Skybox("West", 0, -Math.PI/2);
public static CameraPreset SKYBOX_UP = new Skybox("Up", -Math.PI/2, Math.PI);
public static CameraPreset SKYBOX_DOWN = new Skybox("Down", -Math.PI/2, 0);
public static CameraPreset SKYBOX_NORTH = new Skybox("North", -Math.PI/2, -Math.PI/2);
public static CameraPreset SKYBOX_SOUTH = new Skybox("South", Math.PI/2, -Math.PI/2);
Icon.isoSW.imageIcon(), -7*Math.PI/4, -Math.PI/4);
public static CameraPreset SKYBOX_RIGHT = new Skybox("Right", Icon.skyboxRight.imageIcon(), Math.PI, -Math.PI/2);
public static CameraPreset SKYBOX_LEFT = new Skybox("Left", Icon.skyboxLeft.imageIcon(), 0, -Math.PI/2);
public static CameraPreset SKYBOX_UP = new Skybox("Up", Icon.skyboxUp.imageIcon(), -Math.PI/2, Math.PI);
public static CameraPreset SKYBOX_DOWN = new Skybox("Down", Icon.skyboxDown.imageIcon(), -Math.PI/2, 0);
public static CameraPreset SKYBOX_FRONT = new Skybox("Front (North)", Icon.skyboxFront.imageIcon(), -Math.PI/2, -Math.PI/2);
public static CameraPreset SKYBOX_BACK = new Skybox("Back", Icon.skyboxBack.imageIcon(), Math.PI/2, -Math.PI/2);

public static class Isometric extends CameraPreset {

private final double yaw;
private final double pitch;
private final ImageIcon icon;

public Isometric(String name, ImageIcon icon, double yaw, double pitch) {
super("Isometric " + name);
super("Isometric " + name, icon);
this.yaw = yaw;
this.pitch = pitch;
this.icon = icon;
}

@Override
public void apply(Camera camera) {
camera.setView(yaw, pitch, 0);
camera.setProjectionMode(ProjectionMode.PARALLEL);
}

@Override
public ImageIcon getIcon() {
return icon;
}
}

public static class Skybox extends CameraPreset {

private final double yaw;
private final double pitch;
private final ImageIcon icon;

public Skybox(String name, double yaw, double pitch) {
super("Skybox " + name);
public Skybox(String name, ImageIcon icon, double yaw, double pitch) {
super("Skybox " + name, icon);
this.yaw = yaw;
this.pitch = pitch;
this.icon = icon;
}

@Override
Expand All @@ -95,14 +90,16 @@ public void apply(Camera camera) {

@Override
public ImageIcon getIcon() {
return null;
return icon;
}
}

private final String name;
private ImageIcon icon;

public CameraPreset(String name) {
public CameraPreset(String name, ImageIcon icon) {
this.name = name;
this.icon = icon;
}

@Override
Expand All @@ -116,5 +113,7 @@ public String toString() {
*/
abstract public void apply(Camera camera);

abstract public ImageIcon getIcon();
public ImageIcon getIcon() {
return icon;
}
}
Expand Up @@ -73,7 +73,7 @@ public static final void pathTrace(Scene scene, Ray ray, WorkerState state,
// sky color
scene.sky.getSkySpecularColor(ray, scene.waterHeight > 0);
} else {
scene.sky.getSkyDiffuseColor(ray, scene.waterHeight > 0);
scene.sky.getSkyColor(ray, scene.waterHeight > 0);
}
break;
}
Expand Down
179 changes: 163 additions & 16 deletions chunky/src/java/se/llbit/chunky/renderer/scene/RayTracer.java
@@ -1,4 +1,4 @@
/* Copyright (c) 2013 Jesper Öqvist <jesper@llbit.se>
/* Copyright (c) 2013-2014 Jesper Öqvist <jesper@llbit.se>
*
* This file is part of Chunky.
*
Expand All @@ -20,14 +20,15 @@
import se.llbit.chunky.renderer.WorkerState;
import se.llbit.chunky.world.Block;
import se.llbit.chunky.world.Clouds;
import se.llbit.math.QuickMath;
import se.llbit.math.Ray;
import se.llbit.math.Ray.RayPool;

/**
* @author Jesper Öqvist <jesper@llbit.se>
*/
public class RayTracer {
private static final double CLOUD_OPACITY = 0.9;

/**
* @param scene
* @param state
Expand Down Expand Up @@ -70,7 +71,7 @@ public static void quickTrace(Scene scene, WorkerState state) {
*/
public static boolean nextIntersection(Scene scene, Ray ray, WorkerState state) {

if (scene.cloudsEnabled && cloudIntersection(scene, ray)) {
if (scene.sky().cloudsEnabled() && cloudIntersection(scene, ray)) {
Ray oct = state.rayPool.get(ray);
if (nextWorldIntersection(scene, oct, state.rayPool) &&
oct.distance <= ray.distance) {
Expand All @@ -81,12 +82,9 @@ public static boolean nextIntersection(Scene scene, Ray ray, WorkerState state)
ray.prevMaterial = oct.prevMaterial;
ray.currentMaterial = oct.currentMaterial;
} else {
ray.color.set(1, 1, 1, 1);
ray.prevMaterial = ray.currentMaterial;
ray.currentMaterial = Block.GRASS_ID;
ray.x.scaleAdd(ray.tNear, ray.d, ray.x);
ray.n.set(0, -QuickMath.signum(ray.d.y), 0);
ray.distance += ray.tNear;
ray.x.scaleAdd(ray.tNear + Ray.EPSILON, ray.d);
}
state.rayPool.dispose(oct);
return true;
Expand Down Expand Up @@ -130,18 +128,167 @@ private static boolean nextWorldIntersection(Scene scene, Ray ray,
}

private static boolean cloudIntersection(Scene scene, Ray ray) {
if (ray.d.y != 0) {
ray.t = (scene.cloudHeight - scene.origin.y - ray.x.y) / ray.d.y;
if (ray.t > Ray.EPSILON) {
double u = ray.x.x + ray.d.x * ray.t;
double v = ray.x.z + ray.d.z * ray.t;
if (Clouds.getCloud((int) (u/128), (int) (v/128)) != 0) {
ray.distance += ray.t;
return true;
double offsetX = scene.sky().cloudXOffset();
double offsetY = scene.sky().cloudYOffset();
double offsetZ = scene.sky().cloudZOffset();
double inv_size = 1/scene.sky().cloudSize();
double cloudBot = offsetY - scene.origin.y;
double cloudTop = offsetY - scene.origin.y + 5;
int target = 1;
double t_offset = 0;
ray.tNear = Double.POSITIVE_INFINITY;
if (ray.x.y < cloudBot || ray.x.y > cloudTop) {
if (ray.d.y > 0) {
t_offset = (cloudBot - ray.x.y) / ray.d.y;
} else {
t_offset = (cloudTop - ray.x.y) / ray.d.y;
}
if (t_offset < 0) {
return false;
}
// ray is entering cloud
if (inCloud((ray.d.x*t_offset + ray.x.x)*inv_size + offsetX, (ray.d.z*t_offset + ray.x.z)*inv_size + offsetZ)) {
ray.tNear = t_offset;
ray.distance += t_offset;
ray.n.set(0, -Math.signum(ray.d.y), 0);
ray.color.set(1,1,1,CLOUD_OPACITY);
return true;
}
} else if (inCloud(ray.x.x*inv_size + offsetX, ray.x.z*inv_size + offsetZ)) {
target = 0;
return false;
}
double tExit = Double.MAX_VALUE;
if (ray.d.y > 0) {
tExit = (cloudTop - ray.x.y) / ray.d.y - t_offset;
} else {
tExit = (cloudBot - ray.x.y) / ray.d.y - t_offset;
}
double x0 = (ray.x.x + ray.d.x*t_offset)*inv_size + offsetX;
double z0 = (ray.x.z + ray.d.z*t_offset)*inv_size + offsetZ;
double xp = x0;
double zp = z0;
int ix = (int) Math.floor(xp);
int iz = (int) Math.floor(zp);
int xmod = (int)Math.signum(ray.d.x), zmod = (int)Math.signum(ray.d.z);
double dx = Math.abs(ray.d.x)*inv_size;
double dz = Math.abs(ray.d.z)*inv_size;
double t = 0;
int i = 0;
int nx = 0, nz = 0;
if (dx > dz) {
double m = dz/dx;
double xrem = xmod * (ix+0.5*(1+xmod) - xp);
double zlimit = xrem*m;
while (t < tExit) {
double zrem = zmod * (iz+0.5*(1+zmod) - zp);
zp = z0 + zmod * (i+1) * m;
if (zrem < zlimit) {
iz += zmod;
if (Clouds.getCloud(ix, iz) == target) {
t = i/dx + zrem/dz;
nx = 0;
nz = -zmod;
break;
}
ix += xmod;
if (Clouds.getCloud(ix, iz) == target) {
t = (i+xrem)/dx;
nx = -xmod;
nz = 0;
break;
}
} else {
ix += xmod;
if (Clouds.getCloud(ix, iz) == target) {
t = (i+xrem)/dx;
nx = -xmod;
nz = 0;
break;
}
if (zrem <= m) {
iz += zmod;
if (Clouds.getCloud(ix, iz) == target) {
t = i/dx + zrem/dz;
nx = 0;
nz = -zmod;
break;
}
}
}
t = i/dx;
i+=1;
}
} else {
double m = dx/dz;
double zrem = zmod * (iz+0.5*(1+zmod) - zp);
double xlimit = zrem*m;
while (t < tExit) {
double xrem = xmod * (ix+0.5*(1+xmod) - xp);
xp = x0 + xmod * (i+1) * m;
if (xrem < xlimit) {
ix += xmod;
if (Clouds.getCloud(ix, iz) == target) {
t = i/dz + xrem/dx;
nx = -xmod;
nz = 0;
break;
}
iz += zmod;
if (Clouds.getCloud(ix, iz) == target) {
t = (i+zrem)/dz;
nx = 0;
nz = -zmod;
break;
}
} else {
iz += zmod;
if (Clouds.getCloud(ix, iz) == target) {
t = (i+zrem)/dz;
nx = 0;
nz = -zmod;
break;
}
if (xrem <= m) {
ix += xmod;
if (Clouds.getCloud(ix, iz) == target) {
t = i/dz + xrem/dx;
nx = -xmod;
nz = 0;
break;
}
}
}
t = i/dz;
i+=1;
}
}
int ny = 0;
if (target == 1) {
if (t > tExit) {
return false;
}
} else {
if (t > tExit) {
nx = 0;
ny = (int) Math.signum(ray.d.y);
nz = 0;
t = tExit;
} else {
nx = -nx;
nz = -nz;
}
}
return false;
ray.n.set(nx, ny, nz);
ray.tNear = t + t_offset;
ray.distance += ray.tNear;
ray.color.set(1, 1, 1, CLOUD_OPACITY);
return true;
}

private static boolean inCloud(double x, double z) {
return Clouds.getCloud((int)Math.floor(x), (int)Math.floor(z)) == 1;
}


}

0 comments on commit fe07dfa

Please sign in to comment.