public void AddSample(ImageSample sample) { int previousCount = Samples.Count; Samples.Add(sample); int diff = Samples.Count - previousCount; if (diff > 0) { // add sample only if it is not a duplicate SampleMap.Add(sample.Y * Width + sample.X, sample); } }
public static ImageSample bilerp(ImageSample result, ImageSample i00, ImageSample i01, ImageSample i10, ImageSample i11, float dx, float dy) { float k00 = (1.0f - dx) * (1.0f - dy); float k01 = (1.0f - dx) * dy; float k10 = dx * (1.0f - dy); float k11 = dx * dy; Color c00 = i00.c; Color c01 = i01.c; Color c10 = i10.c; Color c11 = i11.c; Color c = Color.mul(k00, c00); c.madd(k01, c01); c.madd(k10, c10); c.madd(k11, c11); result.c = c; result.alpha = k00 * i00.alpha + k01 * i01.alpha + k10 * i10.alpha + k11 * i11.alpha; return result; }
public bool isDifferent(ImageSample sample, float thresh) { if (instance != sample.instance) return true; if (shader != sample.shader) return true; if (Color.hasContrast(c, sample.c, thresh)) return true; if (Math.Abs(alpha - sample.alpha) / (alpha + sample.alpha) > thresh) return true; // only compare normals if this pixel has not been averaged float dot = (nx * sample.nx + ny * sample.ny + nz * sample.nz); return dot < 0.9f; }
private void refineSamples(ImageSample[] samples, int sbw, int x, int y, int stepSize, float thresh, IntersectionState istate) { int dx = stepSize; int dy = stepSize * sbw; int i00 = x + y * sbw; ImageSample s00 = samples[i00]; ImageSample s01 = samples[i00 + dy]; ImageSample s10 = samples[i00 + dx]; ImageSample s11 = samples[i00 + dx + dy]; if (!s00.sampled()) computeSubPixel(s00, istate); if (!s01.sampled()) computeSubPixel(s01, istate); if (!s10.sampled()) computeSubPixel(s10, istate); if (!s11.sampled()) computeSubPixel(s11, istate); if (stepSize > minStepSize) { if (s00.isDifferent(s01, thresh) || s00.isDifferent(s10, thresh) || s00.isDifferent(s11, thresh) || s01.isDifferent(s11, thresh) || s10.isDifferent(s11, thresh) || s01.isDifferent(s10, thresh)) { stepSize >>= 1; thresh *= 2; refineSamples(samples, sbw, x, y, stepSize, thresh, istate); refineSamples(samples, sbw, x + stepSize, y, stepSize, thresh, istate); refineSamples(samples, sbw, x, y + stepSize, stepSize, thresh, istate); refineSamples(samples, sbw, x + stepSize, y + stepSize, stepSize, thresh, istate); return; } } // interpolate remaining samples float ds = 1.0f / stepSize; for (int i = 0; i <= stepSize; i++) for (int j = 0; j <= stepSize; j++) if (!samples[x + i + (y + j) * sbw].processed()) ImageSample.bilerp(samples[x + i + (y + j) * sbw], s00, s01, s10, s11, i * ds, j * ds); }
private void computeSubPixel(ImageSample sample, IntersectionState istate) { float x = sample.rx; float y = sample.ry; double q0 = QMC.halton(1, sample.i); double q1 = QMC.halton(2, sample.i); double q2 = QMC.halton(3, sample.i); if (superSampling > 1) { // multiple sampling sample.add(scene.getRadiance(istate, x, y, q1, q2, q0, sample.i, 4, null)); for (int i = 1; i < superSampling; i++) { double time = QMC.mod1(q0 + i * invSuperSampling); double lensU = QMC.mod1(q1 + QMC.halton(0, i)); double lensV = QMC.mod1(q2 + QMC.halton(1, i)); sample.add(scene.getRadiance(istate, x, y, lensU, lensV, time, sample.i + i, 4, null)); } sample.scale((float)invSuperSampling); } else { // single sample sample.set(scene.getRadiance(istate, x, y, q1, q2, q0, sample.i, 4, null)); } }
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); }