public Polygon(Polygon s) { points = new List <int>(s.points); host = s.host; DPen = s.DPen.Clone() as Pen; Normal = new PointXYZ(s.Normal); }
//отражение /* * Направление отраженного луча определяется по закону: * отраженный луч = падающий луч - 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 void LineRotateRad(float rang, PointXYZ p1, PointXYZ p2) { p2 = new PointXYZ(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z); p2 = PointXYZ.normal(p2); float[,] mt = GetMatrix(); ApplyMatrix(RotateAroundLine(mt, p1, p2, rang)); }
//модель освещения 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 Surface(Surface m) { reflection = m.reflection; refraction = m.refraction; environment = m.environment; ambient = m.ambient; diffuse = m.diffuse; Color = new PointXYZ(m.Color); }
PointXYZ MakeCamera(Polygon side) { //нормаль стороны комнаты PointXYZ normal = Polygon.GetNormal(side); // центр стороны комнаты PointXYZ center = (ul + ur + dl + dr) / 4; return(center + normal * 11); }
public void ScaleAroundCenter(float xs, float ys, float zs) { float[,] pnts = GetMatrix(); PointXYZ p = GetCenter(); pnts = ApplyOffset(pnts, -p.X, -p.Y, -p.Z); pnts = ApplyScale(pnts, xs, ys, zs); pnts = ApplyOffset(pnts, p.X, p.Y, p.Z); ApplyMatrix(pnts); }
public static PointXYZ normal(PointXYZ p) { float z = (float)Math.Sqrt((float)(p.X * p.X + p.Y * p.Y + p.Z * p.Z)); if (z == 0) { return(new PointXYZ(p)); } return(new PointXYZ(p.X / z, p.Y / z, p.Z / z)); }
public PointXYZ(PointXYZ p) { if (p == null) { return; } X = p.X; Y = p.Y; Z = p.Z; }
public static PointXYZ GetNormal(Polygon S) { if (S.points.Count() < 3) { return(new PointXYZ(0, 0, 0)); } PointXYZ U = S.GetPoint(1) - S.GetPoint(0); PointXYZ V = S.GetPoint(S.points.Count - 1) - S.GetPoint(0); PointXYZ normal = U * V; return(PointXYZ.normal(normal)); }
public Form1() { InitializeComponent(); cameraPoint = new PointXYZ(); ul = new PointXYZ(); ur = new PointXYZ(); dl = new PointXYZ(); dr = new PointXYZ(); h = pictureBox1.Height; w = pictureBox1.Width; pictureBox1.Image = new Bitmap(w, h); }
private PointXYZ GetCenter() { PointXYZ res = new PointXYZ(0, 0, 0); foreach (PointXYZ p in Points) { res.X += p.X; res.Y += p.Y; res.Z += p.Z; } res.X /= Points.Count(); res.Y /= Points.Count(); res.Z /= Points.Count(); return(res); }
Figure MakeRoom() { Figure room = Figure.CreateCube(10); ul = room.Polygons[0].GetPoint(0); ur = room.Polygons[0].GetPoint(1); dr = room.Polygons[0].GetPoint(2); dl = room.Polygons[0].GetPoint(3); room.SetPen(new Pen(Color.Gray)); room.Polygons[0].DPen = new Pen(Color.Green); room.Polygons[1].DPen = new Pen(Color.Green); room.Polygons[2].DPen = new Pen(Color.GreenYellow); room.Polygons[3].DPen = new Pen(Color.GreenYellow); room.FigureSurface = new Surface(0, 0, 0.05f, 0.7f); return(room); }
private static float[,] RotateAroundLine(float[,] transform_matrix, PointXYZ start, PointXYZ dir, float angle) { float cos_angle = (float)Math.Cos(angle); float sin_angle = (float)Math.Sin(angle); float val00 = dir.X * dir.X + cos_angle * (1 - dir.X * dir.X); float val01 = dir.X * (1 - cos_angle) * dir.Y + dir.Z * sin_angle; float val02 = dir.X * (1 - cos_angle) * dir.Z - dir.Y * sin_angle; float val10 = dir.X * (1 - cos_angle) * dir.Y - dir.Z * sin_angle; float val11 = dir.Y * dir.Y + cos_angle * (1 - dir.Y * dir.Y); float val12 = dir.Y * (1 - cos_angle) * dir.Z + dir.X * sin_angle; float val20 = dir.X * (1 - cos_angle) * dir.Z + dir.Y * sin_angle; float val21 = dir.Y * (1 - cos_angle) * dir.Z - dir.X * sin_angle; float val22 = dir.Z * dir.Z + cos_angle * (1 - dir.Z * dir.Z); float[,] rotateMatrix = new float[, ] { { val00, val01, val02, 0 }, { val10, val11, val12, 0 }, { val20, val21, val22, 0 }, { 0, 0, 0, 1 } }; return(ApplyOffset(MultiplyMatrix(ApplyOffset(transform_matrix, -start.X, -start.Y, -start.Z), rotateMatrix), start.X, start.Y, start.Z)); }
public void RotateArondRad(float rangle, string type) { float[,] mt = GetMatrix(); PointXYZ center = GetCenter(); switch (type) { case "CX": mt = ApplyOffset(mt, -center.X, -center.Y, -center.Z); mt = ApplyRotation_X(mt, rangle); mt = ApplyOffset(mt, center.X, center.Y, center.Z); break; case "CY": mt = ApplyOffset(mt, -center.X, -center.Y, -center.Z); mt = ApplyRotation_Y(mt, rangle); mt = ApplyOffset(mt, center.X, center.Y, center.Z); break; case "CZ": mt = ApplyOffset(mt, -center.X, -center.Y, -center.Z); mt = ApplyRotation_Z(mt, rangle); mt = ApplyOffset(mt, center.X, center.Y, center.Z); break; case "X": mt = ApplyRotation_X(mt, rangle); break; case "Y": mt = ApplyRotation_Y(mt, rangle); break; case "Z": mt = ApplyRotation_Z(mt, rangle); break; default: break; } ApplyMatrix(mt); }
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 virtual bool FigureIntersection(Ray r, out float intersect, out PointXYZ normal) { intersect = 0; normal = null; Polygon side = null; foreach (Polygon figure_side in Polygons) { if (figure_side.points.Count == 3) { if (TriangleInter(r, figure_side.GetPoint(0), figure_side.GetPoint(1), figure_side.GetPoint(2), out float t) && (intersect == 0 || t < intersect)) { intersect = t; side = figure_side; } } else if (figure_side.points.Count == 4) { if (TriangleInter(r, figure_side.GetPoint(0), figure_side.GetPoint(1), figure_side.GetPoint(3), out float t) && (intersect == 0 || t < intersect)) { intersect = t; side = figure_side; } else if (TriangleInter(r, figure_side.GetPoint(1), figure_side.GetPoint(2), figure_side.GetPoint(3), out t) && (intersect == 0 || t < intersect)) { intersect = t; side = figure_side; } } } if (intersect != 0) { normal = Polygon.GetNormal(side); FigureSurface.Color = new PointXYZ(side.DPen.Color.R / 255f, side.DPen.Color.G / 255f, side.DPen.Color.B / 255f); return(true); } 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); }
public Ray(Ray r) { Start = r.Start; End = r.End; }
public void CalculateSideNormal() { Normal = GetNormal(this); }
public static float ScalarMul(PointXYZ p1, PointXYZ p2) { return(p1.X * p2.X + p1.Y * p2.Y + p1.Z * p2.Z); }
private void button1_Click(object sender, EventArgs e) { Figure room = MakeRoom(); cameraPoint = MakeCamera(room.Polygons[0]); figuresList.Add(room); raySource = new Lamp(new PointXYZ(0f, 2f, 4.9f), new PointXYZ(1f, 1f, 1f)); AddBigCube(); AddLittleCube(); /* * Учитывая разницу между размером комнаты и экранным отображение приводим координаты к пикселям */ pixels = new PointXYZ[w, h]; pixels_color = new Color[w, h]; PointXYZ step_up = (ur - ul) / (w - 1); //отношение ширины комнаты к ширине экрана PointXYZ step_down = (dr - dl) / (w - 1); //отношение высоты комнаты к высоте экрана PointXYZ up = new PointXYZ(ul); PointXYZ down = new PointXYZ(dl); for (int i = 0; i < w; ++i) { PointXYZ step_y = (up - down) / (h - 1); PointXYZ d = new PointXYZ(down); for (int j = 0; j < h; ++j) { pixels[i, j] = d; d += step_y; } up += step_up; down += step_down; } /* * Количество первичных лучей также известно – это общее * количество пикселей видового окна */ for (int i = 0; i < w; ++i) { for (int j = 0; j < h; ++j) { Ray r = new Ray(cameraPoint, pixels[i, j]); r.Start = new PointXYZ(pixels[i, j]); PointXYZ color = RTAlgotithm(r, 10, 1);//луч,кол-во итераций,коэфф if (color.X > 1.0f || color.Y > 1.0f || color.Z > 1.0f) { color = PointXYZ.normal(color); } pixels_color[i, j] = Color.FromArgb((int)(255 * color.X), (int)(255 * color.Y), (int)(255 * color.Z)); } } for (int i = 0; i < w; ++i) { for (int j = 0; j < h; ++j) { (pictureBox1.Image as Bitmap).SetPixel(i, j, pixels_color[i, j]); } pictureBox1.Invalidate(); } }
public Ray(PointXYZ st, PointXYZ end) { Start = new PointXYZ(st); End = PointXYZ.normal(end - st); }
public Lamp(PointXYZ p, PointXYZ c) { point_light = new PointXYZ(p); color_light = new PointXYZ(c); }
public void LineRotate(float ang, PointXYZ p1, PointXYZ p2) { ang = ang * (float)Math.PI / 180; LineRotateRad(ang, p1, p2); }