private void renderBucket(IDisplay 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(UI.Module.BCKT, "Dumping bucket [{0}, {1}] 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_{0}_{1}.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.0f; 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); }
private void renderBucket(IDisplay display, int bx, int by, int threadID, IntersectionState istate, ShadingCache cache) { // 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]; for (int y = 0, i = 0, cy = imageHeight - 1 - y0; y < bh; y++, cy--) { for (int x = 0, cx = x0; x < bw; x++, i++, cx++) { // sample pixel Color c = Color.black(); float a = 0; int instance = ((cx & ((1 << QMC.MAX_SIGMA_ORDER) - 1)) << QMC.MAX_SIGMA_ORDER) + QMC.sigma(cy & ((1 << QMC.MAX_SIGMA_ORDER) - 1), QMC.MAX_SIGMA_ORDER); double jitterX = QMC.halton(0, instance); double jitterY = QMC.halton(1, instance); double jitterT = QMC.halton(2, instance); double jitterU = QMC.halton(3, instance); double jitterV = QMC.halton(4, instance); for (int s = 0; s < numSamples; s++) { float rx = cx + 0.5f + (float)warpCubic(QMC.mod1(jitterX + s * invNumSamples)); float ry = cy + 0.5f + (float)warpCubic(QMC.mod1(jitterY + QMC.halton(0, s))); double time = QMC.mod1(jitterT + QMC.halton(1, s)); double lensU = QMC.mod1(jitterU + QMC.halton(2, s)); double lensV = QMC.mod1(jitterV + QMC.halton(3, s)); ShadingState state = scene.getRadiance(istate, rx, ry, lensU, lensV, time, instance + s, 5, cache); if (state != null) { c.add(state.getResult()); a++; } } bucketRGB[i] = c.mul(invNumSamples); bucketAlpha[i] = a * invNumSamples; if (cache != null) { cache.reset(); } } } // update pixels display.imageUpdate(x0, y0, bw, bh, bucketRGB, bucketAlpha); }
private int progressiveRenderNext(IntersectionState istate) { int TASK_SIZE = 16; SmallBucket first = smallBucketQueue.Count > 0 ? smallBucketQueue.Dequeue() : null; if (first == null) { return(0); } int ds = first.size / TASK_SIZE; bool useMask = smallBucketQueue.Count != 0; 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 = (int)((uint)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.Enqueue(b); } } } } } return(pixels); }