上一篇:经纬
为了便于实现天球上任意一点的经纬度坐标向三维坐标的转换,我定义了一个宏:
#macro UV_to_XYZ(U, V)
vrotate(vrotate(-z, V * x), -U * y)
#end
有了这个宏,天球上任意一点的三维坐标唾手可得。例如
#declare A = UV_to_XYZ(118.41, 34.61);
现在,我想以 A
为原点,构造一个直角坐标系 (X_A, Y_A, Z_A)
。X_A
轴与 Y_A
轴分别与过 A
点的纬线和经线相切,Z_A
轴指向 <0, 0, 0>
。
Z_A
轴很容易构造出来:
#declare Z_A = vnormalize(<0, 0, 0> - A);
至于 X_A
和 Y_A
,其实也很容易构造出来:
#declare X_A = vrotate(x, -118.41 * y);
#declare Y_A = vrotate(vrotate(y, 34.61 * x), -118.41 * y);
究其原理,如计算 A
的坐标的思路如出一辙,并不需要使用微分几何方面的知识。
可以使用之前定义的 MakeAxis
宏,定性地检验一下所得坐标系是否正确:
MakeFrame(A, X_A, Y_A, Z_A, 0.25, 0.01)
结果为
我打算在 A 点安装一颗卫星,用它监视天球之内的世界。这是我在这个世界安装的第一颗卫星,要给它取个有纪念意义的名字……就叫有纪念意义的名字吧。
实际上,有纪念意义的名字,不过是一个相机:
camera {
location A
right X_A
up Y_A
direction Z_A
}
如果用这个相机来取代之前用来拍摄上述图像的相机,结果可得
镜头恰好被相交的经线和纬线挡住了,如果沿着 -Z_A
的方向移动一下相机,
camera {
location A - 3.5 * Z_A
right X_A
up Y_A
direction Z_A
}
或者
camera {
location A
right X_A
up Y_A
direction Z_A
translate -3.5 * Z_A
}
就可以看到整个天球了,
但是,看上去怪怪的,似乎整个世界被压扁了一些。先不管这个问题,更应该关心的是,相机的设定不同以往。right
,up
,direction
这三个向量的值恰好分别就是我苦心孤诣构造的 X_A
,Y_A
,Z_A
,它们的含义是什么呢?
right
的含义是相机的右侧方向。up
的含义是相机的顶部方向。direction
的含义是相机镜头的方向。这三个方向再加上 location
,便完全确定了相机的方位。至于这个相机拍摄到的图像为何是扁的,是因为相机认为要拍摄的画面是个正方形,即画面的宽度和高度相同,但是我给它的底片却是长方形的,所以相机不得不把它拍摄到的正方形画面压扁,印到底片上。
画面的宽度和高度信息,需要以比例值通过 right
向量传递给相机。POV Ray 有两个内部变量,image_width
和 image_height
,它们的值分别是底片的宽度值和高度值。通过 right
向量传递给相机的方法是
camera {
location A
right (image_width/image_height) * X_A
up Y_A
direction Z_A
translate -3.5 * Z_A
}
用这个新的相机,就可以拍摄到正确的结果:
从此,名字为有纪念意义的名字的卫星就可以工作了。不过,它的设定其实还可以更简单一些:
camera {
location A - 3.5 Z_A
sky Y_A
look_at <0, 0, 0>
}
POV Ray 能够根据 sky
和 look_at
的值自动为我计算出上面的相机信息。
附录
完整的代码:
#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
}
#macro UV_to_XYZ(U, V)
vrotate(vrotate(-z, V * x), -U * y)
#end
#declare A = UV_to_XYZ(118.41, 34.61);
#declare X_A = vrotate(x, -118.41 * y);
#declare Y_A = vrotate(vrotate(y, 34.61 * x), -118.41 * y);
#declare Z_A = vnormalize(<0, 0, 0> - A);
MakeFrame(A, X_A, Y_A, Z_A, 0.5, 0.025)
light_source {
<5, 5, -5>
color White
shadowless // 无影灯
}
camera {
location A
right (image_width / image_height) * X_A
up Y_A
direction Z_A
translate -3.5 * Z_A
}
下一篇:天网
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。