⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 3d 映射原理 .txt

📁 会变语言实现的一些程序
💻 TXT
📖 第 1 页 / 共 2 页
字号:
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 + -