上一篇:第一颗卫星
终于可以着手布置天网了。前几篇文章写得凌乱,而且也着实无趣。这是因为,它们充其量不过是我的一些学习笔记。
这篇文章依然无趣,因为有变量,有各种几何体,有宏,有数组,有循环,有 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
由于 MakeAxis
和 MakeFrame
不仅仅在天网工程中有用,将来我可能会在其他工程中使用它们,因此把它们连同它们所用的全局变量从 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
完。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。