添加阴影

之前添加了漫反射和镜面反射。

球体看起来已经很好了,现在我们要更上一步。添加阴影。

 

阴影

你的影子,物体的影子都可以说时阴影。

在书上,可以说阴影就是在光源和点之间存在物体。这个物体影响了点的光的计算。

因为点自带环境光,所以影响的是点光和方向光。 

 

分析

既然光源和点之间存在物体就会存在阴影。

按照这个思路,我们计算的就是点到光源,如何计算点到光源之间存不存物体呢?

我们可以利用我们现有绘制函数,因为我们的绘制函数也是判断视口到球体之间是否有交点。

但是,有个问题,就是是否需要判断点和光源的距离? 在绘制阴影之前,我们判断的距离都是无限远。

判断阴影是否也可以使用无限远的设定呢?

首先我们是希望光源到点这个距离或者这个范围内,如果存在物体我们绘制阴影,如果无限远,可能存在其他物体,也会投下阴影。所以我们最好是限制这个距离正好是光源射线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

posted @ 2022-06-28 17:04  ARM830  阅读(91)  评论(0编辑  收藏  举报