public static Vec trace(Vec origin, Vec direction) { var hitPoint = new Vec(0, 0, 0); var normal = new Vec(0, 0, 0); var result = new Vec(0, 0, 0); double attenuation = 1.0; var newDirection = new Vec(direction.x, direction.y, direction.z); var newOrigin = new Vec(origin.x, origin.y, origin.z); for (int bounceCount = 3; bounceCount > 0; --bounceCount) { Hit hitType = rayMarching(newOrigin, newDirection, ref hitPoint, ref normal); if (hitType == Hit.None) { break; } if (hitType == Hit.Figure) // Specular bounce on a letter. No color acc. { newDirection.minusM(normal.times(normal.dot(newDirection) * 2.0)); newOrigin = hitPoint.plus(newDirection.times(0.1)); attenuation *= 0.2; } else if (hitType == Hit.Wall) { double incidence = normal.dot(lightDirection); double p = 6.283185 * rnd.NextDouble(); double c = rnd.NextDouble(); double s = Math.Sqrt(1.0 - c); double g = normal.z < 0 ? -1 : 1; double u = -1 / (g + normal.z); double v = normal.x * normal.y * u; Vec a = new Vec(v, g + normal.y * normal.y * u, -normal.y); a.timesM(s * Math.Cos(p)); Vec b = new Vec(1 + g * normal.x * normal.x * u, g * v, -g * normal.x); b.timesM(s * Math.Sin(p)); newDirection = a; newDirection.plusM(b); newDirection.plusM(normal.times(Math.Sqrt(c))); newOrigin = hitPoint.plus(newDirection.times(0.1)); attenuation *= 0.2; var ptAbove = hitPoint.plus(normal.times(0.1)); if (incidence > 0) { var tmp = rayMarching(ptAbove, lightDirection, ref hitPoint, ref normal); if (tmp == Hit.Sun) { result.plusM(colorWall.times(attenuation * incidence)); } } } else if (hitType == Hit.Sun) { result.plusM(colorSun.times(attenuation)); break; } } return(result); }
public void run(Vec position, Vec dirObserver, int samplesCount, int w, int h) { Vec dirLeft = (new Vec(dirObserver.z, 0, -dirObserver.x)).normalize(); //dirLeft.timesM(1.0 / w); dirLeft.timesM(1.0 / h); // Cross-product to get the up vector Vec dirUp = new Vec(dirObserver.y * dirLeft.z - dirObserver.z * dirLeft.y, dirObserver.z * dirLeft.x - dirObserver.x * dirLeft.z, dirObserver.x * dirLeft.y - dirObserver.y * dirLeft.x); dirUp.normalizeM(); dirUp.timesM(1.0 / h); byte[] pixels = new byte[3 * w * h]; for (int y = h; y > 0; --y) { for (int x = w; x > 0; --x) { Vec color = new Vec(0, 0, 0); for (int p = samplesCount; p > 0; --p) { var randomLeft = dirLeft.times(x - w / 2 + rnd.NextDouble()); var randomUp = dirUp.times((y - h / 2 + rnd.NextDouble())); var randomizedDir = new Vec(dirObserver.x, dirObserver.y, dirObserver.z); randomizedDir.plusM(randomLeft); randomizedDir.plusM(randomUp); randomizedDir.normalizeM(); //Hit hType = Hit.None; var incr = trace(position, randomizedDir); if (y < h / 2) { ; } color.plusM(incr); } // Reinhard tone mapping color.timesM(241.0 / samplesCount); color = new Vec((color.x + 14.0) / (color.x + 255.0), (color.y + 14.0) / (color.y + 255.0), (color.z + 14.0) / (color.z + 255.0)); color.timesM(255.0); int index = 3 * (w * y - w + x - 1); pixels[index] = (byte)color.x; pixels[index + 1] = (byte)color.y; pixels[index + 2] = (byte)color.z; } } Output.createBMP(pixels, w, h, "card.bmp"); }
public static Hit rayMarching(Vec origin, Vec direction, ref Vec hitPos, ref Vec hitNorm) { Hit hitType = Hit.None; int noHitCount = 0; double d = 0.0; // distance from the closest object in the world. for (double totalD = 0; totalD < 100; totalD += d) { hitPos = origin.plus(direction.times(totalD)); d = queryDatabase(hitPos, ref hitType); if (d < 0.01 || ++noHitCount > 99) { Hit temp = Hit.None; double normX = queryDatabase(hitPos.plus(dX), ref temp) - d; double normY = queryDatabase(hitPos.plus(dY), ref temp) - d; double normZ = queryDatabase(hitPos.plus(dZ), ref temp) - d; hitNorm = (new Vec(normX, normY, normZ)).normalize(); return(hitType); } } return(Hit.None); }