添加阴影
之前添加了漫反射和镜面反射。
球体看起来已经很好了,现在我们要更上一步。添加阴影。
阴影
你的影子,物体的影子都可以说时阴影。
在书上,可以说阴影就是在光源和点之间存在物体。这个物体影响了点的光的计算。
因为点自带环境光,所以影响的是点光和方向光。
分析
既然光源和点之间存在物体就会存在阴影。
按照这个思路,我们计算的就是点到光源,如何计算点到光源之间存不存物体呢?
我们可以利用我们现有绘制函数,因为我们的绘制函数也是判断视口到球体之间是否有交点。
但是,有个问题,就是是否需要判断点和光源的距离? 在绘制阴影之前,我们判断的距离都是无限远。
判断阴影是否也可以使用无限远的设定呢?
首先我们是希望光源到点这个距离或者这个范围内,如果存在物体我们绘制阴影,如果无限远,可能存在其他物体,也会投下阴影。所以我们最好是限制这个距离正好是光源射线L的长度。
剩下就是点到光源的计算,我们使用跟踪函数,也就是说我们要从点出发到光源,但是呢,这个公式基础是P+tD,这个t是有最小值,如果t为0,那么就说P+tD=P,这个点给自己投了阴影。所以我们要在点的射线方向稍稍加大一点,不再是起点位置。
比如0.00001,反正不是0就行了。
代码
基础理论就这么多,实际操作的条件也有拥有。
为了编码更加方便,我们将追踪函数拆分为
计算点是否最小最大值的判断的函数
cop closestIntersection(Vector3D origin, Vector3D dline, double min, double max) { double closet = double.PositiveInfinity; sharp claset_sharp = null; foreach (var item in sharplist) { var gp = IntersectRayShere(origin, dline, item); if (gp.X >= min && gp.X <= max && gp.X < closet) { closet = gp.X; claset_sharp = item; claset_sharp.CloseIntersection = closet; } if (gp.Y >= min && gp.Y <= max && gp.Y < closet) { closet = gp.Y; claset_sharp = item; claset_sharp.CloseIntersection = closet; } } if (claset_sharp != null) { return new cop(claset_sharp, closet); } return null; }
追踪函数
Color tracrray(Vector3D origin, Vector3D dline, double min, double max) { double closet = double.PositiveInfinity; var clp = closestIntersection(origin, dline, min, max); if (clp == null) { return Colors.Transparent; } closet = clp.Closet; var claset_sharp = clp.Claset_sharp; var p = origin + (closet * dline); var n = p - claset_sharp.center; n = n / n.Length; var cl = ComputeLighting(p, n, -dline, claset_sharp.specular); //保证颜色上下限正确 var M = Color.FromRgb((byte)(Math.Min(255, Math.Max(0, cl * claset_sharp.color.R))), (byte)(Math.Min(255, Math.Max(0, (cl * claset_sharp.color.G)))), (byte)(Math.Min(255, Math.Max(0, (cl * claset_sharp.color.B))))); return M; }
在计算反射光的位置添加上计算阴影的代码
毕竟阴影属于光计算的一部分
double ComputeLighting(Vector3D p, Vector3D n, Vector3D v, double s) { double i = 0; Vector3D l = new Vector3D(); double tmax = 1; foreach (var light in lightlist) { if (light.LightType == LightEnum.Ambient) { i += light.Intenesity; } else { if (light.LightType == LightEnum.Point) { l = light.Position - p; tmax =l.Length;//tmax=1; } else { l = light.Direction; tmax =(light.Position-p).Length;//tmax=intf } var sharpx = closestIntersection(p, l, 0.0000000001, tmax); //此时存在,则意味着光源和点之间存在物体 if (sharpx != null) { //不计算此刻光源 //此点一定会返回环境光 continue; } var dot = Vector3D.DotProduct(n, l); if (dot > 0) { i += light.Intenesity * dot / (n.Length * l.Length); } if (s != -1) { var r = 2 * Vector3D.DotProduct(n, l) * n - l; var dotv = Vector3D.DotProduct(r, v); if (dotv > 0) { i += light.Intenesity * Math.Pow(dotv / (r.Length * v.Length), s); } } } } //可以设置大1为1 //i>1?1:i return i; }
阴影 计算机图形学入门_3D渲染指南: 由C#作为编程语言,WPF作为输出。 计算机图形学入门3D渲染指南 - Gitee.com