📄 cornellbox.java
字号:
package org.sunflow.core.primitive;
import org.sunflow.SunflowAPI;
import org.sunflow.core.IntersectionState;
import org.sunflow.core.LightSample;
import org.sunflow.core.LightSource;
import org.sunflow.core.ParameterList;
import org.sunflow.core.PrimitiveList;
import org.sunflow.core.Ray;
import org.sunflow.core.Shader;
import org.sunflow.core.ShadingState;
import org.sunflow.image.Color;
import org.sunflow.math.BoundingBox;
import org.sunflow.math.Matrix4;
import org.sunflow.math.OrthoNormalBasis;
import org.sunflow.math.Point3;
import org.sunflow.math.Vector3;
public class CornellBox implements PrimitiveList, Shader, LightSource {
private float minX, minY, minZ;
private float maxX, maxY, maxZ;
private Color left, right, top, bottom, back;
private Color radiance;
private int samples;
private float lxmin, lymin, lxmax, lymax;
private float area;
private BoundingBox lightBounds;
public CornellBox() {
updateGeometry(new Point3(-1, -1, -1), new Point3(1, 1, 1));
// cube colors
left = new Color(0.80f, 0.25f, 0.25f);
right = new Color(0.25f, 0.25f, 0.80f);
Color gray = new Color(0.70f, 0.70f, 0.70f);
top = bottom = back = gray;
// light source
radiance = Color.WHITE;
samples = 16;
}
private void updateGeometry(Point3 c0, Point3 c1) {
// figure out cube extents
lightBounds = new BoundingBox(c0);
lightBounds.include(c1);
// cube extents
minX = lightBounds.getMinimum().x;
minY = lightBounds.getMinimum().y;
minZ = lightBounds.getMinimum().z;
maxX = lightBounds.getMaximum().x;
maxY = lightBounds.getMaximum().y;
maxZ = lightBounds.getMaximum().z;
// work around epsilon problems for light test
lightBounds.enlargeUlps();
// light source geometry
lxmin = maxX / 3 + 2 * minX / 3;
lxmax = minX / 3 + 2 * maxX / 3;
lymin = maxY / 3 + 2 * minY / 3;
lymax = minY / 3 + 2 * maxY / 3;
area = (lxmax - lxmin) * (lymax - lymin);
}
public boolean update(ParameterList pl, SunflowAPI api) {
Point3 corner0 = pl.getPoint("corner0", null);
Point3 corner1 = pl.getPoint("corner1", null);
if (corner0 != null && corner1 != null) {
updateGeometry(corner0, corner1);
}
// shader colors
left = pl.getColor("leftColor", left);
right = pl.getColor("rightColor", right);
top = pl.getColor("topColor", top);
bottom = pl.getColor("bottomColor", bottom);
back = pl.getColor("backColor", back);
// light
radiance = pl.getColor("radiance", radiance);
samples = pl.getInt("samples", samples);
return true;
}
public void init(String name, SunflowAPI api) {
// register with the api properly
api.geometry(name, this);
api.shader(name + ".shader", this);
api.parameter("shaders", name + ".shader");
api.instance(name + ".instance", name);
api.light(name + ".light", this);
}
public BoundingBox getBounds() {
return lightBounds;
}
public float getBound(int i) {
switch (i) {
case 0:
return minX;
case 1:
return maxX;
case 2:
return minY;
case 3:
return maxY;
case 4:
return minZ;
case 5:
return maxZ;
default:
return 0;
}
}
public boolean intersects(BoundingBox box) {
// this could be optimized
BoundingBox b = new BoundingBox();
b.include(new Point3(minX, minY, minZ));
b.include(new Point3(maxX, maxY, maxZ));
if (b.intersects(box)) {
// the box is overlapping or enclosed
if (!b.contains(new Point3(box.getMinimum().x, box.getMinimum().y, box.getMinimum().z)))
return true;
if (!b.contains(new Point3(box.getMinimum().x, box.getMinimum().y, box.getMaximum().z)))
return true;
if (!b.contains(new Point3(box.getMinimum().x, box.getMaximum().y, box.getMinimum().z)))
return true;
if (!b.contains(new Point3(box.getMinimum().x, box.getMaximum().y, box.getMaximum().z)))
return true;
if (!b.contains(new Point3(box.getMaximum().x, box.getMinimum().y, box.getMinimum().z)))
return true;
if (!b.contains(new Point3(box.getMaximum().x, box.getMinimum().y, box.getMaximum().z)))
return true;
if (!b.contains(new Point3(box.getMaximum().x, box.getMaximum().y, box.getMinimum().z)))
return true;
if (!b.contains(new Point3(box.getMaximum().x, box.getMaximum().y, box.getMaximum().z)))
return true;
// all vertices of the box are inside - the surface of the box is
// not intersected
}
return false;
}
public void prepareShadingState(ShadingState state) {
state.init();
state.getRay().getPoint(state.getPoint());
int n = state.getPrimitiveID();
switch (n) {
case 0:
state.getNormal().set(new Vector3(1, 0, 0));
break;
case 1:
state.getNormal().set(new Vector3(-1, 0, 0));
break;
case 2:
state.getNormal().set(new Vector3(0, 1, 0));
break;
case 3:
state.getNormal().set(new Vector3(0, -1, 0));
break;
case 4:
state.getNormal().set(new Vector3(0, 0, 1));
break;
case 5:
state.getNormal().set(new Vector3(0, 0, -1));
break;
default:
state.getNormal().set(new Vector3(0, 0, 0));
break;
}
state.getGeoNormal().set(state.getNormal());
state.setBasis(OrthoNormalBasis.makeFromW(state.getNormal()));
state.setShader(this);
}
public void intersectPrimitive(Ray r, int primID, IntersectionState state) {
float intervalMin = Float.NEGATIVE_INFINITY;
float intervalMax = Float.POSITIVE_INFINITY;
float orgX = r.ox;
float invDirX = 1 / r.dx;
float t1, t2;
t1 = (minX - orgX) * invDirX;
t2 = (maxX - orgX) * invDirX;
int sideIn = -1, sideOut = -1;
if (invDirX > 0) {
if (t1 > intervalMin) {
intervalMin = t1;
sideIn = 0;
}
if (t2 < intervalMax) {
intervalMax = t2;
sideOut = 1;
}
} else {
if (t2 > intervalMin) {
intervalMin = t2;
sideIn = 1;
}
if (t1 < intervalMax) {
intervalMax = t1;
sideOut = 0;
}
}
if (intervalMin > intervalMax)
return;
float orgY = r.oy;
float invDirY = 1 / r.dy;
t1 = (minY - orgY) * invDirY;
t2 = (maxY - orgY) * invDirY;
if (invDirY > 0) {
if (t1 > intervalMin) {
intervalMin = t1;
sideIn = 2;
}
if (t2 < intervalMax) {
intervalMax = t2;
sideOut = 3;
}
} else {
if (t2 > intervalMin) {
intervalMin = t2;
sideIn = 3;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -