添加镜面反射

在有了阴影之后,我们小球看起非常的不错了。

现在我们让球更加的真实吧。

我们的效果如下

 

 

镜面反射

实际上我们一直做的事情光线追踪,只不过是非常简单的。

我们定义的球体是一个可以拥有反射,闪亮程度的物体,所以做到这样子不成问题的。

那么如何去做呢。

实际上我们看图中的反射部分,相当于在球体的其他部分画出来他能看出的球体,反射和阴影而已。正如同我们在一些物体上可以到看到其他的物体,如果仔细看反射的物体又在反射其他,最后相互嵌套。

这个是叫做德罗斯特效应(Droste effect)效应

 

 

 

 

分析

实现这样的效果,实际上挺简单,就是对这个概念需要一定的理解,因为绘制这样效果的一切工具都已经满足了。

当我们的光线追踪函数发出一个射线并接触到一个点时,我们就会绘制出这个点的颜色,光的效果。

实际上这个我们这个点是有可能是反射之后存在的效果。

 

也即是说,我们看到的也许是这个反射后的效果,如果我们想要呈现真实的情况,我们应该继续追踪光线,并绘制出每次追踪点的颜色,光的效果。

 

 什么意思呢,就是此时我发出射线看到P,然后我们应该继续发射射线追踪光L,看看是否是其他点反射过来的。

也就是说我们计算完第一次画布视线的射线所接触到的点后, 应该从这个点发射出一个正好是画布射线镜像的射线,如果这个射线接触到点,那么就绘制这个点,然后嵌套继续运行...

很明显,这是一个递归过程。

为了保证不会卡死运行,我们设置一个递归层数。

 

反射

怎么求呢?实际上我们添加光的镜面反射时已经做好了,我们只需要把这个计算的内容提取出来单独做一个函数就行了啊。

我们第一次从画布的视线射线做镜像,值得注意的时我们对光的所有射线都是反着,这样是被方便计算,毕竟光实际上时存在,只是描述方向变化了,不影响实际效果.

那么我们就实际开始代码吧

 

代码:

 

镜像函数

      Vector3D reflectiveray(Vector3D R, Vector3D N)
        {
            return 2 * N * Vector3D.DotProduct(N, R) - R;
        }

 

改光线追踪的函数,可以运行递归,如果反射效果是0或者递归深度小于0就返回好了,对反射的颜色的,我们让他们除去原有反射之后,混合再一起就行了。

  Color tracrray(Vector3D origin, Vector3D dline, double min, double max, int deepth = 3)
        {
            double closet = double.PositiveInfinity;
            var clp = closestIntersection(origin, dline, min, max);

            if (clp == null)
            {
                return Colors.Black;
            }
            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)))));
            if (claset_sharp.reflective <= 0 || deepth <= 0)
            {
                return M;
            }
            var r = reflectiveray(-dline, n);
            var color = tracrray(p, r, 0.001, double.PositiveInfinity, deepth - 1);
            var k = 1 - claset_sharp.reflective;
            var rgb1 = Math.Min(255, Math.Max(0, M.R * k));
            var rgb2 = Math.Min(255, Math.Max(0, M.G * k));
            var rgb3 = Math.Min(255, Math.Max(0, M.B * k));
            var m2 = Color.Multiply(M, (float)k);
            var m3 = Color.Multiply(color, (float)claset_sharp.reflective);
            var m4 = Color.Add(m2, m3);
            return m4;
        }

 

启动时 默认递归10 其实3次以上就看挺好的了

void Start()
        {
            WriteableBitmap.Lock();
            for (double x = 0 - cw / 2; x < cw / 2; x++)
            {
                for (double y = 0 - ch / 2; y < ch / 2; y++)
                {
                    var D = canvastoviewport(new Point(x, y));
                    var color = tracrray(new Vector3D(), D, 1, double.MaxValue, 10);
                    var p = MidPoint(new Point(x, y));

                    byte[] colorData = { color.B, color.G, color.R, color.A };

                    int stride = (WriteableBitmap.PixelWidth * WriteableBitmap.Format.BitsPerPixel) / 8;
                    WriteableBitmap.WritePixels(new Int32Rect((int)p.X, (int)p.Y, 1, 1), colorData, stride, 0);
                }
            }
            WriteableBitmap.Unlock();

        }

 

最终代码 计算机图形学入门_3D渲染指南: 由C#作为编程语言,WPF作为输出。 计算机图形学入门3D渲染指南 - Gitee.com

 

posted @ 2022-06-28 17:49  ARM830  阅读(185)  评论(2编辑  收藏  举报