use of org.sunflow.image.Color in project joons-renderer by joonhyublee.
the class LightServer method calculatePhotons.
boolean calculatePhotons(final PhotonStore map, String type, final int seed, Options options) {
if (map == null) {
return true;
}
if (lights.length == 0) {
UI.printError(Module.LIGHT, "Unable to trace %s photons, no lights in scene", type);
return false;
}
final float[] histogram = new float[lights.length];
histogram[0] = lights[0].getPower();
for (int i = 1; i < lights.length; i++) {
histogram[i] = histogram[i - 1] + lights[i].getPower();
}
UI.printInfo(Module.LIGHT, "Tracing %s photons ...", type);
map.prepare(options, scene.getBounds());
int numEmittedPhotons = map.numEmit();
if (numEmittedPhotons <= 0 || histogram[histogram.length - 1] <= 0) {
UI.printError(Module.LIGHT, "Photon mapping enabled, but no %s photons to emit", type);
return false;
}
UI.taskStart("Tracing " + type + " photons", 0, numEmittedPhotons);
Thread[] photonThreads = new Thread[scene.getThreads()];
final float scale = 1.0f / numEmittedPhotons;
int delta = numEmittedPhotons / photonThreads.length;
photonCounter = 0;
Timer photonTimer = new Timer();
photonTimer.start();
for (int i = 0; i < photonThreads.length; i++) {
final int threadID = i;
final int start = threadID * delta;
final int end = (threadID == (photonThreads.length - 1)) ? numEmittedPhotons : (threadID + 1) * delta;
photonThreads[i] = new Thread(new Runnable() {
public void run() {
IntersectionState istate = new IntersectionState();
for (int i = start; i < end; i++) {
synchronized (LightServer.this) {
UI.taskUpdate(photonCounter);
photonCounter++;
if (UI.taskCanceled()) {
return;
}
}
int qmcI = i + seed;
double rand = QMC.halton(0, qmcI) * histogram[histogram.length - 1];
int j = 0;
while (rand >= histogram[j] && j < histogram.length) {
j++;
}
// make sure we didn't pick a zero-probability light
if (j == histogram.length) {
continue;
}
double randX1 = (j == 0) ? rand / histogram[0] : (rand - histogram[j]) / (histogram[j] - histogram[j - 1]);
double randY1 = QMC.halton(1, qmcI);
double randX2 = QMC.halton(2, qmcI);
double randY2 = QMC.halton(3, qmcI);
Point3 pt = new Point3();
Vector3 dir = new Vector3();
Color power = new Color();
lights[j].getPhoton(randX1, randY1, randX2, randY2, pt, dir, power);
power.mul(scale);
Ray r = new Ray(pt, dir);
scene.trace(r, istate);
if (istate.hit()) {
shadePhoton(ShadingState.createPhotonState(r, istate, qmcI, map, LightServer.this), power);
}
}
}
});
photonThreads[i].setPriority(scene.getThreadPriority());
photonThreads[i].start();
}
for (int i = 0; i < photonThreads.length; i++) {
try {
photonThreads[i].join();
} catch (InterruptedException e) {
UI.printError(Module.LIGHT, "Photon thread %d of %d was interrupted", i + 1, photonThreads.length);
return false;
}
}
if (UI.taskCanceled()) {
// shut down task cleanly
UI.taskStop();
return false;
}
photonTimer.end();
UI.taskStop();
UI.printInfo(Module.LIGHT, "Tracing time for %s photons: %s", type, photonTimer.toString());
map.init();
return true;
}
use of org.sunflow.image.Color in project joons-renderer by joonhyublee.
the class BucketRenderer method renderBucket.
private void renderBucket(Display display, int bx, int by, int threadID, IntersectionState istate) {
// pixel sized extents
int x0 = bx * bucketSize;
int y0 = by * bucketSize;
int bw = Math.min(bucketSize, imageWidth - x0);
int bh = Math.min(bucketSize, imageHeight - y0);
// prepare bucket
display.imagePrepare(x0, y0, bw, bh, threadID);
Color[] bucketRGB = new Color[bw * bh];
float[] bucketAlpha = new float[bw * bh];
// subpixel extents
int sx0 = x0 * subPixelSize - fs;
int sy0 = y0 * subPixelSize - fs;
int sbw = bw * subPixelSize + fs * 2;
int sbh = bh * subPixelSize + fs * 2;
// round up to align with maximum step size
sbw = (sbw + (maxStepSize - 1)) & (~(maxStepSize - 1));
sbh = (sbh + (maxStepSize - 1)) & (~(maxStepSize - 1));
// extra padding as needed
if (maxStepSize > 1) {
sbw++;
sbh++;
}
// allocate bucket memory
ImageSample[] samples = new ImageSample[sbw * sbh];
// allocate samples and compute jitter offsets
float invSubPixelSize = 1.0f / subPixelSize;
for (int y = 0, index = 0; y < sbh; y++) {
for (int x = 0; x < sbw; x++, index++) {
int sx = sx0 + x;
int sy = sy0 + y;
int j = sx & (sigmaLength - 1);
int k = sy & (sigmaLength - 1);
int i = (j << sigmaOrder) + QMC.sigma(k, sigmaOrder);
float dx = useJitter ? (float) QMC.halton(0, k) : 0.5f;
float dy = useJitter ? (float) QMC.halton(0, j) : 0.5f;
float rx = (sx + dx) * invSubPixelSize;
float ry = (sy + dy) * invSubPixelSize;
ry = imageHeight - ry;
samples[index] = new ImageSample(rx, ry, i);
}
}
for (int x = 0; x < sbw - 1; x += maxStepSize) {
for (int y = 0; y < sbh - 1; y += maxStepSize) {
refineSamples(samples, sbw, x, y, maxStepSize, thresh, istate);
}
}
if (dumpBuckets) {
UI.printInfo(Module.BCKT, "Dumping bucket [%d, %d] to file ...", bx, by);
GenericBitmap bitmap = new GenericBitmap(sbw, sbh);
for (int y = sbh - 1, index = 0; y >= 0; y--) {
for (int x = 0; x < sbw; x++, index++) {
bitmap.writePixel(x, y, samples[index].c, samples[index].alpha);
}
}
bitmap.save(String.format("bucket_%04d_%04d.png", bx, by));
}
if (displayAA) {
// color coded image of what is visible
float invArea = invSubPixelSize * invSubPixelSize;
for (int y = 0, index = 0; y < bh; y++) {
for (int x = 0; x < bw; x++, index++) {
int sampled = 0;
for (int i = 0; i < subPixelSize; i++) {
for (int j = 0; j < subPixelSize; j++) {
int sx = x * subPixelSize + fs + i;
int sy = y * subPixelSize + fs + j;
int s = sx + sy * sbw;
sampled += samples[s].sampled() ? 1 : 0;
}
}
bucketRGB[index] = new Color(sampled * invArea);
bucketAlpha[index] = 1.0f;
}
}
} else {
// filter samples into pixels
float cy = imageHeight - (y0 + 0.5f);
for (int y = 0, index = 0; y < bh; y++, cy--) {
float cx = x0 + 0.5f;
for (int x = 0; x < bw; x++, index++, cx++) {
Color c = Color.black();
float a = 0;
float weight = 0.0f;
for (int j = -fs, sy = y * subPixelSize; j <= fs; j++, sy++) {
for (int i = -fs, sx = x * subPixelSize, s = sx + sy * sbw; i <= fs; i++, sx++, s++) {
float dx = samples[s].rx - cx;
if (Math.abs(dx) > fhs) {
continue;
}
float dy = samples[s].ry - cy;
if (Math.abs(dy) > fhs) {
continue;
}
float f = filter.get(dx, dy);
c.madd(f, samples[s].c);
a += f * samples[s].alpha;
weight += f;
}
}
float invWeight = 1.0f / weight;
c.mul(invWeight);
a *= invWeight;
bucketRGB[index] = c;
bucketAlpha[index] = a;
}
}
}
// update pixels
display.imageUpdate(x0, y0, bw, bh, bucketRGB, bucketAlpha);
}
use of org.sunflow.image.Color in project joons-renderer by joonhyublee.
the class ProgressiveRenderer method progressiveRenderNext.
private int progressiveRenderNext(IntersectionState istate) {
final int TASK_SIZE = 16;
SmallBucket first = smallBucketQueue.poll();
if (first == null) {
return 0;
}
int ds = first.size / TASK_SIZE;
boolean useMask = !smallBucketQueue.isEmpty();
int mask = 2 * first.size / TASK_SIZE - 1;
int pixels = 0;
for (int i = 0, y = first.y; i < TASK_SIZE && y < imageHeight; i++, y += ds) {
for (int j = 0, x = first.x; j < TASK_SIZE && x < imageWidth; j++, x += ds) {
// check to see if this is a pixel from a higher level tile
if (useMask && (x & mask) == 0 && (y & mask) == 0) {
continue;
}
int instance = ((x & ((1 << QMC.MAX_SIGMA_ORDER) - 1)) << QMC.MAX_SIGMA_ORDER) + QMC.sigma(y & ((1 << QMC.MAX_SIGMA_ORDER) - 1), QMC.MAX_SIGMA_ORDER);
double time = QMC.halton(1, instance);
double lensU = QMC.halton(2, instance);
double lensV = QMC.halton(3, instance);
ShadingState state = scene.getRadiance(istate, x, imageHeight - 1 - y, lensU, lensV, time, instance, 4, null);
Color c = state != null ? state.getResult() : Color.BLACK;
pixels++;
// fill region
display.imageFill(x, y, Math.min(ds, imageWidth - x), Math.min(ds, imageHeight - y), c, state == null ? 0 : 1);
}
}
if (first.size >= 2 * TASK_SIZE) {
// generate child buckets
int size = first.size >>> 1;
for (int i = 0; i < 2; i++) {
if (first.y + i * size < imageHeight) {
for (int j = 0; j < 2; j++) {
if (first.x + j * size < imageWidth) {
SmallBucket b = new SmallBucket();
b.x = first.x + j * size;
b.y = first.y + i * size;
b.size = size;
b.constrast = 1.0f / size;
smallBucketQueue.put(b);
}
}
}
}
}
return pixels;
}
use of org.sunflow.image.Color in project joons-renderer by joonhyublee.
the class AnisotropicWardShader method getRadiance.
public Color getRadiance(ShadingState state) {
// make sure we are on the right side of the material
state.faceforward();
OrthoNormalBasis onb = state.getBasis();
// direct lighting and caustics
state.initLightSamples();
state.initCausticSamples();
Color lr = Color.black();
// compute specular contribution
if (state.includeSpecular()) {
Vector3 in = state.getRay().getDirection().negate(new Vector3());
for (LightSample sample : state) {
float cosNL = sample.dot(state.getNormal());
float fr = brdf(in, sample.getShadowRay().getDirection(), onb);
lr.madd(cosNL * fr, sample.getSpecularRadiance());
}
// indirect lighting - specular
if (numRays > 0) {
int n = state.getDepth() == 0 ? numRays : 1;
for (int i = 0; i < n; i++) {
// specular indirect lighting
double r1 = state.getRandom(i, 0, n);
double r2 = state.getRandom(i, 1, n);
float alphaRatio = alphaY / alphaX;
float phi = 0;
if (r1 < 0.25) {
double val = 4 * r1;
phi = (float) Math.atan(alphaRatio * Math.tan(Math.PI / 2 * val));
} else if (r1 < 0.5) {
double val = 1 - 4 * (0.5 - r1);
phi = (float) Math.atan(alphaRatio * Math.tan(Math.PI / 2 * val));
phi = (float) Math.PI - phi;
} else if (r1 < 0.75) {
double val = 4 * (r1 - 0.5);
phi = (float) Math.atan(alphaRatio * Math.tan(Math.PI / 2 * val));
phi += Math.PI;
} else {
double val = 1 - 4 * (1 - r1);
phi = (float) Math.atan(alphaRatio * Math.tan(Math.PI / 2 * val));
phi = 2 * (float) Math.PI - phi;
}
float cosPhi = (float) Math.cos(phi);
float sinPhi = (float) Math.sin(phi);
float denom = (cosPhi * cosPhi) / (alphaX * alphaX) + (sinPhi * sinPhi) / (alphaY * alphaY);
float theta = (float) Math.atan(Math.sqrt(-Math.log(1 - r2) / denom));
float sinTheta = (float) Math.sin(theta);
float cosTheta = (float) Math.cos(theta);
Vector3 h = new Vector3();
h.x = sinTheta * cosPhi;
h.y = sinTheta * sinPhi;
h.z = cosTheta;
onb.transform(h);
Vector3 o = new Vector3();
float ih = Vector3.dot(h, in);
o.x = 2 * ih * h.x - in.x;
o.y = 2 * ih * h.y - in.y;
o.z = 2 * ih * h.z - in.z;
float no = onb.untransformZ(o);
float ni = onb.untransformZ(in);
float w = ih * cosTheta * cosTheta * cosTheta * (float) Math.sqrt(Math.abs(no / ni));
Ray r = new Ray(state.getPoint(), o);
lr.madd(w / n, state.traceGlossy(r, i));
}
}
lr.mul(rhoS);
}
// add diffuse contribution
lr.add(state.diffuse(getDiffuse(state)));
return lr;
}
use of org.sunflow.image.Color in project joons-renderer by joonhyublee.
the class NormalShader method getRadiance.
public Color getRadiance(ShadingState state) {
Vector3 n = state.getNormal();
if (n == null) {
return Color.BLACK;
}
float r = (n.x + 1) * 0.5f;
float g = (n.y + 1) * 0.5f;
float b = (n.z + 1) * 0.5f;
return new Color(r, g, b);
}
Aggregations