光线追踪算法(计算机图形学)
1.Point3d类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RayTracing { public class Point3d { private double _x; private double _y; private double _z; public double X { get => _x; set => _x = value; } public double Y { get => _y; set => _y = value; } public double Z { get => _z; set => _z = value; } public Point3d() { this.X = 0; this.Y = 0; this.Z = 0; } public Point3d(double x,double y,double z) { this.X = x; this.Y = y; this.Z = z; } public static Vector3d operator -(Point3d a,Point3d b) { return new Vector3d(a.X - b.X, a.Y - b.Y, a.Z - b.Z); } public static Vector3d operator -(Point3d a,Vector3d b) { return new Vector3d(a.X - b.X, a.Y - b.Y, a.Z - b.Z); } public static Vector3d operator -(Vector3d a,Point3d b) { return new Vector3d(a.X - b.X, a.Y - b.Y, a.Z - b.Z); } } }
2.Vector3d类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RayTracing { public class Vector3d { private double _x; private double _y; private double _z; public double X { get => _x; set => _x = value; } public double Y { get => _y; set => _y = value; } public double Z { get => _z; set => _z = value; } public Vector3d() { this.X = 0; this.Y = 0; this.Z = 0; } public Vector3d(double x,double y,double z) { this.X = x; this.Y = y; this.Z = z; } public static double operator *(Vector3d a,Vector3d b) { return (double)(a.X * b.X + a.Y * b.Y + a.Z * b.Z); } public static Vector3d operator *(double a, Vector3d b) { return new Vector3d(a * b.X, a * b.Y, a * b.Z); } public static Vector3d operator *(Vector3d b, double a) { return new Vector3d(a * b.X, a * b.Y, a * b.Z); } public static Vector3d operator +(Vector3d vec1, Vector3d vec2) { return new Vector3d(vec1.X + vec2.X, vec1.Y + vec2.Y, vec1.Z + vec2.Z); } public static Point3d operator +(Point3d a, Vector3d b) { return new Point3d(a.X + b.X, a.Y + b.Y, a.Z + b.Z); } public static Vector3d operator -(Vector3d vec1, Vector3d vec2) { return new Vector3d(vec1.X - vec2.X, vec1.Y - vec2.Y, vec1.Z - vec2.Z); } public double Magnitude() { return Math.Sqrt(X * X + Y * Y + Z * Z); } public void Normalize() { double d = Magnitude(); X = X / d; Y = Y / d; Z = Z / d; } public Vector3d GetNorVec() { double d = Magnitude(); return new Vector3d(X / d, Y / d, Z / d); } } }
3.Ray类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RayTracing { public class Ray { private Point3d _origin; private Vector3d _direction; internal Point3d Origin { get => _origin; set => _origin = value; } internal Vector3d Direction { get => _direction; set => _direction = value; } public Ray() { this.Origin = new Point3d(); this.Direction = new Vector3d(); } public Ray(Point3d pd,Vector3d vt) { this.Origin = pd; this.Direction = vt; } } }
4.GeometryObject类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RayTracing { public abstract class GeometryObject { private Material _mat = new Material(); internal Material Mat { get => _mat; set => _mat = value; } public abstract bool isHit(Ray ray, ShadeRec rec); } }
5.Sphere类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RayTracing { class Sphere:GeometryObject { private Point3d _o; private double radiu; public double Radiu { get => radiu; set => radiu = value; } internal Point3d O { get => _o; set => _o = value; } public Sphere() { this.O = new Point3d(0,0,0); this.Radiu = 1.0; } public Sphere(Point3d pd,double r) { this.O = pd; this.Radiu = r; } public override bool isHit(Ray ray, ShadeRec rec) { double t; Vector3d temp = ray.Origin - O; double a = ray.Direction * ray.Direction; double b = 2.0 * (ray.Direction * temp); double c = temp * temp - Radiu * Radiu; double delta = b * b - 4 * a * c; if (delta >= 0) { t = (-b - Math.Sqrt(delta)) / (2 * a); if (t < 0) { t = (-b + Math.Sqrt(delta)) / (2 * a); } rec.HitT = t; rec.HitPoint = ray.Origin + t * ray.Direction; Vector3d v = rec.HitPoint - O; rec.Normal = v.GetNorVec(); rec.IsHit = true; rec.HitObjMat = Mat; return true; } else { rec.IsHit = false; return false; } } } }
6.Material类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RayTracing { public class Material { private double _ks=0.8; private double _kd=0.5; private double _ns=50; private Scolor _scolor=new Scolor(); public double Ks { get => _ks; set => _ks = value; } public double Kd { get => _kd; set => _kd = value; } public double Ns { get => _ns; set => _ns = value; } internal Scolor Scolor { get => _scolor; set => _scolor = value; } public Material() { this.Kd = 0; this.Ks = 0; this.Ns = 0; this.Scolor = new Scolor(0, 0, 0); } public Material(double kd,double ks,double ns,Scolor scolor) { this.Kd = kd; this.Ks = ks; this.Ns = ns; this.Scolor = scolor; } } }
7.ShadeRec类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RayTracing { public class ShadeRec { private bool _isHit; private Point3d _hitPoint; private Vector3d _normal; private Material _hitObjMat; private double _hitT; public bool IsHit { get => _isHit; set => _isHit = value; } public double HitT { get => _hitT; set => _hitT = value; } internal Point3d HitPoint { get => _hitPoint; set => _hitPoint = value; } internal Vector3d Normal { get => _normal; set => _normal = value; } internal Material HitObjMat { get => _hitObjMat; set => _hitObjMat = value; } } }
8.Scolor类
using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RayTracing { public class Scolor { private double _r; private double _g; private double _b; public double R { get{ return _r; } set{ _r =value; if (_r < 0.0) { _r = 0.0; } if (_r> 1.0) { _r = 1.0; } } } public double G { get { return _g; } set { _g = value; if (_g < 0.0) { _g = 0.0; } if (_g > 1.0) { _g = 1.0; } } } public double B { get { return _b; } set { _b = value; if (_b < 0.0) { _b = 0.0; } if (_b > 1.0) { _b = 1.0; } } } public Scolor() { this.R = 0.0; this.G = 0.0; this.B = 0.0; } public Scolor(double r,double g,double b) { this.R = r; this.G = g; this.B = b; } public static Scolor operator *(double k,Scolor sc) { return new Scolor(k * sc.R,k * sc.G, k * sc.B); } public static Scolor operator *(Scolor sc, double k) { return new Scolor(k * sc.R, k * sc.G, k * sc.B); } public Color Get255() { return Color.FromArgb((int)(R * 255), (int)(G * 255), (int)(B * 255)); } public static Scolor operator +(Scolor s1,Scolor s2) { return new Scolor(s1.R + s2.R, s1.G + s2.G, s1.B + s2.B); } } }
9.LightResourse类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RayTracing { class LightResourse { private double _Ka; private Point3d _Location; private Scolor _LightColor; public double Ka { get => _Ka; set => _Ka = value; } internal Point3d Location { get => _Location; set => _Location = value; } public Scolor LightColor { get => _LightColor; set => _LightColor = value; } public LightResourse() { this.Ka= 0; this.Location = new Point3d(); this.LightColor = new Scolor(); } public LightResourse(double ka,Point3d Location,Scolor SC) { this.Ka = ka; this.Location = Location; this.LightColor = SC; } } }
10.World类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RayTracing { public class World { private List<GeometryObject> _ListObj = new List<GeometryObject> (); public void AddGeoObj(GeometryObject Gto) { _ListObj.Add(Gto); } public void Build() { for (double i = 0; i < 14; i += 2) { Sphere sphere = new Sphere(new Point3d(i - 7 + 1, 0, -20), 1); Material mat = new Material(0.6, 0.8, 60, new Scolor(i / 14, 1 - i / 14, i / 14)); sphere.Mat = mat; AddGeoObj(sphere); } } public ShadeRec HitAll(Ray ray) { ShadeRec srNearest= null; ShadeRec sr=new ShadeRec(); double Mint = 1e8; for(int i = 0; i < _ListObj.Count; i++) { if(_ListObj[i].isHit(ray, sr) && (sr.HitT<Mint) ) { srNearest = sr; Mint = sr.HitT; } } return srNearest; } public List<GeometryObject> ListObj { get => _ListObj; set => _ListObj = value; } } }
11.Form1
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace RayTracing { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button2_Click(object sender, EventArgs e) { int hor = 2000; int ver = 1000; double resx = 4.0 / hor; double rexy = 2.0 / ver; //观察点 Point3d eye = new Point3d(0, 0, 5); World world = new World(); world.ListObj.Clear(); world.Build(); //光源 LightResourse lightRes = new LightResourse(0.3, new Point3d(-1, 2, -1), new Scolor(1, 0, 0)); //环境光源 LightResourse envirLight=new LightResourse(0.3,new Point3d(),new Scolor(1, 1, 1)); Bitmap btm = new Bitmap(hor, ver); for (int i = 0; i < hor; i++) { for (int j = 0; j < ver; j++) { //成像平面上的点 Point3d p = new Point3d(-2 + i * resx, 1 - j * rexy, -1); Vector3d dir = p - eye; Ray ray = new Ray(eye, dir); ShadeRec SR = world.HitAll(ray); double LN,RV; if (SR != null) { Material Mat = SR.HitObjMat; Vector3d Ln = (lightRes.Location - SR.HitPoint).GetNorVec(); //计算R:反射的单位矢量 Vector3d Rn = (2.0 * (SR.Normal * Ln) * SR.Normal - Ln).GetNorVec(); Vector3d v = (eye - SR.HitPoint).GetNorVec(); LN = Ln * SR.Normal; RV = Rn * v; if (LN < 0) { LN = 0; } if (RV < 0) { RV = 0; } Scolor sco1 = envirLight.LightColor * envirLight.Ka; Scolor sco2 = lightRes.LightColor * Mat.Kd * LN; Scolor sco3 = lightRes.LightColor * Mat.Ks * Math.Pow(RV, Mat.Ns); //漫反射+环境光+phone Scolor idiffuse = sco1 + sco2 + sco3; btm.SetPixel(i, j, idiffuse.Get255()); } else { btm.SetPixel(i, j, Color.FromArgb(i/10,j/10,(i+j)/30)); } } } pictureBox1.BackgroundImage = btm; } private void trackBar1_Scroll(object sender, EventArgs e) { } } }
效果:
在加阴影的时候出现了BUG如图
小球只渲染出一半
原因:
这行代码要写在for循环里面
效果: