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 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); }