//отражение /* * Направление отраженного луча определяется по закону: * отраженный луч = падающий луч - 2* нормаль к точке попадания луча на сторону на скалярное произведение падающего луча и нормали * из презентации */ public Ray Reflect(PointXYZ hit_point, PointXYZ normal) { //высчитываем направление отраженного луча PointXYZ reflect_dir = End - 2 * normal * PointXYZ.ScalarMul(End, normal); return(new Ray(hit_point, hit_point + reflect_dir)); }
//модель освещения public PointXYZ Lumiance(PointXYZ hit_point, PointXYZ normal, PointXYZ material_color, float diffuse_coef) { PointXYZ direction = PointXYZ.normal(point_light - hit_point);// направление луча //если угол между нормалью и направлением луча больше 90 градусов,то диффузное освещение равно 0 PointXYZ diff = diffuse_coef * color_light * Math.Max(PointXYZ.ScalarMul(normal, direction), 0); return(new PointXYZ(diff.X * material_color.X, diff.Y * material_color.Y, diff.Z * material_color.Z)); }
public bool TriangleInter(Ray r, PointXYZ p0, PointXYZ p1, PointXYZ p2, out float intersect) { intersect = -1; PointXYZ edge1 = p1 - p0; PointXYZ edge2 = p2 - p0; PointXYZ h = r.End * edge2; float a = PointXYZ.ScalarMul(edge1, h); if (a > -0.0001 && a < 0.0001) { return(false); // Этот луч параллелен этому треугольнику. } float f = 1.0f / a; PointXYZ s = r.Start - p0; float u = f * PointXYZ.ScalarMul(s, h); if (u < 0 || u > 1) { return(false); } PointXYZ q = s * edge1; float v = f * PointXYZ.ScalarMul(r.End, q); if (v < 0 || u + v > 1) { return(false); } // На этом этапе мы можем вычислить t, чтобы узнать, где находится точка пересечения на линии. float t = f * PointXYZ.ScalarMul(edge2, q); if (t > 0.0001) { intersect = t; return(true); } else //Это означает, что есть пересечение линий, но не пересечение лучей. { return(false); } }
//преломление //все вычисления взяты из презентации public Ray Refract(PointXYZ hit_point, PointXYZ normal, float refraction, float refract_coef) { Ray res_ray = new Ray(); float sclr = PointXYZ.ScalarMul(normal, End); /* * Если луч падает,то он проходит прямо,не преломляясь */ float n1n2div = refraction / refract_coef; float theta_formula = 1 - n1n2div * n1n2div * (1 - sclr * sclr); if (theta_formula >= 0) { float cos_theta = (float)Math.Sqrt(theta_formula); res_ray.Start = new PointXYZ(hit_point); res_ray.End = PointXYZ.normal(End * n1n2div - (cos_theta + n1n2div * sclr) * normal); return(res_ray); } else { return(null); } }
public PointXYZ RTAlgotithm(Ray r, int iter, float env) { if (iter <= 0) { return(new PointXYZ(0, 0, 0)); } float intersectionRayFigure = 0;// позиция точки пересечения луча с фигурой на луче //нормаль стороны фигуры,с которой пересекся луч PointXYZ normal = null; Surface surface = new Surface(); PointXYZ res_color = new PointXYZ(0, 0, 0); bool isFigureRefrat = false; foreach (var f in figuresList) { if (f.FigureIntersection(r, out float intersect, out PointXYZ norm)) { if (intersect < intersectionRayFigure || intersectionRayFigure == 0)// нужна ближайшая фигура к точке наблюдения { intersectionRayFigure = intersect; normal = norm; surface = new Surface(f.FigureSurface); } } } if (intersectionRayFigure == 0) //если не пересекается с фигурой { return(new PointXYZ(0, 0, 0)); //Луч уходит в свободное пространство .Возвращаем значение по умолчанию } //угол между направление луча и нормалью стороны острый //определяем из какой среды в какую //http://edu.glavsprav.ru/info/zakon-prelomleniya-sveta/ if (PointXYZ.ScalarMul(r.End, normal) > 0) { normal *= -1; isFigureRefrat = true; } //Точка пересечения луча с фигурой PointXYZ hit_point = r.Start + r.End * intersectionRayFigure; /*В точке пересечения луча с объектом строится три вторичных * луча – один в направлении отражения (1), второй – в направлении * источника света (2), третий в направлении преломления * прозрачной поверхностью (3). */ //цвет коэффициент принятия фонового освещения PointXYZ lightk = raySource.color_light * surface.ambient; lightk.X = (lightk.X * surface.Color.X); lightk.Y = (lightk.Y * surface.Color.Y); lightk.Z = (lightk.Z * surface.Color.Z); res_color += lightk; //диффузное освещение //если точка пересечения луча с объектом видна из источника света float raydist = (raySource.point_light - hit_point).Distance(); //координаты источника света луча Ray tmp_r = new Ray(hit_point, raySource.point_light); foreach (Figure fig in figuresList) { if (fig.FigureIntersection(tmp_r, out float t, out PointXYZ n)) { if (t < raydist || t > 0.0001) { res_color += raySource.Lumiance(hit_point, normal, surface.Color, surface.diffuse); } } } /*Для отраженного луча * проверяется возможность * пересечения с другими * объектами сцены. * * Если пересечений нет, то * интенсивность и цвет * отраженного луча равна * интенсивности и цвету фона. * * Если пересечение есть, то в * новой точке снова строится * три типа лучей – теневые, * отражения и преломления. */ if (surface.reflection > 0) { Ray refray = r.Reflect(hit_point, normal); res_color += surface.reflection * RTAlgotithm(refray, iter - 1, env); } //расчет коэфициентов преломления if (surface.refraction > 0) { //взависимости от того,из какой среды в какую,будет меняться коэффициент приломления float refractk; if (isFigureRefrat) { refractk = surface.environment; } else { refractk = 1 / surface.environment; } Ray refracted_ray = r.Refract(hit_point, normal, surface.refraction, refractk);//создаем приломленный луч /* * Как и в предыдущем случае, * проверяется пересечение вновь * построенного луча с объектами, * и, если они есть, в новой точке * строятся три луча, если нет – используется интенсивность и * цвет фона. */ if (refracted_ray != null) { res_color += surface.refraction * RTAlgotithm(refracted_ray, iter - 1, surface.environment); } } return(res_color); }