public Color Shade(Ray ray, Scene scene, int depth) { if (depth == 0) { var ambient = scene.AmbientLight.Color; return(new Color(ambient.R, ambient.G, ambient.B)); } HitInfo hitInfo = new HitInfo(); if (scene.HitTest(ray, ref hitInfo, 0.001f, float.PositiveInfinity)) { var emitted = hitInfo.ModelHit.Material.Emitted(hitInfo.TexCoord.X, hitInfo.TexCoord.Y); if (hitInfo.ModelHit.Material.Scatter(ref ray, ref hitInfo, out Color attenuation, out Ray scattered)) { return(emitted + attenuation * Shade(scattered, scene, depth - 1)); } return(emitted); } return(scene.AmbientLight.Color); }
public override void Render(Scene scene, Camera camera) { scene.Preprocess(); if (camera is LensCamera lensCamera && lensCamera.AutoFocus) { var ray = lensCamera.GetRay(0.5f, 0.5f); var hitInfo = new HitInfo(); var hit = scene.HitTest(ray, ref hitInfo, 0.001f, float.PositiveInfinity); if (hit) { lensCamera.FocusDistance = hitInfo.Distance; } else { lensCamera.FocusDistance = 100000f; } } int width = Resolution; int height = (int)(width / camera.AspectRatio); var image = new Texture(width, height); AbstractSampler <Vector2> sampler = new ThreadSafeSampler <Vector2>(Sampling, Samples); for (int k = 0; k < Samples; k++) { try { Parallel.For(0, width, new ParallelOptions { CancellationToken = CancellationToken }, i => { for (int j = 0; j < height; j++) { var sample = sampler.GetSample(k); float u = (i + sample.X) / (width - 1); float v = (j + sample.Y) / (height - 1); Ray ray = camera.GetRay(u, v); var shade = Shade(ray, scene, MaxDepth); image.Bloom(shade, i, j, (int)shade.GetBrightness(), Bloom); image[i, j] += shade; } }); } catch (Exception e) { Log.Warn(e); } if (CancellationToken.IsCancellationRequested) { return; } if (k % SamplesRenderStep == 0 || k == Samples - 1) { var output = new Texture(image); output.Process(c => (c / (k + 1)).Clamp()); if (GammaCorrection) { output.AutoGammaCorrect(); } var percentage = (k + 1) * 100 / Samples; OnFrameReady?.Invoke(percentage, output); } } }
private Color Trace(ref Ray ray, Scene scene, int depth) { HitTestResult hittest = scene.HitTest(ref ray); if (hittest == null) { return(Color.Black); } var d = hittest.Ray.Direction; var pos = hittest.Distance * hittest.Ray.Direction + hittest.Ray.Position; //交点位置 var normal = hittest.Shape.GetNormal(pos); //交点法向量 var cosa = normal * d; //-Cos(入射角) var reflectDir = d - 2 * cosa * normal; //反射方向 Color color; if (cosa < 0) { color = GetNaturalColor(hittest.Shape, ref pos, ref normal, ref reflectDir, scene); } else { color = Color.Black; } if (depth <= 0) { return(color); } depth--; //追踪反射光线 var ray1 = new Ray(pos, reflectDir); var rcolor1 = Trace(ref ray1, scene, depth); rcolor1.Multiply(hittest.Shape.Surface.Specular(pos)); //追踪折射光线 var n = hittest.Shape.Surface.Refraction(pos);// 折射率 if (n > 0) { if (cosa > 0) { n = 1 / n; } var f = Math.Acos(-cosa); //入射角 var t = Math.Asin(Math.Sqrt(1 - cosa * cosa)); //折射角 //fresnel公式,计算反射比 double s1 = Math.Sin(f - t); double s2 = Math.Sin(f + t); double t1 = Math.Tan(f - t); double t2 = Math.Tan(f + t); var factor = (float)(((s1 * s1) / (s2 * s2) + (t1 * t1) / (t2 * t2)) / 2); if (factor < 1 && factor >= 0) //等于1时发生了全反射 { rcolor1.Multiply(factor); //折射定律矢量形式:n2 * e2 - n1 * e1 = (n2 * cos(t) - n1 * cos(f)) * normal var dir = ((float)(n * Math.Cos(t) - Math.Cos(f)) * normal) * (1 / n); dir.Normalize(); var ray2 = new Ray(pos, dir); var rcolor2 = Trace(ref ray2, scene, depth); rcolor2.Multiply(1 - factor); color.Add(ref rcolor2); } } color.Add(ref rcolor1); return(color); }