上一篇:坐标变换
今天不再编蹩脚的故事,只是一本正经地画一个坐标系。
坐标系的原点用球体表示。画球的语法为
sphere {
球心, 半径
texture { ... }
}
例如:
sphere {
<0, 0, 0>, 1
texture { pigment { color Gray20 } }
}
坐标系的每个轴向由一个圆柱体和一个圆锥体组合而成。圆柱体的语法为
cylinder {
端面的中心, 另一端面的中心,
半径
texture { ... }
}
例如
cylinder {
<0, 0, 0>, x
0.05
texture { pigment { color Red } }
}
POV Ray 没有提供绘制直线段的功能,可以用圆柱模拟直线段。
圆锥体(确切地说,是圆台)的语法为
cone {
端面的中心, 半径
另一端面的中心, 半径
texture { ... }
}
例如
cone {
x, 0.1
x + 0.3 * x, 0
texture { pigment { color Red } }
}
下面是一个完整的例子,画出世界坐标系的 X 轴:
#version 3.7;
#include "colors.inc"
global_settings {
assumed_gamma 1.0
}
object {
union {
sphere {
<0, 0, 0>, 0.1
texture { pigment { color Gray20 } }
}
object {
union {
cylinder {
<0, 0, 0>, x
0.05
texture { pigment { color Red } }
}
cone {
x, 0.1
x + 0.3 * x, 0
texture { pigment { color Red } }
}
}
}
}
}
camera {
location <0, 0, -3>
look_at <0, 0, 0>
}
light_source {
<3, 3, -3>
color White
}
可得
依上例类推,可以相继画出世界坐标系的 Y 轴和 Z 轴:
object {
union {
// 原点
sphere {
<0, 0, 0>, 0.1
texture { pigment { color Gray20 } }
}
// X 轴
object {
union {
cylinder {
<0, 0, 0>, x
0.05
texture { pigment { color Red } }
}
cone {
x, 0.1
x + 0.3 * x, 0
texture { pigment { color Red } }
}
}
}
// Y 轴
object {
union {
cylinder {
<0, 0, 0>, y
0.05
texture { pigment { color Green } }
}
cone {
y, 0.1
y + 0.3 * y, 0
texture { pigment { color Green } }
}
}
}
// Z 轴
object {
union {
cylinder {
<0, 0, 0>, z
0.05
texture { pigment { color Blue } }
}
cone {
z, 0.1
z + 0.3 * z, 0
texture { pigment { color Blue } }
}
}
}
}
}
结果为
Z 轴是看不见的,因为它垂直指向屏幕的内部,被原点挡住了。如果修改一下相机的位置,
camera {
location <3, 3, -3>
look_at <0, 0, 0>
}
就可以看到 Z 轴。
这种坐标系叫左手系。伸出左手,拇指向右指为 X 轴正向,食指向上指为 Y 轴的正向,中指向前指为 Z 轴的正向(谁也没办法让它向后指)。中学几何课上所用的直角坐标系是右手系。用 POV Ray 画画,若把握不住左手系,往往会南辕北辙。
画一个坐标系是容易的,虽然有些繁琐。画三十三个坐标系,也是容易的,只是差不多三十三倍的繁琐。
作为一名真正勤劳的人,不是极富耐心,去迁就繁琐,而是极富耐心,从代码中寻找一致性,化繁为简。这就是形而上学的用处。凡是能够维持事物原有功能的化繁为简的形而上学,就是有益的。
显然,坐标系的各个轴,除了方位和颜色有区别,它们的形状是相同——圆柱和圆锥的组合。这意味着,起码在形状上,三个轴可以共用一部分代码。这样,就可以构造一个函数,它的变量是坐标轴的方位、长度、粗细以及颜色。基于 POV Ray 的宏 #macro ... #end
,可定义该函数:
#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
上述代码定义了 MakeAxis
宏,其中 local
语句用于宏内定义一个局部变量 r
,作为轴的粗度(圆柱体的半径)。vlength
则是 POV Ray 内部定义的函数,用于计算向量的长度。
有了 MakeAxis
宏,画世界坐标系的代码便可简化为
#declare Origin = <0, 0, 0>;
object {
union {
sphere {
Origin, 0.1
texture { pigment { color Gray20 } }
}
#local Length = 1;
#local Thickness = 0.05;
MakeAxis(Origin, x, Length, Thickness, Red)
MakeAxis(Origin, y, Length, Thickness, Green)
MakeAxis(Origin, z, Length, Thickness, Blue)
}
}
可以继续用宏,对代码进行简化。因为基于 MakeAxis
,可以定义一个绘制任意一个直角坐标系的宏:
#macro MakeFrame(A, X_A, Y_A, Z_A, Length, Thickness)
object {
union {
sphere {
A, 2 * Thickness
texture { pigment { color Gray50 } }
}
MakeAxis(A, X_A, Length, Thickness, Red)
MakeAxis(A, Y_A, Length, Thickness, Green)
MakeAxis(A, Z_A, Length, Thickness, Blue)
}
}
#end
现在,只用 MakeFrame
这个宏就可以绘制世界坐标系:
MakeFrame(<0, 0, 0>, x, y, z, 1.0, 0.05)
如果只是画一个坐标系,MakeFrame
着实鸡肋,但是要画三十三个坐标系,它的威力就能充分得以体现。下面的代码可以绘制三十三个坐标系:
#for(i, -5, 5, 1)
object {
MakeFrame(<0, 0, 0>, x, y, z, 1.0, 0.05)
rotate 90 * i * x
translate 4 * i * x
}
object {
MakeFrame(<0, 0, 0>, x, y, z, 1.0, 0.05)
rotate 90 * i * y
translate 4 * (i * x + z)
}
object {
MakeFrame(<0, 0, 0>, x, y, z, 1.0, 0.05)
rotate 90 * i * z
translate 4 * (i * x - z)
}
#end
上述的 #for ... #end
代码段,用 C 语言来表述,意思是
for (int i = -5; i <= 5; i++) {
... ... ...
}
结果为:
附录
完整的代码如下:
#version 3.7;
#include "colors.inc"
global_settings {
assumed_gamma 1.0
}
#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
#for(i, -5, 5, 1)
object {
MakeFrame(<0, 0, 0>, x, y, z, 1.0, 0.05)
rotate 90 * i * x
translate 4 * i * x
}
object {
MakeFrame(<0, 0, 0>, x, y, z, 1.0, 0.05)
rotate 90 * i * y
translate 4 * (i * x + z)
}
object {
MakeFrame(<0, 0, 0>, x, y, z, 1.0, 0.05)
rotate 90 * i * z
translate 4 * (i * x - z)
}
#end
#declare forenoon = 15 * <1, 1, -1>;
light_source {
forenoon
color White
}
camera {
location forenoon * <2, 0.3, 0>
look_at <0, 0, 0>
}
下一篇:标准正交基
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。