📄 3d 映射原理 .txt
字号:
3D 映射原理
作者:ham 于2007-11-16上传
--------------------------------------------------------------------------------
By ham
下载本程序需要使用的MASM6.15版本
计算机技术迅猛发展,多媒体技术也是日新月异,3D技术是一门很热门的话题,比如很具有代表性的游戏CS,我想很多人多玩过,非常的经典游戏(说一声,我从没有玩过,只是被它的3D效果给吸引)。现在最出色的应该是极品飞车11了,看过广告,据说效果超好。
本文就对3D中最基础的知识--如何把计算好的3D坐标数据映射到2D的屏幕上,此算法是我自己推导的,其实是很简单的,没有任何高深的数学理论都是基本的东西,如发现不当之处,指正一下。
在讲本文之前,我对文中所要涉及到的基本数学知识简单的阐述一下,以方便理解。
向量点乘:
这反映的是一 向量在 向量上的投影积,也就是说 在 上的投影向量的模长乘以 向量的模长。
向量叉乘:
向量叉乘所得的向量与 向量 都是垂直的,其模长等于 与 所围成的平行四边形的面积。
求 向量在 向量上的投影向量:
其中 就是向量 与向量 夹角的余弦值,显然再乘以 的模长 就是 在 投影的模长, 是 的单位向量,与模长相乘后得到的就是 向量在 向量上的投影向量了。
现在就开始讲述3D数据如何投影到2D的屏幕上。
如上图:
向量与 向量是垂直的,屏幕就是由这两垂直向量所够成, 是屏幕的x轴坐标, 是屏幕的y轴坐标,o就是屏幕的原点坐标(因为屏幕是无法显示负坐标的,所以最后还要把计算好的位置加上一个偏移值),点p就是要投影到屏幕上的空间中的一点,点c就是已经在屏幕上投影好的点,a与b就是对应的屏幕坐标。
这里所述的屏幕其实是在xyz空间直角坐标系内的一个平面。
我们来说明原理。
我们先求出屏幕在原点o处的法向量坐标,法向量坐标 ,此时我们就可以看出投影到屏幕上的点c其实就在向量 与 (原点与点p所构成的向量)所构成的平面与屏幕的交线上,此交线的一向量为 ,为了求出 ,我们先求出向量 与 所构成的平面的法向量 ,具定理 与 是垂直的,我现在就可以求出向量 了,向量 。
此时我们就只要求出 在 中的投影向量 (原点与点c所构成的向量)就可以了:
我们这时似乎感觉到只要分别求出 在 与 投影向量的模长就可以了(在 上的投影模长就是屏幕的x轴坐标,在 上的投影模长就是屏幕的y轴坐标),但是向量的模长始终是正数(原来应该为负的成为了正值),这是我们所不能接受的。
我们可以采取另外的方法:求出 与 , 与 的夹角的余弦值,然后再乘以 的模长。
与 的夹角的余弦值余弦值为:
与 的夹角的余弦值余弦值为:
则:
+偏移
+偏移
这时的x,y就是映射到屏幕的坐标。
现在开始讲如何对映射好的图形进行改变视角。
如上图所示:
是屏幕的x轴参考坐标的单位向量, 是屏幕的y轴坐标的单位向量, 是屏幕在原点的法向量的单位向量。
根据原理已知 与 就可以求出法向量
现在要使映射好的3D图形中的一点p在屏幕x轴方向移动,则对应在3维直角坐标系中 垂直于 旋转θ度,所以现在只要求出此时的向量 的值就可以求出新的在屏幕上的x轴坐标。
假设旋转后的向量 是向量 ,如何才能求出向量 呢?
因为 与 都是单位向量,那么根据 在 与 上的两条投影向量的合成向量就可以求出 ,很明显在 上的的投影向量为:
在 上的的投影向量为:
所以:
此时必须更新 与 :
最后根据 与 求出点p在平面上的新坐标。
同理在屏幕y轴移动,根据在 与 求出新的y轴参考坐标,然后再更新 。
讲了这么多理论,我们现在就来看看具体的程序:
我们来绘制空间函数,下图是本程序的一张截图:
我们现在就来分析此程序。此程序是我写的,如果发现指令使用闲罗嗦请原谅,尤其是sse指令本人还第一次这样“大规模”的使用,水平暂且不高,指令优化很不到位。
.386
.xmm
.model flat,stdcall
option casemapnone
include windows.inc
include gdi32.inc
includelib gdi32.lib
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
.data
VectorR db 16 dup()
VectorL db 16 dup()
VectorT db 16 dup()
VectorA db 16 dup()
VectorB db 16 dup()
;红色的变量的内存地址必须对齐16字节,不可随便改变前后顺序
MoveX dd
MoveY dd
Move1X dd
Move1Y dd
DownJh dd
hInstance dd
hWinMain dd
lpBmp dd
hdc dd
.data
VectorU dd 1.0, 0.0, 0.0, 0;屏幕x轴参考向量
VectorV dd 0.0, 1.0, 0.0, 0;屏幕y轴参考向量
x dd -100.0;空间函数x轴变量
y dd -100.0;空间函数y轴变量
z dd ;空间函数z轴变量
dd
.const
add2 dd 2.0
szClassName db 'My 3D',0
szCaptionMain db '3D Graph(鼠标左键按下拖动改变方向)',0
.code
Projection proc lpVectorU,lpVectorV,lpVectorR
;此函数用来求VectorU向量在VectorV上的投影向量VectorR
mov esi,lpVectorU
mov edi,lpVectorV
mov ebx,lpVectorR
movaps XMM0,[esi]
movaps XMM1,[edi]
movaps XMM6,[edi]
mulps XMM0,XMM1
mulps XMM1,XMM1
pshufd XMM2,XMM0,1
pshufd XMM3,XMM0,2
pshufd XMM4,XMM1,1
pshufd XMM5,XMM1,2
addss XMM0,XMM2
addss XMM1,XMM4
addss XMM0,XMM3
addss XMM1,XMM5
divss XMM0,XMM1
pshufd XMM1,XMM0,0
mulps XMM6,XMM1
movaps [ebx],XMM6
ret
Projection endp
VectorAbs proc lpVector
;此函数用来求向量的模长,返回在eax中
local @ABS
mov esi,lpVector
movaps XMM0,[esi]
mulps XMM0,XMM0
pshufd XMM2,XMM0,1
pshufd XMM3,XMM0,2
addss XMM0,XMM2
addss XMM0,XMM3
movd @ABS,XMM0
finit
fld @ABS
fsqrt
fst @ABS
mov eax,@ABS
ret
VectorAbs endp
VecMul proc lpVectorU,lpVectorV,lpVectorR
;此函数用来求向量VectorU与向量VectorV的叉乘,
;结果放入lpVectorR指定的内存中
mov esi,lpVectorU
mov edi,lpVectorV
mov ebx,lpVectorR
pshufd XMM0,[esi],10000110b
pshufd XMM1,[edi],00101001b
pshufd XMM2,[esi],0001b
pshufd XMM3,[edi],0100b
mulps XMM0,XMM1
mulps XMM2,XMM3
movapd XMM4,XMM0
shufps XMM4,XMM2,011101b
shufps XMM0,XMM2,001000b
subps XMM4,XMM0
movaps [ebx],XMM4
ret
VecMul endp
SetBmpPixel proc lpBitMap,_x,_y
;把指定的点坐标设置到位图中
mov edi,lpBitMap
mov eax,dword ptr[edi+4]
mov ebx,_x
mov ecx,_y
cmp ebx,eax
ja @f
cmp ecx,dword ptr[edi+8]
ja @f
mul ecx
add eax,ebx
shl eax,2
mov dword ptr[edi+eax+44],0
ret
@@
mov dword ptr[edi+2996034+56],0
ret
SetBmpPixel endp
Function proc lpVector
;空间函数或者自行编辑空间函数
; _______ _______
;f(x,y)=2000sin((√x^2+y^2)10)√x^2+y^2
mov edi,lpVector
finit
fld _10
fld _2000
fld dword ptr[edi+4]
fld dword ptr[edi]
fmul st,st
fxch
fmul st,st
fadd
fsqrt
fld st
fdiv st,st(3)
fsin
fdiv st,st(1)
fmul st,st(2)
fst dword ptr[edi+8]
ret
_10 dd 10.0
_2000 dd 2000.0
Function endp
IntstAngle proc lpVectorU,lpVectorV
;用来求两条向量夹角的余弦值
local @ls
local @ls1
local @ls2
mov esi,lpVectorU
mov edi,lpVectorV
invoke VectorAbs,esi
mov ebx,eax
invoke VectorAbs,edi
mov esi,lpVectorU
movd XMM4,ebx
movd XMM5,eax
movaps XMM0,[esi]
mulps XMM0,[edi]
pshufd XMM1,XMM0,1
pshufd XMM2,XMM0,2
mulss XMM4,XMM5
addss XMM0,XMM2
addss XMM0,XMM1
divss XMM0,XMM4
movd eax,XMM0
ret
IntstAngle endp
Circumgyrate proc
;对参考平面(屏幕)进行变换坐标,在上文的理论分析中已述,用于图形变换
local ls
local ls1
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -