void render(byte[] buf, List <Sphere> spheres, int pixelWidth) { float tm = _timer.gameTime() * 2.0f; int stride = (pixelWidth * 32) / 8; int pixelHeight = buf.Length / stride; int width = pixelWidth; int height = pixelHeight; float invWidth = 1.0f / (float)width; float invHeight = 1.0f / (float)height; float fov = 80.0f; float aspectratio = width / (float)height; float angle = (float)Math.Tan(M_PI * 0.5f * fov / 180.0f); List <Task> tasks = new List <Task>(); for (int i = 0; i < lineTos.Length; i++) { Task task = new Task(obj => { // Trace rays lineToRender toRender = lineTos[(int)obj]; for (int y = toRender.from; y < toRender.to; ++y) { for (int x = 0; x < width; ++x) { float xx = (2.0f * (((float)x + 0.5f) * invWidth) - 1.0f) * angle * aspectratio; float yy = (1.0f - 2.0f * (((float)y + 0.5f) * invHeight)) * angle; Vec3f raydir = new Vec3f(xx, yy, -1.0f); raydir.normalize(); Vec3f raydir2 = new Vec3f(Mat4f.RotationYMatrix(-tm) * raydir); //Vec3f raydir2 = raydir; //Vec3f rayorig = new Vec3f(0.0f, 1.0f + (float)Math.Sin(tm) * 4.0f, -20.0f) + new Vec3f(Mat4f.RotationYMatrix(-tm) * new Vec3f(0.0f, 1.0f, 20.0f)); Vec3f rayorig = new Vec3f(0.0f, 5.0f, -25.0f) + new Vec3f(Mat4f.RotationYMatrix(-tm) * new Vec3f(0.0f, 5.0f, 25.0f)); //Vec3f rayorig = spheres[1].center + new Vec3f(Mat4f.RotationYMatrix(-tm) * new Vec3f(0.0f, 3.0f, 5.0f + tm)); //Vec3f rayorig = new Vec3f(0.0f); //Vec3f rayorig = new Vec3f(0.0f, 10.0f, 0.0f); Vec3f pixel = trace(rayorig, raydir2, spheres, 0); printPixel(buf, x, y, new pBGRA((byte)(pixel.z * 255.0f), (byte)(pixel.y * 255.0f), (byte)(pixel.x * 255.0f), 255), pixelWidth); } } }, i); task.Start(); tasks.Add(task); } foreach (Task t in tasks) { t.Wait(); } }
void render(byte[] buf, List <Sphere> spheres, int pixelWidth) { float tm = _timer.gameTime() * 2.0f; int stride = (pixelWidth * 32) / 8; int pixelHeight = buf.Length / stride; int width = pixelWidth; int height = pixelHeight; float invWidth = 1.0f / (float)width; float invHeight = 1.0f / (float)height; float fov = 40.0f; float aspectratio = width / (float)height; float angle = (float)Math.Tan(M_PI * 0.5f * fov / 180.0f); Parallel.For(0, lineTos.Length, i => { lineToRender toRender = lineTos[i]; for (int y = toRender.from; y < toRender.to; ++y) { for (int x = 0; x < width; ++x) { float xx = (2.0f * (((float)x + 0.5f) * invWidth) - 1.0f) * angle * aspectratio; float yy = (1.0f - 2.0f * (((float)y + 0.5f) * invHeight)) * angle; Vec3f raydir = new Vec3f(xx, yy, -1.0f); raydir.normalize(); Vec3f raydir2 = new Vec3f(Mat4f.RotationYMatrix(-tm) * raydir); Vec3f rayorig = new Vec3f(0.0f, 5.0f, -25.0f) + new Vec3f(Mat4f.RotationYMatrix(-tm) * new Vec3f(0.0f, 5.0f, 25.0f)); Vec3f pixel = trace(rayorig, raydir2, spheres, 0); printPixel(buf, x, y, new pBGRA((byte)(pixel.z * 255.0f), (byte)(pixel.y * 255.0f), (byte)(pixel.x * 255.0f), 255), pixelWidth); } } }); }
public Vec3f trace(Vec3f rayorig, Vec3f raydir, List <Sphere> spheres, int depth) { float tnear = INFINITY; Sphere sphere = null; //find intersection of this ray with the sphere in the scene int spCount = spheres.Count(); for (int i = 0; i < spCount; ++i) { float t0 = INFINITY; float t1 = INFINITY; (bool, float, float)inter = spheres[i].intersect(rayorig, raydir); if (inter.Item1) { t0 = inter.Item2; t1 = inter.Item3; if (t0 < 0) { t0 = t1; } if (t0 < tnear) { tnear = t0; sphere = spheres[i]; } } } //if there's no intersection return black or background color if (sphere == null) { return(new Vec3f(1.0f)); } Vec3f surfaceColor = new Vec3f(0.0f); //color of the ray/surfaceof the object intersected by the ray Vec3f phit = rayorig + raydir * tnear; //point of intersection Vec3f nhit = phit - sphere.center; //normal at the intersection point nhit.normalize(); //normalize normal direction //If the normal and the view direction are not opposite to each other //reverse the normal direction. That also means we are inside the sphere so set //the inside bool to true. Finally reverse the sign of IdotN which we want //positive. float bias = 0.0001f; //add some bias to the point from which we will be tracing bool inside = false; if (raydir.dot(nhit) > 0) { nhit = -1 * nhit; inside = true; } if ((sphere.transparency > 0 || sphere.reflection > 0) && depth < MAX_RAY_DEPTH) { float facingratio = -raydir.dot(nhit); // change the mix value to tweak the effect float fresneleffect = mix((float)Math.Pow(1 - facingratio, 3), 1, 0.1f); // compute reflection direction (not need to normalize because all vectors // are already normalized) Vec3f refldir = raydir - nhit * 2 * raydir.dot(nhit); refldir.normalize(); Vec3f reflection = trace(phit + nhit * bias, refldir, spheres, depth + 1); Vec3f refraction = new Vec3f(0.0f); // if the sphere is also transparent compute refraction ray (transmission) if (sphere.transparency != 0.0f) { float ior = 1.1f; float eta = (inside) ? ior : 1 / ior; // are we inside or outside the surface? float cosi = -nhit.dot(raydir); float k = 1 - eta * eta * (1 - cosi * cosi); Vec3f refrdir = raydir * eta + nhit * (eta * cosi - (float)Math.Sqrt(k)); refrdir.normalize(); refraction = trace(phit - nhit * bias, refrdir, spheres, depth + 1); } // the result is a mix of reflection and refraction (if the sphere is transparent) surfaceColor = (reflection * fresneleffect + refraction * (1.0f - fresneleffect) * sphere.transparency) * sphere.surfaceColor; } else { // it's a diffuse object, no need to raytrace any further for (int i = 0; i < spCount; ++i) { if (spheres[i].emissionColor.x > 0) { // this is a light Vec3f transmission = new Vec3f(1.0f); Vec3f lightDirection = spheres[i].center - phit; lightDirection.normalize(); for (int j = 0; j < spCount; ++j) { if (i != j) { if (spheres[j].intersect(phit + nhit * bias, lightDirection).Item1) { transmission = new Vec3f(0.0f); break; } } } surfaceColor += sphere.surfaceColor * transmission * Math.Max(0.0f, nhit.dot(lightDirection)) * spheres[i].emissionColor; } } } return(surfaceColor + sphere.emissionColor); }