2012年3月8日

图形学资源(期刊与实验室)

国内外图形学研究机构

posted @ 2012-03-08 15:29 帅哥帅 阅读(265) | 评论 (0)编辑 收藏

2012年3月1日

3D Graphic File Formats

http://www.mediatel.lu/workshop/graphic/3D_fileformat/h_format.html

posted @ 2012-03-01 02:11 帅哥帅 阅读(287) | 评论 (0)编辑 收藏

2012年2月25日

UML学习



UML
组件图:
参考文章:http://www.ibm.com/developerworks/rational/library/dec04/bell 
组件图表示:
订单系统对库存系统的引用:


组件接口的二种表示方法:


第二副图左边表示组件提供的对外接口(:P 可以叫棒棒糖接口),右边的是需要外部提供的接口(插座接口)


各个组件间的关系图


组件内部关系完整图:


序列图:
参考文章:http://www.ibm.com/developerworks/rational/library/3101.html 

普通形式:
发送消息到下一个模块中,图中表示analyst是FinacialAnalyst的一个实例,它用了system实例的getAvailableReports()这个方法,注意,发送的方法消息是由下一个模块实现。
调用自己的实现的函数:



几个特殊的情况
if...else


switch



循环:


引用另一个序列图:



break


parallel

posted @ 2012-02-25 14:17 帅哥帅 阅读(360) | 评论 (0)编辑 收藏

2012年2月24日

wxWidget小试

之前用wxlua写过脚本界面,今天尝试了一下wxWidget的win32版本。
没有界面设计工具怎么行,什么年代了肯定不能纯手工打造界面。
于是找得code:Blocks,开源的,才22M,一用,很牛B,基本上编译器功能全了,基于插件的设计和ECLIPCE一样强大。
但是如果用vs自带的cl编译的话,还要配置很多,设置断点还得用cdb,太麻烦了,我不就是想找一个界面设计的工具么,你给我连编译器都准备好了,过了。window下还是习惯Vs,没办法,房子都是别人建的,在别人房子里还是得守别人的规则。
不过有一点,如果是跨平台的话,用code:Blocks+GCC+wxWidget绝对是强大,而且还能在mac中一样用啊,强大啊强大,汗奔。不过鉴于本人太懒,不想编译gcc版本的wxWidget了,还是先放下这个诱人的工具。
最后尝试开源的,也没什么好选择的,就WxFormBuilder这个东东了。
设计与编码分离,也算是比较不错的选择了,以后做界面就用他了。MFC,win32的东西实在是不想碰,实在是不利于水平的提高,界面也丑死了。
不过wx,几M的dll,也是很蛋疼的。快速开发界面的有木有,有木有,一定要我用脚本来界面么。

设计好界面,做一个继承类,直接加入工程中,以后就界面就交给基类了,改动的话,直接复制过来就好了,我们的部分逻辑在派生的类中进行。
用widget2.9+wXForm3.2做界面时,调试时会报错:
无法解析Sizer::Remove(wxWindow*) ,跟踪进去看发现如下定义
1#if WXWIN_COMPATIBILITY_2_6
2    // Deprecated in 2.6 since historically it does not delete the window,
3    // use Detach instead.
4    wxDEPRECATED( virtual bool Remove( wxWindow *window ) );
5#endif // WXWIN_COMPATIBILITY_2_6
原来这个东东已经过时了,是为了兼容2.6所保留了,由于我用的是2.9,只兼容2.8,由于在生成wx库的时候,没有定义2.6的宏,所以肯定没这没个函数的定义啦。
但是由于wx/setup
#define WXWIN_COMPATIBILITY_2_6 1
里,有如下定义,这样生成库时不兼容2.6,头文件里却定义为1.
改回来:
#define WXWIN_COMPATIBILITY_2_6 0

wxFrom自动生成四个文件
BaseForm.h BaseForm.cpp
AppForm.h AppForm.cpp
全部复制到工程里
然后:
.h
 1#pragma  once
 2#include <wx/wx.h>
 3
 4class TestWxFrame : public wxApp
 5{
 6public:
 7    TestWxFrame();
 8    virtual ~TestWxFrame();
 9    virtual bool OnInit();
10
11}
;
12
13DE CLARE_APP(TestWxFrame)

.cpp

1IMPLEMENT_APP(TestWxFrame)
2bool TestWxFrame::OnInit()
3{
4   AppForm*TF = new AppForm((wxWindow*)NULL);
5   TF->Show(true);
6   return true;
7}

8
9


DECLARE_APP()这个不用说了,和MFC一样的,动态声明全局函数wxGetApp()
1#define wxDECLARE_APP(appname)              \
2    extern appname& wxGetApp()

IMPLEMENT_APP()没什么新意,界面消息等机制就那么一回事,包括XNA,OGRE等的回调机制都是利用多虚性。这个基类A定义好虚函数,在派生类B中重载虚函数如上面的OnInit(),然后再在循环中调用A * = GetApp()得到B类的指针,这样不管B怎么变,只要是A的派生类,重载了一些固有的行为,整个框架都不需要任何改变。(- -!,这也是刚学习MFC时,会被搞晕了的原因,因为所有的细节都被隐藏起来了,更不用说他的消息回调机制了,太过方便了就让人变得蠢,所以不太喜欢微软的东东,越学越成为一个CODE FARMER,不用费脑筋的。不过DX除外)
下面是隐藏的东东,一目了然,把它脱光光一看,就没有之前的欲望啦,原来也是main,原来也是地球人写的代码。
 1#define wxIMPLEMENT_APP(appname)            \
 2    wxIMPLEMENT_WX_THEME_SUPPORT            \
 3    wxIMPLEMENT_APP_NO_THEMES(appname)
 4
 5#define wxIMPLEMENT_APP_NO_THEMES(appname)  \
 6    wxIMPLEMENT_WXWIN_MAIN                  \
 7    wxIMPLEMENT_APP_NO_MAIN(appname)
 8
 9//:)熟悉的WinMain来了
10#define wxIMPLEMENT_WXWIN_MAIN                                              \
11    extern "C" int WINAPI WinMain(HINSTANCE hInstance,                      \
12                                  HINSTANCE hPrevInstance,                  \
13                                  wxCmdLineArgType WXUNUSED(lpCmdLine),     \
14                                  int nCmdShow)                             \
15    {                                                                       \
16        wxDISABLE_DEBUG_SUPPORT();                                          \
17                                                                            \
18        /* NB: We pass NULL in place of lpCmdLine to behave the same as  */ \
19        /*     Borland-specific wWinMain() above. If it becomes needed   */ \
20        /*     to pass lpCmdLine to wxEntry() here, you'll have to fix   */ \
21        /*     wWinMain() above too.                                     */ \
22        return wxEntry(hInstance, hPrevInstance, NULL, nCmdShow);           \
23    }
                                                                       \
24    wxIMPLEMENT_WXWIN_MAIN_BORLAND_NONSTANDARD
25
26//<动态定义GetApp()之类的来了
27#define wxIMPLEMENT_APP_NO_MAIN(appname)                                    \
28    wxAppConsole *wxCreateApp()                                             \
29    {                                                                       \
30        wxAppConsole::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE,         \
31                                        "your program");                    \
32        return new appname;                                                 \
33    }
                                                                       \
34    wxAppInitializer                                                        \
35        wxTheAppInitializer((wxAppInitializerFunction) wxCreateApp);        \
36    appname& wxGetApp() return *static_cast<appname*>(wxApp::GetInstance()); }    \
37    wxDECLARE_APP(appname)

生成图:(比较丑)



不要忘记wx也是有操作系统底层函数的哦,基本上不用用到win32的API,一个wx就可以解决所有问题了。最重要的,他是跨平台的!所以尽量不要用win API.

posted @ 2012-02-24 00:53 帅哥帅 阅读(1815) | 评论 (0)编辑 收藏

2012年2月22日

地形制作全攻略(转载)

     摘要: GameRes游戏开发资源网http://www.gameres.com   地形制作全攻略 作者:程东哲     前言 零.地形简介 一.地形生成算法 1.Fault Formation 2.Midpoint Displacement 二.小纹理生成大地形纹理 1.地形纹理介绍 2.小纹理插值生成大纹理。 3.添加细节纹理 4.添加光...  阅读全文

posted @ 2012-02-22 23:16 帅哥帅 阅读(3225) | 评论 (0)编辑 收藏

Direct3D中实现图元的鼠标拾取(转)

文章来自:http://dev.gameres.com/Program/Visual/3D/pick_2004_529.htm
Direct3D中实现图元的鼠标拾取

 

BY 重剑,2004.5.28 重剑空间 

索引:

1、什么是拾取,拾取能做什么?

2、拾取操作的步骤和实现

    2.1.  变换并获得通过视点和屏幕上点击点的射线矢量(Dir)

 

2.1.1 确定鼠标选取点的屏幕坐标

 

2.1.2 得到Dir在观察坐标空间内的表示

 

2.1.3 转换Dir到世界坐标空间,并得到观察点在世界坐标系中的坐标

 

    2.2   使用射线矢量对场景中的所有三角形图元求交,获得三角形索引值和重心坐标。

 

2.2.1 D3D扩展函数实现求交

2.2.2射线三角面相交的数学算法

2.2.3  拾取完成根据获得的中心坐标计算我们关心的常见量

3、结束及声明

4、参考文献

 

补充:重心坐标的概念

 

 

 

3D交互图形应用程序中,常常要用鼠标去选择图形,其实现的机制基于鼠标拾取算法。本文主要讲述如何在D3D中实现图元的鼠标拾取。为了讨论简单,本文假定读者理解D3D 坐标变换流程和基本的图形学知识,如果阅读有困难请参考相关资料。

1、什么是拾取,拾取能做什么?

首先,拾取操作指当我们在屏幕上用鼠标点击某个图元应用程序能返回该图元的一个标志和某些相关信息。有图形程序设计经验的人都知道,有这些信息就表示我们有了对该图元的控制权,我们可以删除,可以编辑,可以任意对待该图元,至于你到底想干什么,就是阁下自己的事了^_^

2、拾取操作的步骤和实现

拾取算法的思想很简单:得到鼠标点击处的屏幕坐标,通过投影矩阵和观察矩阵把该坐标转换为通过视点和鼠标点击点的一条射入场景的光线,该光线如果与场景模型的三角形相交(本文只处理三角形图元),则获取该相交三角形的信息。本文讲述的方法除可以得到三角形的一个索引号以外还可以得到相交点的重心坐标。

    从数学角度来看,我们只要得到射线的方向矢量和射线的出射点,我们就具备了判断射线与空间一个三角面是否相交的条件,本文主要讨论如何获得这些条件,并描述了射线三角面相交判断算法和D3D的通常实现方法。   

根据拾取操作的处理顺序,大概可以依次分为以下几个步骤

2.1  变换并获得通过视点和屏幕上点击点的射线矢量(Dir

详细介绍之前,为了大家方便理解,我们要先简单说一下d3d坐标转换的大概流程,如下图:

 

所以我们要通过一系列的反变换,得到我们关心的值在世界坐标中的表示。

2.1.1 确定鼠标选取点的屏幕坐标

这一步是非常简单的Windows给我们提供了API来完成屏幕坐标的获取,使用GetCursorPos获得鼠标指针位置,然后再利用ScreenToClient转换坐标到客户区坐标系(以窗口视区左上角为坐标原点,单位为像素),设该坐标为(POINT screenPt)。

2.1.2 得到Dir在观察坐标空间内的表示

在观察坐标系中,Dir是一条从观察坐标原点出发的射线,所以我们只需要再确定一个该射线经过的点,就可以得到它在观察坐标系中的表示。假设我们要求的射线上的另外一点为该射线与透视投影平截头体近剪切面的交点,针对最普遍的透视投影而言,透视投影平截头体经投影变换后,变成一个1/2立方体(请允许我这么叫^_^,因为它的大小为一个正方体的一半,x,y方向边长为2z方向为1)如图:

 

投影坐标系以近剪切面中心为坐标原点,该立方体从z轴负向看过去与图形程序视区相对应,最终近剪切面(前剪切面)上一点与屏幕坐标之间的对应关系如下图所示:

 

根据比例关系,screenPt与投影空间上的点projPt之间的关系为

假设图形程序窗口的宽为screenWidth,高为screenHeight,

projPt.x = (screenPt.x-screenWidth/2)/screenWidth*2; (公式1)

projPt.y = (screenPt.y-screenHeight/2)/screenHeight*2; (公式2)

projPt.z =0;(实际该值可任意取,不影响最终结果。为了处理简单,我们取改值为0,表示该点取在近剪切面上)

得到projPt后,我们需要做的是把该点坐标从投影空间转换到观察空间(view space),

根据透视投影的定义,可假设点(projPt.xprojPt.yprojPt.z)

对应的其次坐标为

(projPt.x*projPt.wprojPt.y*projPt.wprojPt.z*projPt.wprojPt.w)

 

我们可以通过 GetTransform(      D3DTS_PROJECTION,    &ProjMatrix)函数获得投影矩阵ProjMatrix,则根据观察空间到投影空间的变换关系则

(projPt.x*projPt.wprojPt.y*projPt.wprojPt.z*projPt.wprojPt.w)

 = (viewPt.xviewPt.yviewPt.z, 1)*pProjMatrx;

根据定义和图形学原理

ProjMatrix = =

 

所以,

(projPt.x*projPt.wprojPt.y*projPt.wprojPt.z*projPt.wprojPt.w)

= ( viewPt.x*ProjMatrix._m11,

viewPt.y*ProjMatrix._m22,

viewPt.z*Q-QZn,

viewPt.z)

 

所以

projPt.x*projPt.w = viewPt.x*ProjMatrix._m11

projPt.y*projPt.w = viewPt.y*ProjMatrix._m22

projPt.z*projPt.w = viewPt.z*Q-QZn (注意projPt.z = 0

projPt.w = viewPt.z;

解得

viewPt.x = projPt.x*Zn/ ProjMatrix._m11;

viewPt.y = projPt.y*Zn/ ProjMatrix._m22;

viewPt.z = Zn;

好了,到这里为止我们终于求出了射线与近剪切面交点在观察坐标系中的坐标,现在我们拥有了射线的出发点(0,0,0)和射线方向上另外一点(viewPt.x,viewPt.y,viewPt.z),则该射线的方向矢量在观察空间中的表示可确定为(viewPt.x-0,viewPt.y-0,viewPt.z-0,化简一下三个分量同除近剪切面z坐标Zn,该方向矢量可写作

DIRview = (projPt.x/projMatrix._m11,projPt.y/projMatrix._m22,1)

代入公式1,公式2

DIRview.x = (2*screenPt.x/screenWidth-1)/projMatrix._m11;

DIRview.y = (2*screenPt.y/screenHeight-1)/projMatrix._m22;

DIRview.z = 1;

其中screenWidth和screenHeight可以通过图像显示的backBuffer的目标表面(D3DSURFACE_DESC)来获得,该表面在程序初始化时由用户创建。

2.1.3 转换Dir到世界坐标空间,并得到观察点在世界坐标系中的坐标

由于最终的运算要在世界坐标空间中进行,所以我们还需要把矢量DIRview从观察空间转换为世界坐标空间中的矢量DIRworld。

因为

DIRview = DIRworld*ViewMatrix;

其中ViewMatrix为观察矩阵,在D3D中可以用函数GetTransform( D3DTS_VIEW, &ViewMatrix )得到。

所以DIRworld = DIRview * inverse_ViewMatrix,其中inverse_ViewMatrix为

ViewMatrix的逆矩阵。

     观察点在观察坐标系中坐标为OriginView(0,0,0,1),所以其在世界坐标系中的坐标同样可以利用ViewMatrix矩阵,反变换至世界坐标系中,事实上我们可以很简单的判断出,其在世界坐标系中的表示为:

OriginWorld = (inverse_ViewMatrix._41,

inverse_ViewMatrix._42,

inverse_ViewMatrix._43,

1);

到这里为止,判断射线与三角面是否相交的条件就完全具备了。

 

2.2   使用射线矢量对场景中的所有三角形图元求交,获得三角形索引值和重心坐标。

这一步骤地实现由两种途径:

第一种方法非常简单,利用D3D提供的扩展函数D3DXIntersect可以轻松搞定一切。见2.1

第二种方法就是我们根据空间解析几何的知识,自己来完成射线三角形的求交算法。一般来讲,应用上用第一种方法就足够了,但是我们如果要深入的话,必须理解相交检测的数学算法,这样才能自由的扩展,面对不同的需求,内容见2.2

下面分别讲解两种实现途径:

2.2.1 D3D扩展函数实现求交

这种方法很简单也很好用,对于应用来说应尽力是用这种方式来实现,毕竟效率比自己写得要高得多。

实际上其实没什么好讲的,大概讲一下函数D3DXIntersect

D3D SDK该函数声明如下

HRESULT D3DXIntersect(      

    LPD3DXBASEMESH pMesh,
    CONST D3DXVECTOR3 *pRayPos,
    CONST D3DXVECTOR3 *pRayDir,
    BOOL *pHit,
    DWORD *pFaceIndex,
    FLOAT *pU,
    FLOAT *pV,
    FLOAT *pDist,
    LPD3DXBUFFER *ppAllHits,
    DWORD *pCountOfHits
);
l          pMesh指向一个ID3DXBaseMesh的对象,最简单的方式是从.x文件获得,描述了要进行相交检测的三角面元集合的信息,具体规范参阅direct9 SDK
l          pRayPos 指向射线发出点
l          pRayDir 指向前面我们辛辛苦苦求出的射线方向的向量
l          pHit 当检测到相交图元时,指向一个true,不与任何图元相交则为假
l          pU 用于返回重心坐标U分量
l          pV返回重心坐标V分量
l          pDist 返回射线发出点到相交点的长度
注意:以上红色字体部分均指最近的一个返回结果(即*pDist最小)
l          ppAllHits用于如果存在多个相交三角面返回相交的所有结果
l          pCountOfHits 返回共有多少个三角形与该射线相交

 

补充:重心坐标的概念

其中pU和pV用到了重心坐标的概念,下面稍作描述

一个三角形有三个顶点,在迪卡尔坐标系中假设表示为V1(x1,y1,z1),V2(x2,y2,z2),V3(x3,y3,z3),则三角形内任意一点的坐标可以表示为 pV = V1 + U(V2-V1) + V(V3-V1),所以已知三个顶点坐标的情况下,任意一点可用坐标(U,V)来表示,其中 参数U控制V2在结果中占多大的权值,参数V控制V3占多大权值,最终1-U-V控制V1占多大权值,这种坐标定义方式就叫重心坐标。

 

2..2.2射线三角面相交的数学算法

     使用d3d扩展函数,毕竟有时不能满足具体需求,掌握了该方法,我们才能够获得最大的控制自由度,任意修改算法。

    

已知条件射线源点orginPoint,三角形三个顶点 v1,v2,v3,射线方向 Dir

(均以三维坐标向量形式表示)

算法目的判断射线与三角形是否相交,如果相交求出交点的重心坐标(U,V)和射线原点到交点的距离T

 

我们可先假设射线与三角形相交则交点(注以下均为向量运算,*数乘,dot(X,Y) X点乘,crossXYXY叉乘;UVT为标量)

则:

IntersectPoint = V1 + U*(V2-V1) + V*(V3-V1) ;

IntersectPoint = originPoint + T*Dir

 

所以

orginPoint + T*Dir = V1 + U*(V2-V1) + V*(V3-V1);

整理得:

 

这是一个简单的线性方程组,若有解则行列式不为0

根据T,U,V的含义当T>0, 0<U<1,0<V<1,0<U+V<1时该交点在三角形内部,

解此方程组即可获得我们关心的值,具体解法不再赘述,克莱姆法则就够了(详细见线性代数):射线原点到相交点的距离T,和交点的中心坐标(U,V)

下面给出Direct 9 SDK示例程序中的实现代码

IntersectTriangle( const D3DXVECTOR3& orig,

                   const D3DXVECTOR3& dir, D3DXVECTOR3& v0,

                   D3DXVECTOR3& v1, D3DXVECTOR3& v2,

                   FLOAT* t, FLOAT* u, FLOAT* v )

{

    // 算出两个边的向量

    D3DXVECTOR3 edge1 = v1 - v0;

    D3DXVECTOR3 edge2 = v2 - v0;

 

//补充说明:
 
//分解为一向量点乘另它二向量的叉乘,本代码中Cross,再dot的结果就是得到上面所说行阵式的值。
//<使用方向向量之前,最好标准化D3DXVec3Normalize,因为根据上面公式T*dir,dir必须为单位化向量

    D3DXVECTOR3 pvec;

    D3DXVec3Cross( &pvec, &dir, &edge2 );

 

    // 如果det为0,或接近于零则射线与三角面共面或平行,不相交

//此处det就相当于上面的

    FLOAT det = D3DXVec3Dot( &edge1, &pvec );

 

    D3DXVECTOR3 tvec;

    if( det > 0 )

    {

        tvec = orig - v0;

    }

    else

    {

        tvec = v0 - orig;

        det = -det;

    }

 

    if( det < 0.0001f )

        return FALSE;

 

    // 计算u并测试是否合法(在三角形内)

    *u = D3DXVec3Dot( &tvec, &pvec );

    if( *u < 0.0f || *u > det )

        return FALSE;

 

    // Prepare to test V parameter

    D3DXVECTOR3 qvec;

    D3DXVec3Cross( &qvec, &tvec, &edge1 );

 

    //计算u并测试是否合法(在三角形内)

    *v = D3DXVec3Dot( &dir, &qvec );

    if( *v < 0.0f || *u + *v > det )

        return FALSE;

 

    /*计算t,并把t,u,v放缩为合法值(注意前面的t,v,u不同于算法描述中的相应量,乘了一个系数det),注意:由于该步运算需要使用除法,所以放到最后来进行,避免不必要的运算,提高算法效率*/

    *t = D3DXVec3Dot( &edge2, &qvec );

    FLOAT fInvDet = 1.0f / det;

    *t *= fInvDet;

    *u *= fInvDet;

    *v *= fInvDet;

 

    return TRUE;

}

 

 

2.2.3  拾取完成根据获得的中心坐标计算我们关心的常见量,。

根据重心坐标(U,V,我们可以很容易的算出各种相关量比如纹理坐标和交点的差值颜色,假设以纹理坐标为例设V1,V2,V3的纹理坐标分别为T1(tu1,tv1),T2(tu2,tv2),T3(tu3,tv3)则交点的坐标为

 

IntersectPointTexture = T1 + U(T2-T1) + V(T3-T1)

 

3、结束及声明

Ok, 到这里为止关于拾取的相关知识就介绍完了,小弟第一次写这种文章,不知道有没有把问题说清楚,希望对大家有所帮助,有任何问题可以给我发email: jzhang1@mail.xidian.edu.cn

或者到我的网站留言: www.heavysword.com

 

声明:

本文写作的目的是为了广大D3D学习者方便学习服务,文中算法为作者参考相关文献总结,作者无意把这些据为自己的成果,所有权原算法提出者所有(参阅参考文献),文中代码为D3d SDK的示例内容,由笔者进行了必要的解释,代码版权归microsoft所有。

4、参考文献

【1】Microsoft DirectX 9.0 SDK,microsoft

【2】fast,Minimun Storage Ray/Triangle Intersection,Tomas Moler,Ben Trumbore

 

 BY

(转载请注明出处)

posted @ 2012-02-22 10:27 帅哥帅 阅读(716) | 评论 (1)编辑 收藏

3D图形学编程指南笔记

3D图形学编程指南笔记

第二章
自由度:
前提:约束对象体,使所有的点之间的距离不变
平面物体有三个自由度,如位于x轴上的线段,则在沿x,y方向上有二个自由度,再沿原点旋转有一个自由度。
3D物体有六个自由度,三轴,三旋转。
自由度主要是有关平移和旋转。

有关世界到屏幕的过程。
1.得到世界矩阵。  
      首先物体有物体空间,我们要把物体变换到世界空间来,我们要预先知道物体在世界坐标系中的translate,rotate,所以DX里创建世界矩阵是可以根据平移、旋转来创建世界矩阵的。有了这几个参数,就可以把物体从物体坐标系变成世界坐标系中来。
      具体方法可以采用原点重合的方法,就是把物体坐标的原点通过一系列的平移旋转变化与世界坐标系重合,这过程中用到的联合矩阵,就是世界矩阵。
2.得到视口矩阵,观察矩阵。
      把物体变换到世界坐标系后,还应该变换到视口坐标系,也就是观察坐标系,因为我们是通过视口坐标系来观察的,我们眼睛看向的正前方只会是视线所在的方向,也就是z轴所在。所以得把物体从世界坐标系变化到我们的视口坐标系来。
      同样是通过坐标系变换的方法,我们可以得到观察矩阵。
3.得到屏幕坐标,投影矩阵。
      把物体变换到视口坐标系中后,我们要从屏幕上观察到,就必须把3D的视口坐标系,转换成我们可以通过屏幕看到的2D坐标,也就是屏幕坐标系,设备坐标系,在DX中设备坐标系是,正常的笛卡儿坐标第,原点在正中心,X正方向向右,Y正方向向上,单元为1。从视口坐标系到设备坐标系的过程,就可以得到投影坐标系。
注意,如果屏幕是长方形的,有高宽比,那么对于投影矩阵来说,在垂直方向上的最终的y还要乘以宽高比.相当于缩短了垂直方向的视野,达到与屏幕的高宽比相同的视野,具体到投影矩阵,就是视距d还要乘以一个宽高比(垂直方向上)。
要完成物体的拾取,其实就是一个屏幕到世界的反过程。
举例来说:
如果在屏幕上的拾取点是x,y(窗口客户区坐标,640*480)
那么屏幕到世界的流程是
1.窗口客户区坐标-》设备坐标系        
先进行坐标系缩放(2*x/640)再进行平移-1
x(设) = (2*x/640) - 1
2.到视口坐标系
x(视) = x(设)/proj_matrix._11     (proj_maxtrix._11是投影矩阵的第一个元素)
同理到y
3.到世界坐标系,乘以视图和世界矩阵的逆矩阵,得到最终拾取点在世界坐标系中的位置
这样,就完成了屏幕到世界的坐标系变换,然后通过观察点与搭取点决定的射线,就可以找到交点了。

主要流程

0) Obtain your mouse coordinates within the client area

1) Get your Projection matrix and View matrix if no Model matrix required.

2) Multiply View * Projection

3) Inverse the results of multiplication

4) Construct a vector4 consisting of

x = mouseposition.x within a range of window x - transform to values between -1 and 1

y = mouseposition.y within a range of window y - transform to values between -1 and 1 - remember to invert mouseposition.y if needed

z = the depth value ( this can be obtained with glReadPixel) - you can manually go from -1 to 1 ( zNear, zFar )

w = 1.0

5) Multiply the vector by inversed matrix created before 6) Divide result vector by it's w component after matrix multiplication ( perspective division )

 1  POINT mousePos;
 2        GetCursorPos(&mousePos);
 3        ScreenToClient( this->GetWindowHWND(), &mousePos );         
 4
 5        CMatrix4x4 matProjection = m_pCamera->getViewMatrix() *  m_pCamera->getProjectionMatrix() ;
 6
 7        CMatrix4x4 matInverse = matProjection.inverse();
 8
 9        float in[4];
10        float winZ = 1.0;
11
12
13        in[0]=(2.0f*((float)(mousePos.x-0)/(this->GetResolution().x-0)))-1.0f,
14        in[1]=1.0f-(2.0f*((float)(mousePos.y-0)/(this->GetResolution().y-0)));
15        in[2]=2.0* winZ -1.0;
16        in[3]=1.0;          
17
18        CVector4 vIn = CVector4(in[0],in[1],in[2],in[3]);
19        pos = vIn * matInverse;
20
21        pos.w = 1.0 / pos.w;
22
23        pos.x *= pos.w;
24        pos.y *= pos.w;
25        pos.z *= pos.w;
26
27        sprintf(strTitle,"%f %f %f / %f,%f,%f ",m_pCamera->m_vPosition.x,m_pCamera->m_vPosition.y,m_pCamera->m_vPosition.z,pos.x,pos.y,pos.z);
28
29        SetWindowText(this->GetWindowHWND(),strTitle);

参考文章:http://stackoverflow.com/questions/7692988/opengl-math-projecting-screen-space-to-world-space-coords-solved?answertab=active#tab-top


posted @ 2012-02-22 09:33 帅哥帅 阅读(450) | 评论 (0)编辑 收藏

2012年2月21日

Material 和 Shader

关于Material和Shader的概念纠结很久了,一直没找到合适的答案。看起来是一个东西,但总感觉应该不同,有所区别。
今天用FX Composer终于找到感觉了。
Shader可以说是代码段,是没有输入参数的,如纹理
Material是实例化的Shader就是说Material是包括了输入的纹理的,所以不同的纹理就有不同的Material,但用的Shader是相同的。
理解了这个概念就在设计类的时候就可以把Shader和Material分开来设计,Shader负责脚本的加载,而Material负责脚本参数的传入,这样就可以复用了。
可以参考:http://www.leadwerks.com/files/Tutorials/CPP/Materials_And_Shaders.pdf

posted @ 2012-02-21 23:28 帅哥帅 阅读(1093) | 评论 (0)编辑 收藏

2012年2月20日

静态成员(转)

//<  处理静态成员函数要引用普通类成员的办法,可以获取该类的实例,再引用类的普通成员,因为静态成员函数没有传入this指针

文章来源:http://tech.e800.com.cn/articles/2009/1231/1262237064944_1.html

类体中的数据成员的声明前加上static关键字,该数据成员就成为了该类的静态数据成员。和其他数据成员一样,静态数据成员也遵守public/protected/private访问规则。同时,静态数据成员还具有以下特点:

 
1.静态数据成员的定义

静态数据成员实际上是类域中的全局变量。所以,静态数据成员的定义(初始化)不应该被放在头文件中。

其定义方式与全局变量相同。举例如下:

 

xxx.h文件

class base{

private:

static const int _i;//声明,标准c++支持有序类型在类体中初始化,但vc6不支持。

};

 

xxx.cpp文件

const int base::_i=10;//定义(初始化)时不受private和protected访问限制.

 

注:不要试图在头文件中定义(初始化)静态数据成员。在大多数的情况下,这样做会引起重复定义这样的错误。即使加上#ifndef #define #endif或者#pragma once也不行。

 
2.静态数据成员被 类 的所有对象所共享(包括该类派生类的对象)

即派生类对象与基类对象共享基类的静态数据成员。举例如下:

class base{

public :

static int _num;//声明

};

int base::_num=0;//静态数据成员的真正定义

 

class derived:public base{

};

 

main()

{

base a;

derived b;

a._num++;

cout<<"base class static data number _num is"<<a._num<<endl;

b._num++;

cout<<"derived class static data number _num is"<<b._num<<endl;

}

// 结果为1,2;可见派生类与基类共用一个静态数据成员。

 
3.静态数据成员可以成为成员函数的可选参数(普通数据成员则不可以)

举例如下:

class base{

public :

static int _staticVar;

int _var;

void foo1(int i=_staticVar);//正确,_staticVar为静态数据成员

void foo2(int i=_var);//错误,_var为普通数据成员

};

 
4.★静态数据成员的类型可以是所属类的类型(普通数据成员则不可以)

普通数据成员的只能声明为 所属类类型的指针或引用。举例如下:

 

class base{

public :

static base _object1;//正确,静态数据成员

base _object2;//错误

base *pObject;//正确,指针

base &mObject;//正确,引用

};

 
5.★ 静态数据成员的值在const成员函数中可以被合法的改变

举例如下:

 

这个特性,我不知道是属于标准c++中的特性,还是vc6自己的特性。

class base{

public:

base(){_i=0;_val=0;}

 

mutable int _i;

static int _staticVal;

int _val;

void test() const{//const 成员函数

 

_i++;//正确,mutable数据成员

_staticVal++;//正确,static数据成员

_val++;//错误

 

}

};

int base::_staticVal=0;

 
二 静态成员函数

静态成员函数没有什么太多好讲的。

 
1.静态成员函数的地址可用普通函数指针储存(普通成员函数地址需要用 类成员函数指针来储存)

举例如下:

class base{

static int func1();

int func2();

};

 

int (*pf1)()=&base::func1;//普通的函数指针

int (base::*pf2)()=&base::func2;//成员函数指针

 

 
2.静态成员函数不可以调用类的非静态成员

因为静态成员函数不含this指针。

 
3.静态成员函数不可以同时声明为 virtual、const、volatile函数

举例如下:

class base{

virtual static void func1();//错误

static void func2() const;//错误

static void func3() volatile;//错误

};

 

posted @ 2012-02-20 19:15 帅哥帅| 编辑 收藏

UNICODE 处理(转)


原文:http://www.vckbase.com/bbs/prime/viewprime.asp?id=125
这是一个许多人(包括我自己)曾经或至今仍疑惑的问题(这里我们只讨论UTF-16,即双字节版本)。

1.关于UNICODE
首先,UNICODE主要使用的字符类型是WCHAR,定义是unsigned short。从定义我们可以看出这是一个双字节的类型,就是每一个字符占2个字节。这样的话,可以表示的字符类型就可以多达6万多。所有之前的ASCII码分布在0x0000-0x00ff之间,而汉字(包括big5)分布在0x4e00到0x9fff之间。整个unicode包含了几乎世界上所有的文字。关于UNICODE的细节,可以参看以下网页
http://www.unicode.org/unicode/standard/translations/s-chinese.html

2.为什么要使用UNICODE
1)COM:在COM规范中,明确指定了必须使用UNICODE类型,这正是微软充分考虑了跨平台的结果。这也是为什么经常在COM中可以看到BSTR(WCHAR*)类型
2)WIN2000和WINNT:在这两个平台中,默认的字符处理方式是UNICODE。即使你写了一个非UNICODE(multibyte)的程序,系统在执行的时候仍然会对你的字符进行一次转换,这样无疑浪费了CPU时间,使用UNICODE可以有效的提高程序的运行效率(仅使用于这两个平台)。当然将来的XP也会如此。
3)通用性:使用UNICODE可以使我们不在为汉字和英文字符的判断而烦恼(都是2个字节)。

3.如何使用UNICODE
1)首先推荐的类型是TCHAR(通用字符类型)。当你定义了_UNICODE宏的时候,TCHAR就是WCHAR,当你没有定义这个宏的时候,TCHAR就是char,很不可思议吧,我们可以来看一下TCHAR的定义:

#ifdef  UNICODE                     // r_winnt
typedef WCHAR TCHAR, *PTCHAR;
#else   /* UNICODE */               // r_winnt
typedef char TCHAR, *PTCHAR;
#endif /* !_TCHAR_DEFINED */

上面的代码来自WINNT.H我剔除了一些无关的部分。现在一切都显而易见了。
通过TCHAR,我们只需要这样一段代码:
TCHAR tStr[] = _T("t code");
MessageBox(tStr);
就可以支持UNICODE和MULTIBYTE两种版本。_T宏的作用就是转换成TCHAR。

2)关于其他的处理
首先是常用的CString,它本身就支持UNICODE。下面的例子说明了用法:

CString *pFileName = new CString("c:\\tmpfile.txt");

#ifdef _UNICODE

   m_hFile = CreateFile(pFileName->AllocSysString(),
                        GENERIC_READ | GENERIC_WRITE,
                        FILE_SHARE_READ,
                        NULL,
                        OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL);
#else
   m_hFile = CreateFile(pFileName->GetBuffer(pFileName->GetLength()),
                        GENERIC_READ | GENERIC_WRITE,
                        FILE_SHARE_READ,
                        NULL,
                        OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL);
#endif

另外,VC还提供了一些函数如WideCharToMultiByte和MultiByteToWideChar还有另外的一些宏来支持转换。大家可以看MSDN。

posted @ 2012-02-20 17:52 帅哥帅| 编辑 收藏

仅列出标题  下一页
<2024年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

导航

统计

常用链接

留言簿

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜