天网

garfileo
上一篇:第一颗卫星

终于可以着手布置天网了。前几篇文章写得凌乱,而且也着实无趣。这是因为,它们充其量不过是我的一些学习笔记。

这篇文章依然无趣,因为有变量,有各种几何体,有宏,有数组,有循环,有 union,没有 difference,有动画……最终我画出来的东西,无非是一个模拟地球自转的奇怪的几何体,尽管我给它取了个宏大的名字叫世界。

从头开始

每一份 .pov 文件都应该以

#version 3.7; // 取决于所用的 POV Ray 版本
#include "colors.inc"
global_settings {
  assumed_gamma 1.0
}

作为开始,skynet.pov 也不例外。

为了得到的图形点线分明,最好是定义一组全局变量,

#declare PointSize = 0.03;
#declare LineThickness = 0.5 * PointSize;
#declare ArrowLength = 3 * PointSize;

#declare PointColor = texture { pigment { color Gray50 } }
#declare LineColor = texture { pigment { color Yellow } }
#declare X_Color = texture { pigment { color Red } }
#declare Y_Color = texture { pigment { color Green } }
#declare Z_Color = texture { pigment { color Blue } }

一是可以避免代码中出现过多的幻数,二是便于图形的修改。

坐标系

坐标系也叫标准正交基,但是对于天网而言,坐标系只是正交基,因为用于表示各维轴向的向量的长度可能不为 1,毕竟数学不等于现实。

MakeAxis 用于绘制各维轴向:

#macro MakeAxis(Begin, Direction, Texture)
  #local Norm = vnormalize(Direction);
  #local End = Begin + Direction;
  #local ArrowEnd = End + ArrowLength * Norm;
  object {
    union {
      cylinder {
        Begin, End
        LineThickness
        texture { Texture }
      }
      cone {
        End, 2 * LineThickness
        ArrowEnd, 0
        texture { Texture }
      }
    }
  }
#end

当然,MakeAxis 可以绘制给定了起点的任何一个方向,只是 MakeFrame 使用它绘制坐标系的各个轴向:

#macro MakeFrame(A, X_A, Y_A, Z_A)
  object {
    union {
      sphere {
        A, 2 * LineThickness
        texture { PointColor }
      }
      MakeAxis(A, X_A, X_Color)
      MakeAxis(A, Y_A, Y_Color)
      MakeAxis(A, Z_A, Z_Color)
    }
  }
#end

由于 MakeAxisMakeFrame 不仅仅在天网工程中有用,将来我可能会在其他工程中使用它们,因此把它们连同它们所用的全局变量从 skynet.pov 文件中抽取出来,存放到 frame.inc 文件中,然后只需在 skynet.pov 文件中使用 #include 指令便可将其载入:

#include "frame.inc"

只要 frame.inc 与 skynet.pov 位于同一目录,povray 就总能通过 #include 指令找到 frame.inc。

:事实上,像 frame.inc 这样的文件可以放在任意一个目录内,要让 povray 找到它,只需在执行 povray 的命令时,通过 +L 选项告诉它目录的位置。例如,假设 frame.inc 在 /foo/bar 目录,povray 命令可写成
$ povray +A +P +L/foo/bar skynet.pov 

卫星

天网是由卫星构成的。卫星,在本文中使用一些小坐标系代替。在 skynet.pov 中,宏 MakeSatellite 可基于天球上给定的经纬度计算并绘制小坐标系:

#macro UV_to_XYZ(U, V)
  vrotate(vrotate(-z, V * x), -U * y)
#end

#macro MakeSatellite(U, V, S)
  #local A = UV_to_XYZ(U, V);
  #local Z_A = vnormalize(<0, 0, 0> - A);
  #local X_A = vrotate(x, -U * y);
  #local Y_A = vrotate(vrotate(y, V * x), -U * y);
  #if (V = 0)
    union {
      MakeFrame(A, S * X_A, S * Y_A, S * Z_A)
      MakeAxis(A, -S * X_A, X_Color)
      MakeAxis(A, -S * Y_A, Y_Color)
    }
  #elseif (V < 0)
    MakeFrame(A, -S * X_A, -S * Y_A, S * Z_A)
  #else
    MakeFrame(A, S * X_A, S * Y_A, S * Z_A)
  #end
#end

UV_to_XYZ 可将天球上任意一点的经纬度转换为三维坐标。

天网

经过了以上的准备工作,下面可以着手布置天网。先对天网进行一番规划,用数组 UArray 存储一组经度,用数组 VArray 存储一组纬度。

#declare UArray = array[8] {0, 45, 90, 135, 180, -45, -90, -135}
#declare VArray = array[5] {-60, -30, 0, 30, 60}

这两个数组的组合,可构造 40 个天球上的点。这些点就是卫星的安装位置。

天网的用意是监视天球内的一切。但是现在要想看到天网,就必须把天网本身也作为世界的一部分。我用一个半透明的蓝色球体代表天网要监视的一切:

#declare World = sphere {
    <0, 0, 0>, 1
    texture { pigment { color rgbt <0, 0, 1, 0.5> } }
}

然后用 World 去兼并天网:

#declare World = object {
  union {
    object { World }
    #for (i, 0, 7, 1)
      #local U = UArray[i];
      #for (j, 0, 4, 1)
        #local V = VArray[j];
        MakeSatellite(U, V, 0.2)
      #end
    #end
  }
}

最后将整个世界呈现出来:

object {
  World
  rotate -360 * clock * y
  rotate -15 * z
}

这个世界是向右倾斜,并不停地旋转。

天外

要看到天网,光源需要设在天外,我也需要站在天外,向下俯瞰:

light_source {
  <5, 5, -5>
  color White
  shadowless // 无影灯
}

camera  {
  orthographic
  location <3, 3, -3>
  look_at <0, 0, 0>
}

结果可以看到:

动画

若想看到世界的旋转,不得不建立 skynet.ini 文件,其内容为:

Input_File_Name=skynet.pov
Initial_Frame=1
Final_Frame=60
Cyclic_Animation=On

然后使用命令

$ povray +A skynet.ini

产生一系列图片文件 skynet01.png,skynet02.png,……,skynet60.png。使用 ImageMagick 提供的 convert 工具将这个图片序列合成为本文开始的动图:

$ convert -delay 10 -loop 0 skynet*.png skynet.gif

附录

skynet.ini 文件:

Input_File_Name=skynet.pov
Initial_Frame=1
Final_Frame=60
Cyclic_Animation=On

skynet.pov 文件:

#version 3.7;
#include "colors.inc"
#include "frame.inc"
global_settings {
  assumed_gamma 1.0
}

#macro UV_to_XYZ(U, V)
  vrotate(vrotate(-z, V * x), -U * y)
#end

#macro MakeSatellite(U, V, S)
  #local A = UV_to_XYZ(U, V);
  #local Z_A = vnormalize(<0, 0, 0> - A);
  #local X_A = vrotate(x, -U * y);
  #local Y_A = vrotate(vrotate(y, V * x), -U * y);
  #if (V = 0)
    union {
      MakeFrame(A, S * X_A, S * Y_A, S * Z_A)
      MakeAxis(A, -S * X_A, X_Color)
      MakeAxis(A, -S * Y_A, Y_Color)
    }
  #elseif (V < 0)
    MakeFrame(A, -S * X_A, -S * Y_A, S * Z_A)
  #else
    MakeFrame(A, S * X_A, S * Y_A, S * Z_A)
  #end
#end

#declare UArray = array[8] {0, 45, 90, 135, 180, -45, -90, -135}
#declare VArray = array[5] {-60, -30, 0, 30, 60}

#declare World = sphere {
    <0, 0, 0>, 1
    texture { pigment { color rgbt <0, 0, 1, 0.5> } }
}

#declare World = object {
  union {
    object { World }
    #for (i, 0, 7, 1)
      #local U = UArray[i];
      #for (j, 0, 4, 1)
        #local V = VArray[j];
        MakeSatellite(U, V, 0.2)
      #end
    #end
  }
}

object {
  World
  rotate -360 * clock * y
  rotate -15 * z
}

light_source {
  <5, 5, -5>
  color White
  shadowless // 无影灯
}

camera  {
  orthographic
  location <3, 3, -3>
  look_at <0, 0, 0>
}

frame.inc 文件:

#declare PointSize = 0.03;
#declare LineThickness = 0.5 * PointSize;
#declare ArrowLength = 3 * PointSize;

#declare PointColor = texture { pigment { color Gray50 } }
#declare LineColor = texture { pigment { color Yellow } }
#declare X_Color = texture { pigment { color Red } }
#declare Y_Color = texture { pigment { color Green } }
#declare Z_Color = texture { pigment { color Blue } }

#macro MakeAxis(Begin, Direction, Texture)
  #local Norm = vnormalize(Direction);
  #local End = Begin + Direction;
  #local ArrowEnd = End + ArrowLength * Norm;
  object {
    union {
      cylinder {
        Begin, End
        LineThickness
        texture { Texture }
      }
      cone {
        End, 2 * LineThickness
        ArrowEnd, 0
        texture { Texture }
      }
    }
  }
#end

#macro MakeFrame(A, X_A, Y_A, Z_A)
  object {
    union {
      sphere {
        A, 2 * LineThickness
        texture { PointColor }
      }
      MakeAxis(A, X_A, X_Color)
      MakeAxis(A, Y_A, Y_Color)
      MakeAxis(A, Z_A, Z_Color)
    }
  }
#end

完。

阅读 665

5.7k 声望
1.8k 粉丝
0 条评论
你知道吗?

5.7k 声望
1.8k 粉丝
文章目录
宣传栏