📄 globalphotonmap.java
字号:
package org.sunflow.core.photonmap;
import java.util.ArrayList;
import org.sunflow.core.GlobalPhotonMapInterface;
import org.sunflow.core.ShadingState;
import org.sunflow.image.Color;
import org.sunflow.math.BoundingBox;
import org.sunflow.math.Point3;
import org.sunflow.math.Vector3;
import org.sunflow.system.Timer;
import org.sunflow.system.UI;
import org.sunflow.system.UI.Module;
public final class GlobalPhotonMap implements GlobalPhotonMapInterface {
private ArrayList<Photon> photonList;
private Photon[] photons;
private int storedPhotons;
private int halfStoredPhotons;
private int log2n;
private int numGather;
private float gatherRadius;
private BoundingBox bounds;
private boolean hasRadiance;
private float maxPower;
private float maxRadius;
private int numEmit;
public GlobalPhotonMap(int numEmit, int numGather, float gatherRadius) {
this.numEmit = numEmit;
this.numGather = numGather;
this.gatherRadius = gatherRadius;
bounds = new BoundingBox();
hasRadiance = false;
maxPower = 0;
maxRadius = 0;
}
public void prepare(BoundingBox sceneBounds) {
photonList = new ArrayList<Photon>();
photonList.add(null);
photons = null;
storedPhotons = halfStoredPhotons = 0;
}
public void store(ShadingState state, Vector3 dir, Color power, Color diffuse) {
Photon p = new Photon(state.getPoint(), state.getNormal(), dir, power, diffuse);
synchronized (this) {
storedPhotons++;
photonList.add(p);
bounds.include(new Point3(p.x, p.y, p.z));
maxPower = Math.max(maxPower, power.getMax());
}
}
private void locatePhotons(NearestPhotons np) {
float[] dist1d2 = new float[log2n];
int[] chosen = new int[log2n];
int i = 1;
int level = 0;
int cameFrom;
while (true) {
while (i < halfStoredPhotons) {
float dist1d = photons[i].getDist1(np.px, np.py, np.pz);
dist1d2[level] = dist1d * dist1d;
i += i;
if (dist1d > 0.0f)
i++;
chosen[level++] = i;
}
np.checkAddNearest(photons[i]);
do {
cameFrom = i;
i >>= 1;
level--;
if (i == 0)
return;
} while ((dist1d2[level] >= np.dist2[0]) || (cameFrom != chosen[level]));
np.checkAddNearest(photons[i]);
i = chosen[level++] ^ 1;
}
}
private void balance() {
if (storedPhotons == 0)
return;
photons = photonList.toArray(new Photon[photonList.size()]);
photonList = null;
Photon[] temp = new Photon[storedPhotons + 1];
balanceSegment(temp, 1, 1, storedPhotons);
photons = temp;
halfStoredPhotons = storedPhotons / 2;
log2n = (int) Math.ceil(Math.log(storedPhotons) / Math.log(2.0));
}
private void balanceSegment(Photon[] temp, int index, int start, int end) {
int median = 1;
while ((4 * median) <= (end - start + 1))
median += median;
if ((3 * median) <= (end - start + 1)) {
median += median;
median += (start - 1);
} else
median = end - median + 1;
int axis = Photon.SPLIT_Z;
Vector3 extents = bounds.getExtents();
if ((extents.x > extents.y) && (extents.x > extents.z))
axis = Photon.SPLIT_X;
else if (extents.y > extents.z)
axis = Photon.SPLIT_Y;
int left = start;
int right = end;
while (right > left) {
double v = photons[right].getCoord(axis);
int i = left - 1;
int j = right;
while (true) {
while (photons[++i].getCoord(axis) < v) {
}
while ((photons[--j].getCoord(axis) > v) && (j > left)) {
}
if (i >= j)
break;
swap(i, j);
}
swap(i, right);
if (i >= median)
right = i - 1;
if (i <= median)
left = i + 1;
}
temp[index] = photons[median];
temp[index].setSplitAxis(axis);
if (median > start) {
if (start < (median - 1)) {
float tmp;
switch (axis) {
case Photon.SPLIT_X:
tmp = bounds.getMaximum().x;
bounds.getMaximum().x = temp[index].x;
balanceSegment(temp, 2 * index, start, median - 1);
bounds.getMaximum().x = tmp;
break;
case Photon.SPLIT_Y:
tmp = bounds.getMaximum().y;
bounds.getMaximum().y = temp[index].y;
balanceSegment(temp, 2 * index, start, median - 1);
bounds.getMaximum().y = tmp;
break;
default:
tmp = bounds.getMaximum().z;
bounds.getMaximum().z = temp[index].z;
balanceSegment(temp, 2 * index, start, median - 1);
bounds.getMaximum().z = tmp;
}
} else
temp[2 * index] = photons[start];
}
if (median < end) {
if ((median + 1) < end) {
float tmp;
switch (axis) {
case Photon.SPLIT_X:
tmp = bounds.getMinimum().x;
bounds.getMinimum().x = temp[index].x;
balanceSegment(temp, (2 * index) + 1, median + 1, end);
bounds.getMinimum().x = tmp;
break;
case Photon.SPLIT_Y:
tmp = bounds.getMinimum().y;
bounds.getMinimum().y = temp[index].y;
balanceSegment(temp, (2 * index) + 1, median + 1, end);
bounds.getMinimum().y = tmp;
break;
default:
tmp = bounds.getMinimum().z;
bounds.getMinimum().z = temp[index].z;
balanceSegment(temp, (2 * index) + 1, median + 1, end);
bounds.getMinimum().z = tmp;
}
} else
temp[(2 * index) + 1] = photons[end];
}
}
private void swap(int i, int j) {
Photon tmp = photons[i];
photons[i] = photons[j];
photons[j] = tmp;
}
static class Photon {
float x;
float y;
float z;
short dir;
short normal;
int data;
int power;
int flags;
static final int SPLIT_X = 0;
static final int SPLIT_Y = 1;
static final int SPLIT_Z = 2;
static final int SPLIT_MASK = 3;
Photon(Point3 p, Vector3 n, Vector3 dir, Color power, Color diffuse) {
x = p.x;
y = p.y;
z = p.z;
this.dir = dir.encode();
this.power = power.toRGBE();
flags = 0;
normal = n.encode();
data = diffuse.toRGB();
}
void setSplitAxis(int axis) {
flags &= ~SPLIT_MASK;
flags |= axis;
}
float getCoord(int axis) {
switch (axis) {
case SPLIT_X:
return x;
case SPLIT_Y:
return y;
default:
return z;
}
}
float getDist1(float px, float py, float pz) {
switch (flags & SPLIT_MASK) {
case SPLIT_X:
return px - x;
case SPLIT_Y:
return py - y;
default:
return pz - z;
}
}
float getDist2(float px, float py, float pz) {
float dx = x - px;
float dy = y - py;
float dz = z - pz;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -