2012年2月4日

VC Directory设置的位置和以前的版本不一样。VS2010之前,VC Directory的设置都是在IDE的Tools->Options中设置的;VS2010改为,分别在每个项目中的Properties中设置。

难道要每个项目都设置一遍常用的Include吗?答案是,不用。

 

在IDE中,打开View->Other Windows->Property Manager。展开树形后,你会发现一个名为“Microsoft.Cpp.Win32.user”的项目

右击并点击“Properties”后,你会看到一个和VC Project properties类似的属性设置框。

 

现在,你应该知道答案了吧。对,就是这里。只要将常用的Include都放入这里就可以达到你要的效果。

 

P.S.

 

      在刚装好VS2010时,发现新建的项目都包含了机器上的STLport 5.1.2。找了一圈目录和文件,重装了好几次还是一样。后来才发现,原来VS2010不知道什么时候,将STLport的目录加到Microsoft.Cpp.Win32.user里了。修改后,重新打开项目就不再有STLport出现。

posted @ 2012-02-04 17:21 小火球 阅读(6768) | 评论 (0)编辑 收藏


2011年2月24日

RTT是现在很多特效里面都会用到的一项很基本的技术,实现起来很简单,也很重要。但是让人不解的是网上搜索了半天只找到很少的文章说这个事儿,不知道是因为太简单还是因为这项技术已经出现很长时间了。总之我是在摸索这个东西的时候绕了不少弯子。现在把具体的实现方法写下来。

渲染到纹理,顾名思义就是把渲染目标从帧缓存变成一个纹理。这样就可以把一个场景渲染后在进行Post Process,做出现在流行的各种特效。另外在利用GPU做通用计算的时候程序也是通过RTT和GPU交换数据的。

实现步骤:
  1. 声明变量
    LPDIRECT3DTEXTURE9 pRenderTexture = NULL; // 目标纹理
    LPDIRECT3DSURFACE9 pRenderSurface = NULL,pBackBuffer = NULL, pTempSurface;
    // pRenderSurface是pRenderTexture 对应的Surface
    // pBackBuffer用于保存原来的Render Target

  2. 创建一个纹理作为渲染目标(Render Target)
    //注意这里的第三个参数必须为
    D3DUSAGE_RENDERTARGET
    //第四个参数决定纹理的格式,不同的场景会要求不同的格式

    pd3dDevice->CreateTexture(TEX_WIDTH,TEX_HEIGHT,1,D3DUSAGE_RENDERTARGET,D3DFMT_R5G6B5,D3DPOOL_DEFAULT,&pRenderTexture,NULL);

    //获得
    pRenderTexture对应的Surface
    pRenderTexture->GetSurfaceLevel(0,&pRenderSurface);
  3. 渲染场景
    //这里保存下原来的Render target,在做完RTT后再恢复
    pd3dDevice->GetRenderTarget(0,&pBackBuffer);
    if( SUCCEEDED( pd3dDevice->BeginScene() ) )
    {
    //设置我们的纹理为render target
    pd3dDevice->SetRenderTarget(0, pRenderSurface);
    pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DXCOLOR(0.0f,0.00f,0.00f,1.00f), 1.0f, 0);


    //重新将render target设置为帧缓存
    pd3dDevice->SetRenderTarget(0, pBackBuffer);

    pd3dDevice->EndScene();
    pBackBuffer->Release();
    }
  4. 善后
    SAFE_RELEASE(pRenderSurface);
    SAFE_RELEASE(pRenderTexture);
这里需要注意的几点:
  • 渲染的时候要选择正确的纹理格式。如果你要在纹理里面保存高精度浮点数的话。通常所用的A8R8G8B8格式每一个颜色分量只有8位,只能表示0-255。详情可以参考DirectX SDK Help中关于D3DFORMAT的说明。
  • 如果你的纹理长宽比和帧缓存的不同的话,那么你需要在切换RenderTarget以后重新设置投影矩阵。否则渲染出来的图像会被拉伸。
  • 纹理的大小不能太大。经过试验发现是在窗口模式下面窗口和纹理的大小不能超过屏幕减去任务栏的大小。如果超过的话似乎对纹理的任何操作都不会有效果。
  • 如果想要验证自己渲染出来的纹理是否正确,可以用D3DXSaveTextureToFile把纹理保存为图像。
  • 如果想要直接访问纹理中的值则要麻烦一些。按照SDK文档里面的说法,作为RenderTarget的纹理是保存在显存上面的,无法lock与unlock。要向访问其中的值需要做如下操作:
    LPDIRECT3DTEXTURE9 text;
    LPDIRECT3DSURFACE9 surf;
    D3DLOCKED_RECT lockbits;
    pd3dDevice->CreateTexture(TEX_WIDTH,TEX_HEIGHT,1,0,D3DFMT_R5G6B5, D3DPOOL_SYSTEMMEM, &text, NULL);
    text->GetSurfaceLevel(0,&surf);
    if (pd3dDevice->GetRenderTargetData(pRenderSurface, surf) == D3D_OK)
    if (surf->LockRect(&lockbits, NULL, D3DLOCK_READONLY) == D3D_OK)
    {
    pRenderSurface->UnlockRect();
    float* bits=(float*)(lockbits.pBits);
    // SAVE BITS TO TEXT FILE
    FILE* ofile = fopen("output.txt", "w");
    for (int i=0; i<64; i++)
    {
    for (int j=0; j<64; j++)
    fprintf(ofile, "(%2.2f,%2.2f,%2.2f) ", bits[i*64*4+j*4], bits[i*64*4+j*4+1], bits[i*64*4+j*4+2]);
    fprintf(ofile, "\n");
    }
    fclose(ofile);
    }
    text->Release();
    surf->Release();

这个技术可以用来在多通道渲染中传递渲染结果。比如可以把RTT出来的结果用来作为第二编渲染中的纹理来使用,这样可以实现水面反射等效果。另外在通用计算中可以用来保存数据。例如可以把GPU数值计算以后的结果保存在纹理中,再用上面所说的方法把数字读出来(如果真要这么做的话别忘了把纹理格式设置为足够大精度的格式,比如说A32B32G32R32F)。

文章引用自:http://ping.csdn.net/tag/hlsl/

posted @ 2011-02-24 10:39 小火球 阅读(1283) | 评论 (1)编辑 收藏


2011年2月22日

资源锁定意味着能通过CPU来访问该资源。锁定选项有以下几种:

D3DLOCK_DISCARD, D3DLOCK_READONLY, and D3DLOCK_NOOVERWRITE 等,但是只有这三种是经常使用的。运行时并不会

检查应用程序是否遵循了制定的锁定标记所规定的访问方式。也就是说,当指定了D3DLOCK_READONLY后,再去写资源操作,会得到意外的结果,但不会提示是由于写操作导致出错。所以,如果在实际资源访问时的操作与锁定选项相矛盾的话,则无法保证以后的释放操作会成功或发生显著的性能损失。

同时只能对纹理资源加一把锁。在对表面进行锁操作时,不能对该表面进行任何其他的加速操作。

对顶点 buffer  和 索引 buffer资源可以同时加几把锁,但是必须保证加解锁数配对。

资源管理就是把系统内存中的内容提交到显存或者把显存中的内容剔除掉的过程。

Direct3D run time 有自己的管理算法 least-recently-used priority 技术。当检测到在一帧中需要同时共存的资源要多与显存限制时,

d3d就会切换到 most-recently-used priority 技术。

创建资源时使用  D3DPOOL_DEFAULT  标记,意味着资源被放在最合适该资源的存储区。经常会被放在显存或AGP中。在defalut pool

中的资源在设备丢失后要被全部释放,在设备重建后要全部恢复。

创建资源时使用D3DPOOL_MANAGED 标记,即该资源是托管资源,该资源在设备丢失后还继续存在,该资源同时存在于内存和显存中,当需要显示时,由内存拷贝到显存中。但是当设备被销毁或重新create 时,所有的资源都会被释放和重建。

d3d所谓的资源管理,是针对 D3DPOOL_MANAGED  来说的。对于顶点buffer和索引buffer不建议使用 D3DPOOL_MANAGED  ,即不建议使用d3d默认的资源管理,因为 D3DPOOL_MANAGED  标记的资源会在系统内存中存在备份,而顶点buffer和索引buffer如果使用D3DPOOL_MANAGED  来创建,该类缓冲的特点要求会在系统内存和显存之间进行大量频繁的数据拷贝,会极大的影响性能。所以这两种buffer建议使用  D3DPOOL_DEFAULT  结合  D3DUSAGE_DYNAMIC 来使用。

另外还有一种情况是不能使用 D3DPOOL_MANAGED  的,那就是创建作为 RenderTarget 的纹理时,如果该种纹理被标记为了托管,意味着该纹理在内存中存在了备份,在渲染期间,它的内容就会在系统内存和显存之间产生大量的数据交换,造成极大的性能损失。因此,renderTarget 的纹理资源必须被创建在  D3DPOOL_DEFAULT  中。如果CPU需要存取该内容,则数据可以通过下面两种方法拷贝到D3DPOOL_SYSTEMMEM 定义的资源中,IDirect3DDevice9::UpdateTexture, or IDirect3DDevice9::UpdateSurface。

正如前面所提到的,当时使用托管方式创建资源时,就不能使用动态方式,如果同时使用这两种标记会引起性能极大损失。动态顶点buffer是被设计用来绘制动态的数据的,这些数据可能由BSP树或者其他的空间划分算法获取到的。根据应用程序自身的资源管理方式而不是d3d的,这些资源被打包,以支持应用程序的需要。动态顶点buffer的数量会很少,因为应用程序同时只允许少量的不同的顶点步长,而只有当步长不同时才有必要创建新的顶点buffer。当我们以这种方式管理动态资源时,才能保证性能不会损失。

当创建资源时,尽量使用D3DPOOL_DEFAULT方式,这使我们能对系统内存和显存的使用情况能有准确的把握。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/cainiao_liu/archive/2010/02/07/5295808.aspx

posted @ 2011-02-22 22:06 小火球 阅读(625) | 评论 (0)编辑 收藏


2010年12月10日

纹理过滤

  Direct3D渲染一个图元时,会将三维图元映射到二维屏幕上。如果图元有纹理,Direct3D就必须用纹理来产生图元的二维渲染图象上每个像素的颜色。对于图元在二维屏幕上图象的每个像素来说,都必须从纹理中获得一个颜色值。我们把这一过程称为纹理过滤(texture filtering)。

  进行纹理过滤时,正在使用的纹理通常也正在被进行放大或缩小。换句话说,这个纹理将被映射到一个比它大或小的图元的图象上。纹理的放大会导致许多像素被映射到同一个纹理像素上。那么结果看起来就会使矮矮胖胖的。纹理的缩小会导致一个像素被映射到许多纹理像素上。其结果将会变得模糊或发生变化。要解决这些问题,我们可以将一些纹理像素颜色融合到一个像素颜色上。

  Direct3D提供了一些方法来简化纹理过滤的过程。它提供了三种类型的纹理过滤:线性过滤(linear filtering)、各向异性过滤(anisotropic filtering)和mipmap过滤(mipmap filtering)。如果不选择纹理过滤,Direct3D还会使用一种叫做最近点采样(nearest point sampling)的技术。

  每种类型的纹理过滤都有各自的优缺点。例如,线性过滤会产生锯齿状的边缘和矮胖的效果。但是,它对系统的消耗却是最小的。另一方面,mipmap过滤的效果通常是最好的,特别是和各项异性过滤混合使用时。但是它却需要很大的内存消耗。

  如果程序使用纹理句柄,可以调用IDirect3DDevice3::SetRenderState方法来设置当前的纹理过滤方法,同时要将第一个参数设置为D3DRENDERSTATE_TEXTUREMAG或D3DRENDERSTATE_TEXTUREMIN,第二个参数要设置为D3DTEXTUREFILTER枚举类型的一个成员。

  程序也可以使用纹理接口指针来设置纹理过滤方法,这是要调用IDirect3DDevice3::SetTexture平台State方法,并将第一个参数设置为要进行纹理过滤的那个纹理的整数索引号(0-7),将第二个参数设置为D3DTSS_MAGFILTER、D3DTSS_MINFILTER或D3DTSS_MIPFILTER,将第三个参数设置为D3DTEXTUREMAGFILTER、D3DTEXTUREMINFILTER或D3DTEXTUREMIPFILTER枚举类型的一个成员。

  下面我们来分别讨论几种纹理过滤方法:

4.1 最近点采样
4.2 线性纹理过滤
4.3 各向异性纹理过滤
4.4 mipmap纹理过滤
 

4.1 最近点采样

  在程序中,我们并不是必须要使用纹理过滤。Direct3D可以被设置来计算纹理像素的地址,这个地址通常不是一个整数值,这时,可以简单的取一个与它最接近的整数地址来代替原地址,并使用这个整数地址上的纹理像素颜色。我们把这一过程称为最近点采样(nearest point sampling)。当纹理的大小与图元图象的大小差不多时,这种方法非常有效和快捷。如果大小不同,纹理就需要进行放大或缩小,这样,结果就会变得矮胖、变形或模糊。

  调用IDirect3DDevice3::SetTexture平台State方法可以来选择最近点采样方法,将它的第一个参数设置为纹理的整数平台索引号(0-7),第二个参数设置为D3DTEXTUREMAGFILTER、D3DTEXTUREMIPFILTER或D3DTEXTUREMIPFILTER,将第三个参数设置为D3DTFG_POINT(设置放大过滤时)、D3DTFN_POINT(缩小时)或D3DTFP_POINT(mipmap时)。

  使用最近点采样是要特别小心,因为在两个纹理像素的分界线上进行采样时,可能产生图象失真(graphic artifacts)。在进行采样时,系统会选择这个采样纹理像素或者是那个采样纹理像素,在穿越分界线时,得到的结果将会产生很大的变化,也许会得到我们不想要的结果。(在使用线性过滤时,当纹理索引穿越两个纹理像素的边界时,最终的纹理像素将会是这两个纹理像素的融合结果。)

  当我们将一个很小的纹理映射到一个很大的多边形上——这被称为“放大(magnification)”——时,我们会看到这种效果。例如,当我们使用一个看起来象西洋跳棋盘一样的纹理时,最近顶采样会得到一个更大的西洋跳棋盘,并带有明显的边界;而采用线性过滤时,最后在多边形上,跳棋格颜色会平滑的进行过渡,不会有明显的边界出现。

  大多数时候,不使用最近点采样往往能得到最好的结果。并且在现在,大部分的硬件都是针对线性过滤进行优化的。如果你确实想得到最近顶采样那样的效果——比如要清晰的显示一些文本特征——那么就要尽量避免在纹理像素分界处进行采样,否则,就会产生图象的失真。下图中向我们展示了一种图象的失真:

 

  注意上面的两个靠右的方格,它们就产生了失真。要避免这样的失真,我们首先要了解Direct3D最近点采样的规则。Direct3D是将一个浮点纹理坐标,范围为[0.0, 1.0](包括它们),映射到一个整数的纹理像素空间,范围为[–0.5, n–0.5],n是一个给定大小的纹理中纹理像素的数量。最终的纹理索引是最近的整数。这样的映射会导致在纹理像素分界处进行采样。

  下面我们来看一个例子,我们使用D3DTADDRESS_WRAP纹理寻址模式来对多边形进行渲染,使用Direct3D提供的映射对一个有4个纹理像素宽度的纹理进行映射,u纹理索引如下:

 

  注意图中的纹理坐标0.0和1.0,它们正好在两个纹理像素的分界处。更具最近点采样法的规则,纹理坐标的范围为[–0.5, 4–0.5],4是纹理的宽度。这样,被采样的纹理像素就是第0个纹理像素,它的纹理索引值就为1.0。但是,如果纹理坐标只比1.0小一点,那么被采样的纹理像素就应该是第n个而不是第0个。

  这也就表示在将一个小的纹理使用最近点采样方法放大映射到屏幕空间时,会产生在纹理像素分界处采样的情况,这样就会产生失真。

  要将浮点纹理坐标映射到整数纹理像素上是很困难的,并且通常也没有必要这样做。大多数硬件在执行时使用一种迭代的方法来计算每个像素位置上的纹理坐标。这种方法往往会隐藏这些错误,因为它们在迭代过程中会逐渐积累起来。

  Direct3D参考光栅使用直接估计(direct-evaluation)的方法来计算每个像素位置上的纹理索引。与迭代法不同,直接估计过程中的一些错误会比较自由的表现出来。这样,在使用最近点采样时出现的错误就会比较明显。

  我们最好只在必需时再使用最近点采样法。在使用它时,最好能使纹理坐标明显的偏离纹理像素的分界处。

 

4.2 线性纹理过滤

  Direct3D使用的线性过滤方法是双线性过滤(bilinear filtering)。和最近点采样一样,双线性过滤首先要计算一个纹理像素的地址,这个地址通常不是整数地址。然后,找到一个地址最接近的整数地址纹理像素。另外,Direct3D渲染模块还要计算与最近采样的点相邻的四个纹理像素的加权平均(weighted average)。

  可以调用IDirect3DDevice3::SetTexture平台State方法来选择双线性纹理过滤,并将第一个参数设置为纹理的整数索引号(0-7),将第二个参数设置为D3DTEXTUREMAGFILTER、D3DTEXTUREMIPFILTER或D3DTEXTUREMIPFILTER,将第三个参数设置为D3DTFG_LINEAR(放大)、D3DTFN_LINEAR(缩小)或D3DTFP_LINEAR(mipmap)。

 

4.3 各向异性纹理过滤

  各向异性是对一个三维物体纹理像素的可见的变形,这个物体的表面朝向屏幕平面,并与之有一定的角度。各向异性图元的像素在映射到纹理像素时,它的形状会发生变形。Direct3D用反映射到纹理空间的屏幕像素的延伸率(长度/宽度)来度量一个像素的各向异性(anisotropy)。

  各向异性纹理过滤可以和线性过滤或mipmap过滤联合使用。调用IDirect3DDevice3::SetTexture平台State方法可以使各项异性过滤有效,同时要将第一个参数设置为纹理的整数索引值(0-7),将第二个参数设置为D3DTEXTUREMAGFILTER、D3DTEXTUREMINFILTER,将第三个参数设置为D3DTFG_ANISOTROPIC(放大)或D3DTFN_ANISOTROPIC(缩小)。

  程序还要调用IDirect3DDevice3::SetTexture平台State方法将各向异性度(degree of anisotropy)设置在0到1之间(不包含它们),同时将诶一个参数设置为纹理的整数索引值(0-7),将第二个参数设置为D3DTSS_MAXANISOTROPY,最后一个参数就是各向同性度(degree of isotropy)。

  将各向同性度(degree of isotropy)设置为1将使各向同性过滤无效(设置为任何比1大的值将会使它有效)。检查D3DPRIMCAPS结构中的D3DPRASTERCAPS_ANISOTROPY标志,以确定各向异性度的可能的范围。

 

4.4 Mipmap纹理过滤

  Mipmap纹理技术用来降低场景渲染的时间消耗。同时也提高了场景的真实感。但它的缺点是要占用大量的内存空间。

  下面我们来讨论mipmap纹理技术的有关内容:

4.4.1 什么是mipmap
4.4.2 创建一系列mipmap
4.4.3 选择和显示Mipmap
 

4.4.1 什么是mipmap?

  一个mipmap就是一系列的纹理,每一幅纹理都与前一幅是相同的图样,但是分辨率都要比前一幅有所降低。mipmap中的每一幅或者每一级图象的高和宽都比前一级小二分之一。Mipmap并不一定必须是正方形。

  高分辨率的mipmap图象用于接近观察者的物体。当物体逐渐远离观察者时,使用低分辨率的图象。Mipmap可以提高场景渲染的质量,但是它的内存消耗却很大。

  Direct3D将mipmap描绘成一系列相互联系的表面。高分辨率的纹理位于开始处,并与下一级纹理相互联系。以此类推,纹理相互联系,逐渐排列到分辨率最小的一级。

  下面这套插图显示了这样的一个例子。这套纹理是一个三维场景中一个集装箱的标签。当我们创建了一个mipmap时,分辨率最高的一幅纹理就是这一套纹理的第一个。这套mipmap中的每一个纹理宽高都是前一个纹理宽高的二分之一。这样,最大分辨率的纹理是256x256,接下来的纹理就是128x128,最后一个纹理就是64x64。

  我们有一个能看到这个标签的最大距离。如果观察者从远处向标签走近,那么场景中首先会显示最小的一幅纹理,它的大小是64x64的。

 

  当观察者走进标签时,我们就使用更高分辨率一幅纹理:

 

  当观察者走到允许的最近距离时,我们使用分辨率最高的那幅纹理:

 

  这是方法能够模拟纹理的透视效果并能够减少处理时的计算量。与将一幅纹理用于不同的分辨率相比,这种方法更加快速。

  Direct3D能够访问mipmap中与我们想要输出的分辨率最接近的那个纹理设置,并将像素映射到它的纹理像素空间中。如果最终图象的分辨率在mipmap纹理的分辨率的中间,那么Direct3D会对两幅纹理中的纹理像素进行检查,并将它们的颜色值进行融合。

  如果要使用mipmap,首先要创建一套mipmap。详细内容见“创建一系列mipmap”。如果程序使用的使纹理句柄,那么就必须将mipmap选择作为当前纹理。如果使用纹理接口指针,那么就要将mipmap选择作为当前纹理设置中的第一个纹理。详细内容见“多纹理融合”。

接下来,程序要设置用来对纹理像素采样的过滤方法。Mipmap过滤最快的方法就是让Direct3D选择最近的纹理像素。我们用D3DTFP_POINT枚举值来选择这一方法。如果程序使用D3DTFP_LINEAR枚举值,可以得到更好的过滤效果。它会选择最近的mipmap,然后找到当前像素映射在纹理中的位置,计算这个位置周围纹理像素的加权平均。

 

4.4.2 创建一系列Mipmap

  要创建一个表示mipmap中某一级的表面,需要在DDSURFACEDESC结构中声明DDSCAPS_MIPMAP和DDSCAPS_COMPLEX标志,并将这个结构传递给IDirectDraw4::CreateSurface方法。由于所有的mipmap也是纹理,因此也要声明DDSCAPS_TEXTURE标志。

  我们也可以自己来创建mipmap中的每一级,然后使用IDirectDrawSurface4::AddAttachedSurface方法将它们链接在一起。但是我们并不推荐使用这样的方法。许多3-D硬件都对IDirectDraw4::CreateSurface方法优化了它们的驱动程序。因此,通过调用IDirectDrawSurface4::AddAttachedSurface来建立mipmap链接的程序可能会发现mipmapping没有想象中的那么快捷。

  下面的例子向我们展示了如何使用IDirectDraw4::CreateSurface方法来创建一个mipmap链接,这个mipmap分为5级,大小分别为256×256、128×128、64×64、32×32和16×16:


// This code fragment assumes that the variable lpDD is a
// valid pointer to a DirectDraw interface.

DDSURFACEDESC ddsd;
LPDIRECTDRAWSURFACE4 lpDDMipMap;
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_MIPMAPCOUNT;
ddsd.dwMipMapCount = 5;
ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;
ddsd.dwWidth = 256UL;
ddsd.dwHeight = 256UL;

ddres = lpDD->CreateSurface(&ddsd, &lpDDMipMap);
if (FAILED(ddres))
 …… …… 

 

  在用IDirectDraw4::CreateSurface方法创建一系列表面时,我们可以忽略mipmap的级数,这样每一级都是前一级大小的二分之一,直到最小的尺寸大小为止。我们也可以忽略高和宽,这样IDirectDraw4::CreateSurface就会创建我们所声明的级数,并将最小一级的大小设为1×1。

注:一个mipmap链接中的每一个表面的大小都是链接中前一个表面大小的二分之一。如果一mipmap中最顶端一级的大小为256×128,那么第二级的大小就是128×64,第三级为64×32,直到2×1为止。如果你声明了dwWidth和dwHeight成员的大小,就要注意一些限制条件。也就是要注意,在dwMipMapCount中声明的级数大小不能使任何一级mipmap的高或宽的值小于1。我们来看一个简单的最顶端一级大小为4×2的mipmap表面:dwMipMapCount所允许的最大值为2。任何大于2的值都会使高或宽变成小数,这是不允许的。

  创建了mipmap表面之后,需要将表面与一个纹理相互联系起来。如果使用纹理句柄,就可以使用前面在“创建一个纹理句柄”中介绍的方法。如果使用的是纹理接口指针,请看“获得一个纹理接口指针”部分。

 

4.4.3 选择并显示Mipmap

  如果程序使用纹理句柄,就要将mipmap纹理的句柄指派为当前纹理。详细内容见“当前纹理”部分。

  如果程序使用纹理接口指针,就要将mipmap纹理设置作为当前纹理列表的第一个纹理。详细内容见“多纹理融合”部分。

  程序选择了mipmap纹理设置之后,it must assign values from the D3DTEXTUREFILTER enumerated type to the D3DRENDERSTATE_TEXTUREMAG and D3DRENDERSTATE_TEXTUREMIN render states.然后,Direct3D会自动执行mipmap纹理过滤。

  程序也可以自己来设置mipmap表面的链接,这是要使用IDirectDrawSurface4::GetAttachedSurface方法,并要在DDSCAPS结构中声明DDSCAPS_MIPMAP和DDSCAPS_TEXTURE标志。下面的例子中展示了这一过程:


LPDIRECTDRAWSURFACE lpDDLevel, lpDDNextLevel;
DDSCAPS ddsCaps;
HRESULT ddres;

lpDDLevel = lpDDMipMap;
lpDDLevel->AddRef();
ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP;
ddres = DD_OK;
while (ddres == DD_OK)
{
 // Process this level.
 ddres = lpDDLevel->GetAttachedSurface( &ddsCaps, &lpDDNextLevel);
 lpDDLevel->Release();
 lpDDLevel = lpDDNextLevel;
}

if ((ddres != DD_OK) && (ddres != DDERR_NOTFOUND))
{
 // Code to handle the error goes here
}

 

  程序还需要自己实现一个mipmap链接来将位图数据加载到链接中的每一个表面。

  Direct3D会明确保存一个mipmap链接中的级数。当程序获得一个mipmap的表面描述时(调用IDirectDrawSurface4::Lock或IDirectDrawSurface4::GetSurfaceDesc方法),DDSURFACEDESC结构的dwMipMapCount成员就获得了mipmap的级数,包括最顶端一级。对于mipmap中的那些不是最顶端的级来说,dwMipMapCount成员详细说明了链接中的级数。

 

posted @ 2010-12-10 22:14 小火球 阅读(1470) | 评论 (0)编辑 收藏


2010年11月3日

一. 在c中分为这几个存储区
1.栈 - 由编译器自动分配释放
2.堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收
3.全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。- 程序结束释放
4.另外还有一个专门放常量的地方。- 程序结束释放
                                                                                                                                             
在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内定义的static表示只在该函数体内有效。另外,函数中的"adgfdf"这样的字符串存放在常量区。比如:

int a = 0//全局初始化区
char *p1; //全局未初始化区
void main()
{
    
int b; //
    char s[] = "abc"//
    char *p2; //
    char *p3 = "123456"//123456{post.content}在常量区,p3在栈上
    static int c = 0//全局(静态)初始化区
    p1 = (char *)malloc(10); //分配得来得10字节的区域在堆区
    p2 = (char *)malloc(20); //分配得来得20字节的区域在堆区
    strcpy(p1, "123456");
    
//123456{post.content}放在常量区,编译器可能会将它与p3所指向的"123456"优化成一块
}

 

二.在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区
1.栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。
2.堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
3.自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
4.全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
5.常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)

三. 谈谈堆与栈的关系与区别
具体地说,现代计算机(串行执行机制),都直接在代码底层支持栈的数据结构。这体现在,有专门的寄存器指向栈所在的地址,有专门的机器指令完成数据入栈出栈的操作。这种机制的特点是效率高,支持的数据有限,一般是整数,指针,浮点数等系统直接支持的数据类型,并不直接支持其他的数据结构。因为栈的这种特点,对栈的使用在程序中是非常频繁的。对子程序的调用就是直接利用栈完成的。机器的call指令里隐含了把返回地址推入栈,然后跳转至子程序地址的操作,而子程序中的ret指令则隐含从堆栈中弹出返回地址并跳转之的操作。C/C++中的自动变量是直接利用栈的例子,这也就是为什么当函数返回时,该函数的自动变量自动失效的原因。

和栈不同,堆的数据结构并不是由系统(无论是机器系统还是操作系统)支持的,而是由函数库提供的。基本的malloc/realloc/free 函数维护了一套内部的堆数据结构。当程序使用这些函数去获得新的内存空间时,这套函数首先试图从内部堆中寻找可用的内存空间,如果没有可以使用的内存空间,则试图利用系统调用来动态增加程序数据段的内存大小,新分配得到的空间首先被组织进内部堆中去,然后再以适当的形式返回给调用者。当程序释放分配的内存空间时,这片内存空间被返回内部堆结构中,可能会被适当的处理(比如和其他空闲空间合并成更大的空闲空间),以更适合下一次内存分配申请。这套复杂的分配机制实际上相当于一个内存分配的缓冲池(Cache),使用这套机制有如下若干原因:
1. 系统调用可能不支持任意大小的内存分配。有些系统的系统调用只支持固定大小及其倍数的内存请求(按页分配);这样的话对于大量的小内存分类来说会造成浪费。
2. 系统调用申请内存可能是代价昂贵的。系统调用可能涉及用户态和核心态的转换。
3. 没有管理的内存分配在大量复杂内存的分配释放操作下很容易造成内存碎片。

堆和栈的对比
从以上知识可知,栈是系统提供的功能,特点是快速高效,缺点是有限制,数据不灵活;而堆是函数库提供的功能,特点是灵活方便,数据适应面广泛,但是效率有一定降低。栈是系统数据结构,对于进程/线程是唯一的;堆是函数库内部数据结构,不一定唯一。不同堆分配的内存无法互相操作。栈空间分静态分配和动态分配两种。静态分配是编译器完成的,比如自动变量(auto)的分配。动态分配由alloca函数完成。栈的动态分配无需释放(是自动的),也就没有释放函数。为可移植的程序起见,栈的动态分配操作是不被鼓励的!堆空间的分配总是动态的,虽然程序结束时所有的数据空间都会被释放回系统,但是精确的申请内存/ 释放内存匹配是良好程序的基本要素。

    1.碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以>参考数据结构,这里我们就不再一一讨论了。
    2.生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。
    3.分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
    4.分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

    明确区分堆与栈:
    在bbs上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿他第一个开刀。
    首先,我们举一个例子:

void f()

    
int* p=new int[5];
}


这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针p呢?他分配的是一块栈内存,所以这句话的意思就是:在栈内存中存放了一个指向一块堆内存的指针p。在程序会先确定在堆中分配内存的大小,然后调用operator new分配内存,然后返回这块内存的首地址,放入栈中,他在VC6下的汇编代码如下:
    00401028    push         14h
    0040102A    call            operator new (00401060)
    0040102F    add           esp,4
    00401032    mov          dword ptr [ebp-8],eax
    00401035    mov          eax,dword ptr [ebp-8]
    00401038    mov          dword ptr [ebp-4],eax
    这里,我们为了简单并没有释放内存,那么该怎么去释放呢?是delete p么?澳,错了,应该是delete []p,这是为了告诉编译器:我删除的是一个数组,VC6就会根据相应的Cookie信息去进行释放内存的工作。
    好了,我们回到我们的主题:堆和栈究竟有什么区别?
    主要的区别由以下几点:
    1、管理方式不同;
    2、空间大小不同;
    3、能否产生碎片不同;
    4、生长方向不同;
    5、分配方式不同;
    6、分配效率不同;
    管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
    空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。当然,我们可以修改:
    打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit。
注意:reserve最小值为4Byte;commit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。
    堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。

另外对存取效率的比较:
代码:
char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:

void main()
{
    
char a = 1;
    
char c[] = "1234567890";
    
char *="1234567890";
    a 
= c[1];
    a 
= p[1];
    
return;
}


对应的汇编代码
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了.
    无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果,就算是在你的程序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉,编写稳定安全的代码才是最重要的。

static用来控制变量的存储方式和可见性
       函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义一个全局的变量,但定义为一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅受此 函数控制)。

       需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见。

       static的内部机制:
       静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。
       这样,它的空间分配有三个可能的地方,一是作为类的外部接口的头文件,那里有类声明;二是类定义的内部实现,那里有类的成员函数定义;三是应用程序的main()函数前的全局数据声明和定义处。
      静态数据成员要实际地分配空间,故不能在类的声明中定义(只能声明数据成员)。类声明只声明一个类的“尺寸和规格”,并不进行实际的内存分配,所以在类声 明中写成定义是错误的。它也不能在头文件中类声明的外部定义,因为那会造成在多个使用该类的源文件中,对其重复定义。
      static被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间,静态数据成员按定义出现的先后顺序依次初始化,注意静态成员嵌套时,要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。

       static的优势:
       可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的 值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。

        引用静态数据成员时,采用如下格式:
         <类名>::<静态成员名>
    如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式
来引用静态数据成员。

       PS:
      (1)类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致
了它仅能访问类的静态数据和静态成员函数。
      (2)不能将静态成员函数定义为虚函数。
      (3)由于静态成员声明于类中,操作于其外,所以对其取地址操作,就多少有些特殊
,变量地址是指向其数据类型的指针 ,函数地址类型是一个“nonmember函数指针”。

      (4)由于静态成员函数没有this指针,所以就差不多等同于nonmember函数,结果就
产生了一个意想不到的好处:成为一个callback函数,使得我们得以将C++和C-based X W
indow系统结合,同时也成功的应用于线程函数身上。
      (5)static并没有增加程序的时空开销,相反她还缩短了子类对父类静态成员的访问
时间,节省了子类的内存空间。
      (6)静态数据成员在<定义或说明>时前面加关键字static。
      (7)静态数据成员是静态存储的,所以必须对它进行初始化。
      (8)静态成员初始化与一般数据成员初始化不同:
      初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆;
      初始化时不加该成员的访问权限控制符private,public等;
           初始化时使用作用域运算符来标明它所属类;
           所以我们得出静态数据成员初始化的格式:
         <数据类型><类名>::<静态数据成员名>=<值>
      (9)为了防止父类的影响,可以在子类定义一个与父类相同的静态变量,以屏蔽父类的影响。这里有一点需要注意:我们说静态成员为父类和子类共享,但我们有 重复定义了静态成员,这会不会引起错误呢?不会,我们的编译器采用了一种绝妙的手法:name-mangling 用以生成唯一的标志。

补充:new  delete[],基本类型的对象没有析构函数(例如 int , char ),所以回收基本类型组成的数组空间 delete  delete[] 都是应该可以如: int p = new int[10], delete p 和delete[]p 都可 。但是对于类对象数组(如string strArr = new string[10]),只能 delete[]。对 new 的单个对象,只能 delete 不能 delete[] 回收空间 。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/AtlasHR/archive/2009/05/08/4160245.aspx

posted @ 2010-11-03 11:04 小火球 阅读(311) | 评论 (0)编辑 收藏


仅列出标题  下一页

posts - 28, comments - 3, trackbacks - 0, articles - 0

Copyright © 小火球