游戏开发论坛

 找回密码
 立即注册
搜索
查看: 30969|回复: 70

欲做4d引擎。

[复制链接]

9

主题

102

帖子

343

积分

中级会员

Rank: 3Rank: 3

积分
343
发表于 2007-3-1 18:30:00 | 显示全部楼层 |阅读模式
就是把4d空间的图形投影到3d中观察。

完整源代码 http://upload.gameres.com/20107/sf_1603621_1843.zip

首先我们要建立一个4D的“超立方体”。
我们先看一下3D立方体的顶点定义。

先看我们熟悉的d3d左手坐标系,
沿着Z轴正方向是前方。
3D立方体中,(设棱长==2a)
各个(共8个)顶点坐标为:
1 {-a,  a,   a }
2 { a,  a,   a }
3 { a, -a,   a }
4 {-a, -a,   a }

5 {-a,  a,  -a }
6 { a,  a,  -a }
7 { a, -a,  -a }
8 {-a, -a,  -a }

有什么规律呢?。。。。。。

==============================================================================

先打断一下,我有必要说明一下3维坐标,4维坐标的问题。非澄清不可!

在3维空间中,普通的坐标是我们熟知的3维坐标,比如,点A(x1,y1,z1).点B(x2,y2,z2)

d3d中为了更好的变换,引进了4维齐次坐标的。(具体原因在此就不说了)
但是,到底什么是4维齐次坐标?

一个4维齐次坐标,由4个数字组成,比如 点P(x,y,z, w )。
虽然有“4维”,但是,它所表示的,却只不过是3维空间中的一个点。
反过来,一个3维空间的点,用齐次坐标表示,需要4个数。

d3d在绘制的时候,最终,要把4维齐次坐标转换成3维齐次坐标。
具体的转换方式很简单,如果点P的4维齐次坐标是(x,y,z,w),
那么,点P的3维普通坐标是(x/w, y/w, z/w).

反过来,把一个点的3维普通坐标转换成4维齐次坐标,由无数种结果,
不过一般,我们把w设为1。比如点Q(x0,y0,z0),则4维齐次坐标为(x0,y0,z0,1)
当然,比如说设为(2x0,2y0,2z0,2)等等也可以。
------------------------------------------------------------------------------
在4维空间中,有4个方向,一个点的普通坐标,需要用4个数来表示,
比如,点M(x,y,z,u).这叫做4维普通坐标,表示4维空间的点。
习惯上用u来表示第4维。(u应该是ultra“超,超级”的首字母)

4维的点,类似地,也有齐次坐标。4维点的齐次坐标,显然,需要5个数字。
比如,点N(x,y,z,u, w ).那么,点N的普通坐标就是(x/w, y/w, z/w, u/w).

注意,很多朋友包括在回帖的时候,混淆4维齐次坐标和4维普通坐标!
4维齐次坐标表示3维空间的点。
4维普通坐标表示4维空间的点。

***这样说会比较好理解一点***
一个平面上的点,用普通坐标表示,需要2个数字;用齐次坐标表示,需要3个数字;
一个3D空间的点,用普通坐标表示,需要3个数字;用齐次坐标表示,需要4个数字;
一个4D空间的点,用普通坐标表示,需要4个数字;用齐次坐标表示,需要5个数字.

===============================================================================

另外说一下数据类型和变量名的问题,在我的4D引擎中,有4中向量(点,坐标)类型:
这些typedef都是大写,但为了大家能看得清楚一些,我用大小写交替!
i3DxVec3---3维普通坐标。i3DxVec4---4维齐次坐标。
它们都表示3维空间中的点或向量。

typedef union
{
        struct{
                float x,y,z;
        };
        struct{
                float x1,x2,x3;
        };
}I3DXVEC3;
i3DxVec3的成员,可以这样用:x,y,z; 也可以这样用x1,x2,x3.
i3DxVec4的成员,可以这样用:x,y,z,w; 也可以是 x1,x2,x3,x4.
................................................................................
i4DxVec4---4维普通坐标。i4DxVec5---5维齐次坐标。
它们都表示4维空间中的点或向量。
i4DxVec4可以这样用:x,y,z,u; 也可以:x1,x2,x3,x4.
i4DxVec5可以这样用:x,y,z,u,w; 也可以:x1,x2,x3,x4,x5.看你喜好了~
================================================================================

********************************************************************************
***@齐次坐标一般在引擎内部使用,我们研究几何,或定义顶点数据时,都用普通坐标@***
********************************************************************************

================================================================================

继续上面的立方体的话题。

想象有一个4维“超立方体”,棱长==2a, 你怎么写出它的顶点?有几个顶点?
我们很容易发现,3维立方体的各顶点坐标,实际上就是a和-a的排列组合!

类似,在4维空间中,运用排列组合,可以写出各个顶点坐标,排列数就是顶点数目:

列举这些顶点,有许多方式,我们并不是在学习排列组合课程,就不多说了。

方便起见,我这样排列,用Ctrl+C/Ctrl+V把刚才的3维立方体坐标粘贴过来:

1{-a,  a,   a }
2{ a,  a,   a }
3{ a, -a,   a }
4{-a, -a,   a }

5{-a,  a,  -a }
6{ a,  a,  -a }
7{ a, -a,  -a }
8{-a, -a,  -a }

然后,在x,y,z坐标的基础上,加上一维(u坐标):

1{-a,  a,  a,   a}
2{ a,  a,  a,   a}
3{ a, -a,  a,   a}
4{-a, -a,  a,   a}

5{-a,  a, -a,   a}
6{ a,  a, -a,   a}
7{ a, -a, -a,   a}
8{-a, -a, -a,   a}

但是,这当然还没有列举完所有顶点。
Ctrl+C/Ctrl+V,再把最后一维坐标改一下,

1{-a,  a,  a,  -a}
2{ a,  a,  a,  -a}
3{ a, -a,  a,  -a}
4{-a, -a,  a,  -a}

5{-a,  a, -a,  -a}
6{ a,  a, -a,  -a}
7{ a, -a, -a,  -a}
8{-a, -a, -a,  -a}

这样得到了2*8==16个顶点的坐标,这就是4维超立方体的顶点坐标。

然后,为了追求完美,还要做一个小的修饰,把顶点编号改一下:

1_1 {-a,  a,  a,   a}
1_2 { a,  a,  a,   a}
1_3 { a, -a,  a,   a}
1_4 {-a, -a,  a,   a}
1_5 {-a,  a, -a,   a}
1_6 { a,  a, -a,   a}
1_7 { a, -a, -a,   a}
1_8 {-a, -a, -a,   a}

2_1 {-a,  a,  a,  -a}
2_2 { a,  a,  a,  -a}
2_3 { a, -a,  a,  -a}
2_4 {-a, -a,  a,  -a}
2_5 {-a,  a, -a,  -a}
2_6 { a,  a, -a,  -a}
2_7 { a, -a, -a,  -a}
2_8 {-a, -a, -a,  -a}

================================================================================

现在,顶点有了,很简单,但关键是,一个4维超立方体,它的各个顶点之间是怎么连接的?

***我将从两个角度来帮助大家理解,首先是第一个角度***

我们不妨先看看3维立方体中,顶点的连接情况,我把3d立方体的顶点数据再拷贝一份:
1{-a,  a,   a }
2{ a,  a,   a }
3{ a, -a,   a }
4{-a, -a,   a }

5{-a,  a,  -a }
6{ a,  a,  -a }
7{ a, -a,  -a }
8{-a, -a,  -a }

我重新画了图,对于立方体的一些棱,使用了不同的颜色。
顶点之间的连接是:

1~2 , 2~3 , 3~4 , 4~1
实际上,以上的1-4顶点,组成了立方体的"前面"(它们的最后一维z坐标== +a)

5~6 , 6~7 , 7~8 , 8~1
实际上,以上的5-8顶点,组成了立方体的"后面"(它们的最后一维z坐标== -a)

然后,前面和后面,对应顶点,相连在一起:
1~5
2~6
3~7
4~8

这样,就构成了立方体。

上面写的太散了,我们整理整理:
1~2 , 2~3 , 3~4 , 4~1
5~6 , 6~7 , 7~8 , 8~1
1~5 , 2~6 , 3~7 , 4~8
................................................................................

类似地,推广到4维超立方体中,

顶点1_1 到 1_8 的最后一维u坐标== +a.我又拷贝了一份:
1_1 {-a,  a,  a,   a}
1_2 { a,  a,  a,   a}
1_3 { a, -a,  a,   a}
1_4 {-a, -a,  a,   a}
1_5 {-a,  a, -a,   a}
1_6 { a,  a, -a,   a}
1_7 { a, -a, -a,   a}
1_8 {-a, -a, -a,   a}

它们的最后一维u坐标相同,(在同一个3维超平面中),组成了一个3维立方体,
顶点的连接情况自然就是:

1_1 ~ 1_2 , 1_2 ~ 1_3 , 1_3 ~ 1_4 , 1_4 ~ 1_1
1_5 ~ 1_6 , 1_6 ~ 1_7 , 1_7 ~ 1_8 , 1_8 ~ 1_1
1_1 ~ 1_5 , 1_2 ~ 1_6 , 1_3 ~ 1_7 , 1_4 ~ 1_8

顶点2_1 到 2_8 的最后一维u坐标== -a。
它们也组成了3维立方体,顶点连接情况显然是:

2_1 ~ 2_2 , 2_2 ~ 2_3 , 2_3 ~ 2_4 , 2_4 ~ 2_1
2_5 ~ 2_6 , 2_6 ~ 2_7 , 2_7 ~ 2_8 , 2_8 ~ 2_1
2_1 ~ 2_5 , 2_2 ~ 2_6 , 2_3 ~ 2_7 , 2_4 ~ 2_8

(本文中复制粘贴比较多^ ^ 希望大家找到规律)

然后,在这个4维超立方体的顶点中,
u==+a 和 u==-a 的两个3维立方体之间的对应顶点,相互连接即可。也就是:
(这是不是类似于3维立方体前后两个面的对应顶点连接呢?)

1_1 ~ 2_1
1_2 ~ 2_2
1_3 ~ 2_3
1_4 ~ 2_3
1_5 ~ 2_8
1_6 ~ 2_8
1_7 ~ 2_8
1_8 ~ 2_8

------------------------------------------------------------------------------
如果您觉得以上内容还是有些抽象,那么,----

***我们再从另一个角度理解各个顶点之间的连接***

注意:各个顶点的坐标我们早就求出来了,我们现在只需要关心顶点之间的连接!

我们想象用橡皮筋制作一个3维立方体,并为各个顶点标号,观察各个顶点连接情况,
然后把立方体压扁,平放到一张纸上,成为一个2维图形,再来观察各个顶点连接情况,

傻瓜也知道,各个顶点的连接情况,没有发生任何改变。

(说的故弄玄虚一点,这是图形的拓扑不变性,变化前后的两个图形是“同胚”的)。

把一个橡皮筋做的4维立方体压扁到一张白纸上,那么,
各个顶点之间的连接情况也不会改变!
换言之,我们可以在一张大白纸上(2D平面),研究4维立方体的顶点连接情况。
..............................................................................
我们看看2维正方形的顶点连接情况。

可以归纳为:用橡皮筋先构建两条边,平铺在纸上,再把两条边的对应顶点连接。

然后看看3维立方体的顶点连接情况。

可以归纳为:用橡皮筋先构建两个正方形,平铺在纸上,再把它们对应顶点连接。

我们可以类推出,4维超立方体的顶点连接。

可以归纳为:用橡皮筋先构建两个立方体,平铺在纸上,再把它们对应顶点连接。
..............................................................................
我们甚至可以推导出5维超立方体的顶点连接。
就是用橡皮筋先构建两个4维超立方体,平铺在纸上,再把它们的对应顶点连接。


6维超立方体就免了^ ^
------------------------------------------------------------------------------
我们现在直观地理解了高维超立方体,那么,实际上,我们还可以为5维超立方体定义顶点,
大家可以先自己做一下,以加深对高维超立方体的理解.

1_0 {-a,  a,  a,  a,   a}
1_1 { a,  a,  a,  a,   a}
1_2 { a, -a,  a,  a,   a}
1_3 {-a, -a,  a,  a,   a}
1_4 {-a,  a, -a,  a,   a}
1_5 { a,  a, -a,  a,   a}
1_6 { a, -a, -a,  a,   a}
1_7 {-a, -a, -a,  a,   a}
1_8 {-a,  a,  a, -a,   a}
1_9 { a,  a,  a, -a,   a}
1_A { a, -a,  a, -a,   a}
1_B {-a, -a,  a, -a,   a}
1_C {-a,  a, -a, -a,   a}
1_D { a,  a, -a, -a,   a}
1_E { a, -a, -a, -a,   a}
1_F {-a, -a, -a, -a,   a}

2_0 {-a,  a,  a,  a,  -a}
2_1 { a,  a,  a,  a,  -a}
2_2 { a, -a,  a,  a,  -a}
2_3 {-a, -a,  a,  a,  -a}
2_4 {-a,  a, -a,  a,  -a}
2_5 { a,  a, -a,  a,  -a}
2_6 { a, -a, -a,  a,  -a}
2_7 {-a, -a, -a,  a,  -a}
2_8 {-a,  a,  a, -a,  -a}
2_9 { a,  a,  a, -a,  -a}
2_A { a, -a,  a, -a,  -a}
2_B {-a, -a,  a, -a,  -a}
2_C {-a,  a, -a, -a,  -a}
2_D { a,  a, -a, -a,  -a}
2_E { a, -a, -a, -a,  -a}
2_F {-a, -a, -a, -a,  -a}

至于这32个定点之间的连接,就免了,写在这里太浪费harddisk.也可以编程完成.

==============================================================================

超立方体建立完毕后,我们就需要在4D空间中观察它。

4维世界顶点变换处理的流程是:(使用CI4D::SetTransform)

4维世界变换 --> 4维观察变换 --> 4维投影变换 >>> 3维空间(3D输出)

然后,在3维投影空间中,进行3D输出变换:(使用CI4D::Set3DOutputTransform)

3维投影空间 --> 3维观察变换 --> 3维投影变换 >>> 视口缩放 >>> 计算机屏幕

先对3维变换做个说明:


我使用Direct3D做3D输出。

3维观察变换(摄影机变换)之后,顶点到了摄影机空间中,然后再做投影变换:
从摄影机空间中,朝着前方(Z 轴正方向)取一个“观察平截头体”,
然后,把观察平截头体缩放到投影空间中的一个半立方体中(图7右边),
这个半立方体的的范围是,x,y坐标范围从-1 ~ +1;z坐标范围从0 ~ +1。

不过摄影机空间中观察平截头体之外的顶点,也跟着一起变换到了投影空间里,
这些额外的顶点,在那个半立方体之外,所以需要“剪裁”掉。这个工作由3D引擎完成。

然后,3维工作到此结束,接下来,变换最终的z坐标值和(x,y)坐标值,被分开处理:
z坐标,被用于排序;
另一方面,(x,y)坐标(范围在-1 ~ +1)经过视口缩放(乘以窗口的长宽),
变成屏幕坐标,然后绘制到屏幕上。

这样就完成了到2维屏幕的投影。
投影结束后,x,y坐标的范围,是窗口的坐标范围。

***注意:我没有提供3维的世界变换,因为没有必要***
..............................................................................

然后我们对4维变换做个说明:

4维世界变换和4维观察变换之后,顶点到了摄影机空间中,然后再做投影变换:
从摄影机空间中,朝着前方(U 轴正方向!!)取一个“4维观察平截头体”,
然后,把观察平截头体缩放到投影空间中的一个“半超立方体”中,
这个半超立方体的的范围是,x,y,z坐标范围从-1 ~ +1;u坐标范围从0 ~ +1。

不过摄影机空间中观察平截头体之外的顶点,也跟着一起变换到了投影空间里,
这些额外的顶点,在那个半立方体之外,所以需要“剪裁”掉。这个工作,
暂时I4D引擎还没有实现。所以,你心里要有个数,不要让摄影机靠物体太近!
这样的话,比如说,“拍摄”一个4维超立方体,离远一点,就完全没有任何问题了。
因为我迫切要研究4维世界,我等不及,所以,先凑合着用吧^ ^。

然后呢,我没有3D引擎中的“视口缩放”这一步,没必要,所以,
按照最终的x,y,z坐标,填写ID3DVertexBuffer9就可以了。
由于不需要排序(具体原因以后再说),所以第四维u坐标,就没有任何用处了。

这样就完成了到3维世界的投影。

注意(我不需要3D引擎中的“视口缩放”这一步),投影结束后,x,y,z坐标的范围,
是 -1 到 +1。
然后,要设置3D输出变换。
...............................................................................

4维观察平截头体是什么样子的?

3维观察平截头体,可以看成是一个变形的立方体,不是吗?
(立体几何上叫做“4棱锥台”)
变形的立方体,和标准的立方体,各个顶点之间的连接情况是相同的。和图5一样。
(即拓扑结构相同,专业地说,它们是“同胚”的)


那么类似,4维观察平截头体,可以看成是一个变形的4维超立方体。
它的拓扑结构的话,和图6是一样的。

假如我要做5D引擎的话,那么,5维观察平截头体的拓扑结构,大家应该可以想象吧 ^ ^

------------------------------------------------------------------------------
另外,我写了一个CI3DXCamera和CI4DXCamera这两个摄影机类。
其中,CI3DXCamera,和我以前在gameres上贴过的一个CCamera类基本相同。

看一下app.cpp。
InitI4D()中,初始化了I4D对象和两个摄影机(3D摄影机和4D的)
InitDraw()中,初始化了一个4维超立方体的数据。
注意:我使用LineList(线列表)而不是3角形!

顶点建立了之后,需要锁定4维顶点缓冲区,然后用memcpy拷贝顶点:
I4DVERTEX *pVerticesT;
g_pVB1->Lock(&pVerticesT);
{
        memcpy(pVerticesT,pMyVertices,NUM_VERTICES*sizeof(I4DVERTEX));
}
g_pVB1->Unlock();

FrameMove()中,进行4维变换(我没做世界变换),然后做3D输出的变换。

OnKeyDown(WORD KeyCode)中,处理了键盘操作。使用键盘控制摄影机。
我的键盘定义:

3维空间中的前后左右上下平移按键:(沿着摄影机自己的x,y,z轴移动!)
const WORD k3_fore='W',        k3_back= 'S',        k3_left= 'A',        k3_right= 'D',        k3_up='Q',        k3_down='Z';
3维空间中的旋转按键(围绕世界坐标y和z轴来旋转摄影机!)
const WORD k3r_up= 'F',        k3r_down='C',        k3r_left='X',        k3r_right='V';

4维空间中的4个方向上的平移按键:(沿着摄影机自己的x,y,z轴移动!)
const WORD k4_fore='Y',        k4_back= 'H',        k4_left= 'G',        k4_right= 'J',        k4_up=  'T',k4_down='B',k4_up2='U',        k4_down2='N';
4维空间中的旋转按键:(围绕世界坐标的某平面旋转!)
const WORD k4r_xy1='I',        k4r_xz1= 'O',        k4r_xu1= 'P',        k4r_yz1=  219,        k4r_yu1='M',k4r_zu1=190;
const WORD k4r_xy2='K',        k4r_xz2= 'L',        k4r_xu2= 186,   k4r_yz2=  221,        k4r_yu2=188,k4r_zu2=191;
(注:186=";"--219="["--221="]"--188="<"--190=">"--191="?")
需要说的是,3维空间中的旋转,是绕着某个轴进行的,而4维的旋转,需要围绕某个平面进行。
比如:
k4r_xy1 按键:控制围绕xy平面向正方向旋转;
k4r_xy2 按键:控制围绕xy平面向负方向旋转。

最后,是Render()函数:
void Render()
{

        g_pi4d->Clear(I4DCOLOR_XRGB(0,0,0));
        g_pi4d->BeginScene();
        {
                g_pi4d->DrawPrimitive(g_pVB1);
        }
        g_pi4d->EndScene();
/////////////////////
        g_pi4d-&gtresent();

}

9

主题

102

帖子

343

积分

中级会员

Rank: 3Rank: 3

积分
343
 楼主| 发表于 2007-3-1 18:38:00 | 显示全部楼层

Re:欲做4d引擎。

优缺点:

(1)直接式(把4d直接投影到2d):
相当于把3d直接投影到1d上观察的感觉。
直接从2d观察4d很困难,不太直观。
但用户操作简便。只要调整4个方向即可。

(2)间接式(投影到3d空间后,用3d Engine或其他手段观察)
我们想想,把4d投影到3d的感觉,相当于把3d投影到2d的感觉。
而我们的3d游戏都是这么做的,这样做(从2d投影)可以很好地观察3d世界。
同样,从3d投影中可以很好地观察4d世界。
而,我们生活在3d世界中,观察3d投影有许多方法。
但是,缺点是,操作麻烦。要调整4d世界的4个方向。投影之后,
如果用3d引擎观察,还要调整3d世界的3个方向。

28

主题

433

帖子

433

积分

中级会员

Rank: 3Rank: 3

积分
433
发表于 2007-3-1 19:25:00 | 显示全部楼层

Re:欲做4d引擎。

说什么 呢~~

21

主题

109

帖子

127

积分

注册会员

Rank: 2

积分
127
发表于 2007-3-1 19:38:00 | 显示全部楼层

Re:欲做4d引擎。

好哇,但是其用途是?

180

主题

3511

帖子

3520

积分

论坛元老

Rank: 8Rank: 8

积分
3520
发表于 2007-3-1 20:52:00 | 显示全部楼层

Re:欲做4d引擎。

这位高人,
什么叫做 “4D空间” ?
他在画面上与 “3D” 有何区别?

1

主题

78

帖子

82

积分

注册会员

Rank: 2

积分
82
发表于 2007-3-2 09:52:00 | 显示全部楼层

Re:欲做4d引擎。

高人~~~我是彻底无语了。
所谓的3D,是XYZ轴组成的一个空间。而在爱恩斯坦相对论里面所说的4D空间,可以简单理解为现在的3D空间加上时间轴来组成4D空间。建议你去看看物理学巨人的著作~霍金的《时间简史》和《果壳里的宇宙》。
今年第一个搞笑事情

19

主题

73

帖子

73

积分

注册会员

Rank: 2

积分
73
发表于 2007-3-2 09:58:00 | 显示全部楼层

Re:欲做4d引擎。

我准备做 无限D 空间

0

主题

228

帖子

285

积分

中级会员

Rank: 3Rank: 3

积分
285
发表于 2007-3-2 10:42:00 | 显示全部楼层

Re:欲做4d引擎。

4D,时间轴你怎么渲染?

0

主题

57

帖子

57

积分

注册会员

Rank: 2

积分
57
发表于 2007-3-2 13:26:00 | 显示全部楼层

Re:欲做4d引擎。

Great Idea!
但现在来看,自然科学远远不达不到如此境界.而且你的想法有些许混乱和矛盾之处.

6

主题

396

帖子

396

积分

中级会员

Rank: 3Rank: 3

积分
396
发表于 2007-3-2 13:48:00 | 显示全部楼层

Re:欲做4d引擎。

你知不知道你自己在说什么?
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-6-8 06:07

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表