上一篇:标准正交基
假设以 <0, 0, 0>
为中心,半径为 1 的球面是天球,构造天网的第一步是建立球面的经纬度模型,即对于球面上的任意一点 A,能够基于它的经度值 U
和纬度值 V
计算它在世界坐标系中的位置 <x, y, z>
。我打算先通过直观的图形弄清楚 U
, V
和 <x, y, z>
的关系。
首先,构造一个天球,
// 天球
sphere {
<0, 0, 0>, 1
texture { pigment { color rgbt <0, 0, 1, 0.75> } }
hollow // 令球体中空
}
为了更直观一些,使用之前定义的 MakeFrame
宏,把世界坐标系也画出来:
MakeFrame(<0, 0, 0>, x, y, z, 1.25, 0.05)
结果得到
现在考虑在球面上画几个大圆作为纬线和经线。POV Ray 没有线,只有体。大圆,最简单的办法是用呼啦圈或圆环来画。圆环的语法是
torus {
主半径, 从半径
texture { ... }
}
主半径是圆环中心线所构成的圆的半径,从半径是圆环截面圆的半径。例如,绘制赤道和北纬 34.61 对应的纬线:
#declare line_thickness = 0.015;
// 赤道
torus {
1, line_thickness
texture { pigment { color Orange } }
}
// 北纬 34.61
torus {
#local theta = (34.61 / 180) * pi;
cos(theta), line_thickness
texture { pigment { color Orange } }
translate sin(theta) * y
}
结果看上去还不错:
为什么北纬 34.61 要写成 (34.61 / 180) * pi
呢?因为 POV Ray 的三角函数接受的参数是弧度。pi
即圆周率,是 POV Ray 内部已经给出定义的常量。
接下来,绘制本初子午线和东经 118.41 度对应的经线:
#declare PrimeMeridian = torus {
1, line_thickness
rotate 90 * z
texture { pigment { color Orange } }
}
// 本初子午线
PrimeMeridian
// 东经 118.41
object {
PrimeMeridian
rotate -118.41 * y
}
结果就是……有些眼花缭乱:
在上述代码中,为什么东经 118.41 要写成负数呢?因为 POV Ray 用的坐标系是左手系。绕某个坐标轴旋转时,需要遵守左手螺旋定则——左手的拇指指向坐标轴的正向,其他 4 指弯曲的方向即为旋转方向。
然而,我画的经线是错误的。我要画的是本初子午线以及东经 118.41 对应的经线,它们应该是半圆,而非整圆。用 POV Ray 画一个半圆,不是那么容易,需要构造一个足够大的几何体,比如盒子,让它的尺寸刚好能够容纳半个圆——不想要的那半个圆:
#declare Monster = box { <-1.5, -1.5, 0>, <1.5, 1.5, 1.5> }
然后通过 difference
运算,用这个盒子从整圆上吃掉半个圆:
#declare PrimeMeridian = difference {
torus {
1, line_thickness
rotate 90 * z
texture { pigment { color Orange } }
}
Monster
}
然后绘制本初子午线和东经 118.41 度经线:
// 本初子午线
PrimeMeridian
// 东经 118.41
object {
PrimeMeridian
rotate -118.41 * y
}
结果就是不那么眼花缭乱:
difference
和之前用过的 union
,都是用于组合多个几何体。difference
的用法为
difference {
foo
bar
}
其作用是从 foo
上剔除 foo
与 bar
相交的区域。
对上述最终所得的图作一些标注:
回到本文开始时提出的问题,假设上图中红圈所标记的经线与纬线的交点为 A,那么 A 的经度为 +118.41,纬度为 +34.61,那么 A 在图中所示的直角坐标系里的各维坐标是多少?
我也不知道有多少种方法可以算出点 A 的坐标,但是此刻我知道一种最简单的方法,就是
// 点 A
#declare A = vrotate(vrotate(-z, 34.61 * x), -118.41 * y);
sphere {
A, PointSize
texture { PointColor }
}
注:POV Ray 提供的 vrotate
函数对给定的向量,遵循左手螺旋定则,绕一个单位向量以给定的角度旋转。
若不信,请看:
之所以成功,原因是:
若早生 2000 年,我就是有着经天纬地之才的人?
附录
完整的代码:
#version 3.7;
#include "colors.inc"
global_settings {
assumed_gamma 1.0
}
// 天球
sphere {
<0, 0, 0>, 1
texture { pigment { color rgbt <0, 0, 1, 0.75> } }
hollow // 令球体中空
}
// 世界坐标系
#macro MakeAxis(Origin, Direction, Length, Thickness, Color)
#local ArrowLength = 0.3 * Length;
#local Direction = vnormalize(Direction);
#local Begin = Origin;
#local End = Begin + (Length - ArrowLength) * Direction;
#local ArrowBegin = End;
#local ArrowEnd = ArrowBegin + ArrowLength * Direction;
object {
union {
cylinder {
Begin, End
Thickness
texture { pigment { color Color } }
}
cone {
ArrowBegin, 2 * Thickness
ArrowEnd, 0
texture { pigment { color Color } }
}
}
}
#end
#macro MakeFrame(A, X_A, Y_A, Z_A, L, Thickness)
object {
union {
sphere {
A, 2 * Thickness
texture { pigment { color Gray50 } }
}
MakeAxis(A, X_A, L, Thickness, Red)
MakeAxis(A, Y_A, L, Thickness, Green)
MakeAxis(A, Z_A, L, Thickness, Blue)
}
}
#end
MakeFrame(<0, 0, 0>, x, y, z, 2, 0.025)
#declare line_thickness = 0.015;
torus {
1, line_thickness
texture { pigment { color Orange } }
}
torus {
#local theta = (34.61 / 180) * pi;
cos(theta), line_thickness
texture { pigment { color Orange } }
translate sin(theta) * y
}
#declare Monster = box { <-1.5, -1.5, 0>, <1.5, 1.5, 1.5> }
#declare PrimeMeridian = difference {
torus {
1, line_thickness
rotate 90 * z
texture { pigment { color Orange } }
}
Monster
}
// 本初子午线
PrimeMeridian
// 东经 118.41
object {
PrimeMeridian
rotate -118.41 * y
}
// 点 A
#declare A = vrotate(vrotate(-z, 34.61 * x), -118.41 * y);
sphere {
A, 0.05
texture { pigment { color Gray50 } }
}
light_source {
<5, 5, -5>
color White
shadowless // 无影灯
}
camera {
orthographic
location <2.0, 2.0, -1.5>
look_at <0, 0, 0>
}
注:上述代码中,光源的设置里使用了shadowless
修饰,可以消除物体因光照而产生的影子。相机则使用了orthographic
修饰,设定为平行投影,从而取代了默认的透视投影设定。
下一篇:第一颗卫星
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。