跳转到内容

Julia 和 Mandelbrot 集合/景观图片

来自维基教科书,开放书籍,为开放世界

如果在三维坐标系中,我们将带有 Mandelbrot 或 Julia 集合的平面想象成 *x,y*-平面,并在 *z*-方向上绘制距离函数,我们将得到一个看起来像景观的曲面,我们可以从侧面观察它。这样的图片必须从上往下(在屏幕上)绘制,并且从远处到近处(在景观中)绘制,这样某些东西就会生长并隐藏之前绘制的内容。

距离函数可能存在奇点,这意味着曲面在某些点处会无限增长。由于这些强劲的上升如果数量众多且占主导地位则会不美观,因此我们必须修改距离函数(第三幅图)。我们也可以修改它来创造一个更有趣的景观(第二幅图)。

我们可以让光线是“自然的”,就像来自太阳的光线。然后我们想象光线是平行的(并且由两个角度给出),并且我们让曲面上一个点的颜色由这个方向与该点曲面斜率之间的角度决定。强度(在地球上)与距离无关,但由于大气,光线会变得更白,有时地面看起来像是被一层薄雾笼罩(第二幅图)。我们也可以让光线是“人造的”,就像来自观察者手中灯笼发出的光线一样。在这种情况下,颜色必须随着距离而变暗(第三幅图)。

我们从一个向下倾斜的角度观察景观,绘图窗口以这条线为中心的法线。绘图窗口中的垂直线(像素)对应于具有分形的基平面中的线。对于这些线中的每一条,我们从最远点开始,向观察者前进,在窗口的相应垂直线中以以下方式给像素着色:如果 p 是基平面中线上的一个点,我们计算 p 处距离函数的值。如果从曲面上的这个点到眼睛的线与窗口相交,我们将给这个像素着色。我们通过计算距离函数在 p 附近两个非常接近的点(x 方向和 y 方向)的值,来计算该点处曲面的斜率。由三个值跨越的小三角形位于曲面上。我们取三角形法向量(单位向量)与光线方向(单位向量)的标量积 - 在人造光的情况下,这是从眼睛到点的方向向量。

这个数字(乘以密度)可以与其他决定颜色的因素相结合:例如,地面上方的高度或到观察者的距离。

从曲面法向量(单位向量)与观察者方向向量的标量积,我们可以近似计算出为了给相邻像素(分别对应于景观在观察者方向上的上升或下降)着色,我们必须朝观察者前进的距离。由于这种计算不可能精确,因此可能必须将步骤减小一个因子。当我们离观察者足够近时,窗口的这条垂直线的底部像素被着色,我们将停止绘制这条线 - 如果有东西在观察者附近长出来,我们可能还会继续绘制,因为我们希望看到这一点。我们可以在转到下一条垂直线之前完成一条垂直线的绘制,而是通过在朝观察者前进的每一步中转到下一条垂直线,使绘制看起来更迷人,这样绘制看起来就像从上往下波动一样。

曲面主要由与距离函数成正比的函数构成,但程序应该被设计成可以处理这个函数。在第一步中,奇点应该通过选择一个相对最大高度 *hm0* 来变为有限。这个数字乘以截面的宽度是景观中的最大高度 *hm*,我们可以用 *hm * arctan(h/hm)* 来代替高度 *h*,例如。但是,数字 *hm*(常数)应该通过将其除以与实迭代次数的指数函数成正比的数字来改变,以便在边界附近降低最大高度。我们也可以简单地将奇点截断,而不是将它们变为有限,方法是将 *h* 替换为 *hm*,当 *h* > *hm* 时。我们也可以通过将 *h* 除以 来调节 *h*,例如,或者我们可以通过将 *h* 替换为 来将奇点转换为陨石坑,当 *h* > *hm* 时。

此外,可以通过将 *h* 乘以(例如)数字 来使曲面变得平滑,其中 *s* 是曲面的斜率,*a* 和 *b* 是决定效果的参数。

对于自然光,我们可以通过根据这个数字(在 [0, 1] 区间内)将颜色与灰色或黑色混合,来使颜色随着距离和高度变得更白(雾)或更暗:,其中 *a, b, c* 和 *d* 是决定效果的参数。

对于人造光,我们可以通过根据数字 与黑色混合来使颜色随着距离变暗,其中 *s* 是从眼睛到斜率(单位向量)的方向向量的标量积,*l* 和 *e* 是决定效果的参数。

更详细的绘制

[edit | edit source]

我们必须首先选择一个比例,我们将它应用于窗口和景观,即通过将距离 gg 分配给窗口(或图片)的宽度 ww(以像素为单位)。然后,一个像素的宽度是 hh = gg/ww,在以中心为原点的窗口坐标系中,距离左边界 i 个像素的直线具有横坐标 xs = -gg/2 + i×hh

z 轴上具有 z 坐标 z0 的点观察景观,沿着方向为(正)y 轴的直线 lc,该直线与 y 轴交于一个纵坐标为 yc 的点。将分形图案平移,使得基平面中的该点 (0, yc) 成为分形图案的中心:如果分形图案的实际中心是 (x1, y1),则在计算高度和斜率时,我们将 (x1, y1) 加到 x-y 系中的坐标集。

z0lc 的正交线(在 y-z 平面上)与 y 轴相交于一个纵坐标为 -yvyv 为正)的点,这个点是消失点(对观察者来说似乎无限远)。我们令 d (= yc+yv) 为消失点到基点 yc 的距离。如果 mlc(从观察者到基点的距离)除以从观察者到窗口的距离,则窗口中具有横坐标 xs 的垂直线对应于基平面中经过消失点 (0, -yv) 和点 (m×xs, yc) 的线。并且这条线上具有纵坐标 y 的点,其横坐标为 m×xs×(d + y - yc)/d

现在我们选择窗口中的一条垂直线(距离左侧 i 个像素,因此具有横坐标 xs = -gg/2 + i×hh),我们将在基平面上沿着对应的线从远到近对这条线进行着色。假设我们在这条线上到达了具有纵坐标 y 的点。这个点的横坐标是 m×xs×(d + y - yc)/d,我们将计算具有横坐标 x1 + m×xs×(d + y - yc)/d 和纵坐标 y1 + y - yc 的点的表面高度 z 和斜率 s。在 y-z 平面上,我们有从观察者 (0, z0) 到 (y, z) 的线段 lz。要着色的窗口中的像素(距离左侧 i 个像素)距离顶部 j 个像素,其中 jwh/2 - wj×tan(acz) 的整数部分,这里 wh 是窗口的高度(以像素为单位),wj 是从观察者到窗口的距离,也以像素为单位,aczlclz 的角度。

在对这个像素进行着色后,我们必须估计我们要向前移动(沿着负 y 轴方向)以对下一个(相邻)像素(上方或下方)进行着色的步长 dy 的纵坐标。在 y-z 平面上,我们有从观察者 (0, z0) 到 (y, 0) 的线段 ly。我们令 ay 为从(负)z 轴到 ly 的角度,令 ayz 为从 lylz 的角度。如果我们设置

,

dy 由以下公式给出

,

也就是说,新的 yy - dy

由于这只是一个估计值(通过微积分得到的),我们必须安排程序,以便我们可以将步长 dy 乘以一个因子 r < 1。

当最后一个像素被绘制(距离顶部 wh 个像素)并且当新的 y 小于某个 y 值时,窗口中垂直线的绘制就完成了,该 y 值取决于窗口下边界的状况、视角以及景观是向上还是向下(当表面位于基平面下方且视角较小时,这个 y 值可能位于窗口下边界相当远的地方)。

迭代带之间的间隙

[edit | edit source]

有一种情况可能会使我们需要以更小的步长前进,那就是迭代带之间出现间隙的趋势,尤其是在公式中出现了较大的幂次。在接近边界时,距离函数的值会越来越小,但在迭代次数增加 1 的曲线处,计算得到的距离往往比实际值略小。原因是,用于计算距离估计的迭代次数必须非常大,这预设了逃逸半径非常小(或对迭代到无穷大来说非常大),但是如果逃逸半径像它应该的那样小,那么迭代带之间的着色就会变得不干净,因为斜率的计算是不确定的——就像当我们放大很多倍时,分形图片总是变得不干净一样:计算机的精度不再足够了。

插值

[edit | edit source]

我们可以通过应用插值来解决迭代带之间的间隙问题:如果(在向下绘制中,即绘制表面的视觉侧)下一个绘制的像素不是下面紧邻的像素,我们可以通过对颜色值进行插值来填补间隙。使用这种技术,我们总是可以生成没有明显缺陷的图片。此外,这种技术意味着,如果我们令缩小因子 r 大于 1,我们就可以快速绘制出足够好的图片,以便选择图案和颜色(比通常的分形图片更快,因为精细的分形图案不太容易辨认)。

逐像素绘制

[edit | edit source]

上面的绘制方法相当有效,因为我们事先估计了下一个要绘制像素的情况。但是,每次有东西生长起来并隐藏之前绘制的像素时,就会再次绘制像素。此外,不规则的绘制方式意味着一个运作良好的程序会相当复杂(插值和计算景观中从顶部开始绘制到底部结束的位置)。可以创建一个更简单的程序,其中图像以像素到像素的方式规律绘制,这意味着每个像素只被着色一次,但之后每个像素都需要进行多次计算。对于屏幕上的一个给定像素,我们沿着从观察者穿过该像素的直线逐步前进,当计算表明我们位于地表下方时,我们就停止。原则上,我们可以让步长的大小相同,但这将要求步长非常小,计算次数将随之变得非常大。幸运的是,我们有一个工具可以调整步长,使它们一开始比较大,然后以合适的速率变小,即用于构建景观的距离估计。但这种方法需要(或者说效果最好)当景观向下倾斜时,即在地平面以下而不是以上形成,因为我们可以从观察者直线与地平面相交的点开始逐步逼近,并且可以使所有步长(的水平范围)都由距离估计决定。

从观察者直线与地平面相交的点开始,我们依次向前迈出步长,其在地平面上的投影为估计距离的一半,例如。在每一步中,我们计算步长点(在线上)的高度和地表(在这个点下方或上方)的高度,并继续前进,只要第一个高度小于第二个高度。当不再是这种情况时,我们就位于地表下方,然后必须向后退并可能再次向前。这些步长也必须由距离估计决定,因为它们必须在边界附近更小。我们可以使步长为 *e×d×dist*,其中 *dist* 是距离估计,*e* = ±1 表示步长点位于地表上方/下方,*d* 是一个因子,最初固定为给定的一个小数(例如 0.5),并且每次 *e* 的符号改变时除以 2。我们执行逐步逼近,直到两个高度之间的差值小于给定的一个小数,*或* 直到达到最大迭代次数或边界,或者执行的操作次数大于给定的一个大数,以确保过程停止。如果在表面上找到的点对应于复数 *z*(在地平面内),我们计算表面在 *z* 附近两个点(在 *x* 和 *y* 方向上)的高度,以找到表面的斜率。

阴影效果

[edit | edit source]

我们可以(如下面的图片所示)通过绘制阴影(在自然光或来自观察者的光的情况下)使景观看起来更逼真:如果从表面上的一个点指向光源方向的直线在地表下方有一个点,那么该点就在阴影中,因此会变暗。这种方法在向下倾斜的景观情况下最有效,但即使在这种情况下,也可能需要对景观可见部分之外的点执行计算(除非光源位于景观可见部分之上)。

华夏公益教科书