cscの作法 その22 レイトレース


#概要

cscの作法、調べてみた。
練習問題やってみた。

#練習問題

レイトレースを表示せよ。

#参考にしたページ

https://www.codeproject.com/Articles/19732/Simple-Ray-Tracing-in-C

#写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/e6ae041e-3ce0-cfc0-7d77-d5c180c53ddd.png)


#サンプルコード


```
using System;
using System.Windows.Forms;
using System.Drawing;

class form1: Form {
	form1() {
		Text = "ray";
		ClientSize = new Size(300, 300);
	}
	protected override void OnPaint(PaintEventArgs e) {
		Graphics g = e.Graphics;
		Color clrBackground = Color.Black;
		g.FillRectangle(new SolidBrush(clrBackground), new Rectangle(0, 0, 200, 200));
		Rectangle rect = new Rectangle(0, 0, 200, 200);
		System.Collections.ArrayList obj3dArrayList;
		obj3dArrayList = new System.Collections.ArrayList();
		obj3dArrayList.Add(new Sphere(0.0, 0.0, 90.0, 100.0, 0.0, 0.0, 255.0));
		obj3dArrayList.Add(new Sphere(-180.0, -130.0, -110.0, 15.0, 255.0, 0.0, 0.0));
		obj3dArrayList.Add(new Sphere(-140.0, -140.0, -150.0, 20.0, 255.0, 200.0, 0.0));
		Graphics graphics = g;
		double px = (double) 0,
			py = (double) 0,
			pz = (double) 500;
		double lpx = (double) 200,
			lpy = (double) 200,
			lpz = (double) 200;
		double lvx = (double) -1.0,
			lvy = (double) -1.0,
			lvz = (double) -1.0;
		double fMax = 200.0;
		for (int i = rect.Left; i <= rect.Right; i++)
		{
			double x = Sphere.GetCoord(rect.Left, rect.Right, -fMax, fMax, i);
			for (int j = rect.Top; j <= rect.Bottom; j++)
			{
				double y = Sphere.GetCoord(rect.Top, rect.Bottom, fMax, -fMax, j);
				double t = 1.0E10;
				double vx = x - px, vy = y - py, vz = -pz;
				double mod_v = Sphere.modv(vx, vy, vz);
				vx = vx / mod_v;
				vy = vy / mod_v;
				vz = vz / mod_v;
				bool bShadow = false;
				Sphere spherehit = null;
				for (int k = 0; k < (int) obj3dArrayList.Count; k++)
				{
					Sphere sphn = (Sphere) obj3dArrayList[k];
					double taux = Sphere.GetSphereIntersec(sphn.cx, sphn.cy, sphn.cz, sphn.radius, px, py, pz, vx, vy, vz);
					if (taux < 0) continue;
					if (taux > 0 && taux < t)
					{
						t = taux;
						spherehit = sphn;
					}
				}
				Color color = Color.FromArgb(10, 20, 10);
				if (spherehit != null)
				{
					double itx = px + t * vx, ity = py + t * vy, itz = pz + t * vz;
					double tauxla = Sphere.GetSphereIntersec(spherehit.cx, spherehit.cy, spherehit.cz, spherehit.radius, lpx, lpy, lpz, itx - lpx, ity - lpy, itz - lpz);
					for (int k = 0; k < (int)obj3dArrayList.Count; k++)
					{
						Sphere sphnb = (Sphere)(obj3dArrayList[k]);
						if (sphnb != spherehit)
						{
							double tauxlb = Sphere.GetSphereIntersec(sphnb.cx, sphnb.cy, sphnb.cz, sphnb.radius, lpx, lpy, lpz, itx - lpx, ity - lpy, itz - lpz);
							if (tauxlb > 0 && tauxla < tauxlb)
							{
								bShadow = true;
								break;
							}
						}
					}
					double cost = Sphere.GetCosAngleV1V2(lvx, lvy, lvz, itx - spherehit.cx, ity - spherehit.cy, itz - spherehit.cz);
					if (cost < 0) cost = 0;
					double fact = 1.0;
					if (bShadow == true) fact = 0.5; else fact = 1.0;
					double rgbR = spherehit.clR * cost * fact;
					double rgbG = spherehit.clG * cost * fact;
					double rgbB = spherehit.clB * cost * fact;
					color = Color.FromArgb((int)rgbR, (int)rgbG, (int)rgbB);
					Pen pen = new Pen(color);
				}
				Brush brs = new SolidBrush(color);
				graphics.FillRectangle(brs, i, j, 1, 1);
				brs.Dispose();
			}
		}
		base.OnPaint(e);
	}
	class Sphere {
	public Sphere(double x, double y, double z, double r, double clr, double clg, double clb) {
		cx = x;
		cy = y;
		cz = z;
		radius = r;
		clR = clr;
		clG = clg;
		clB = clb;
	}
	public static double GetCoord(double i1, double i2, double w1, double w2, double p) {
		return ((p - i1) / (i2 - i1)) * (w2 - w1) + w1;
	}
	public static double modv(double vx, double vy, double vz) {
		return System.Math.Sqrt(vx * vx + vy * vy + vz * vz);
	}
	void Move(double vx, double vy, double vz) {
		cx += vx;
		cy += vy;
		cz += vz;
	}
	void MoveTo(double vx, double vy, double vz) {
		cx = vx;
		cy = vy;
		cz = vz;
	}
	void RotX(double angle) {
		double y = cy * System.Math.Cos(angle) - cz * System.Math.Sin(angle);
		double z = cy * System.Math.Sin(angle) + cz * System.Math.Cos(angle);
		cy = y;
		cz = z;
	}
	void RotY(double angle) {
		double x = cx * System.Math.Cos(angle) - cz * System.Math.Sin(angle);
		double z = cx * System.Math.Sin(angle) + cz * System.Math.Cos(angle);
		cx = x;
		cz = z;
	}
	public static double GetSphereIntersec(double cx, double cy, double cz, double radius, double px, double py, double pz, double vx, double vy, double vz) {
		double A = (vx * vx + vy * vy + vz * vz);
		double B = 2.0 * (px * vx + py * vy + pz * vz - vx * cx - vy * cy - vz * cz);
		double C = px * px - 2 * px * cx + cx * cx + py * py - 2 * py * cy + cy * cy + pz * pz - 2 * pz * cz + cz * cz - radius * radius;
		double D = B * B - 4 * A * C;
		double t = -1.0;
		if (D >= 0)
		{
			double t1 = (-B - System.Math.Sqrt(D)) / (2.0 * A);
			double t2 = (-B + System.Math.Sqrt(D)) / (2.0 * A);
			if (t1 > t2) t = t1; else t = t2;
		}
		return t;
	}
	public static double GetCosAngleV1V2(double v1x, double v1y, double v1z, double v2x, double v2y, double v2z) {
		return (v1x * v2x + v1y * v2y + v1z * v2z) / (modv(v1x, v1y, v1z) * modv(v2x, v2y, v2z));
	}
	public double cx,
		cy,
		cz,
		radius,
		clR,
		clG,
		clB;
	}
	[STAThread]
	public static void Main() {
		Application.Run(new form1());
	}
}





```

以上。