关于D3D的一些知识

来源:互联网 发布:java程序员的晋升之路 编辑:程序博客网 时间:2024/05/17 08:46

http://www.cnblogs.com/lancidie/category/222816.html


这个人写了很多D3D的东西,有空过去看看

可编程管道下的相机裁剪(这个问别人的,也不知道对不对)

有一个方法是根据世界矩阵,相机矩阵,投影矩阵计算出视锥体(可以是模型坐标系下的,可以是世界坐标系下的,也可以是相机坐标系下的),关于怎么求这个视锥体

http://www.cnblogs.com/lancidie/archive/2010/10/11/1847764.html,用这个视锥体对场景进行裁剪,裁剪后的点送到vertex shader里面,这个是程序主动裁剪的,

还有一部分是程序自动裁剪的

 vertex shader传给pixel shader中会有一次。
pixel shader 绘制到target的时候还会有一次。
一共两次自动裁剪

关于裁剪平面有两个方法
方法1:

在过去的固定渲染管道时代,剪裁平面的实现较为简单,比如在DirectX 9中,可以先设定剪裁平面在世界坐标系下的方程(ax+by+cz+d=0),再调用SetClipPlane(DWORD Index,CONST float * pPlane)这个API函数就可以了。

附上例子程序:

vPosition=D3DXVECTOR3(0,0,0);//平面上一个点
vNormal=D3DXVECTOR3(0,1,0);//法向量
D3DXPlaneFromPointNormal( &clipplane, &vPosition, &vNormal );//生成剪裁平面
m_pDevice()->SetClipPlane( 0, (float*)clipplane); 

这么做的话平面下方的点都会被裁剪,

但是如果在可编程管道的情况下,你设置的裁剪平面方程会被认为是在裁剪坐标系下的,所以当你在可编程管道的情况下调用SetClipPlane函数的时候会发生错误,

这个时候如果你把你的裁剪平面方程变换到裁剪坐标系下,然后调用SetClipPlane的时候就会正确了,这个时候vertex shader会自动的裁剪掉所有裁剪平面以下的点

如果使用的是固定渲染管线(没有使用着色器),裁剪面的方程式(也就是ax+by+cz+d=0中的a、b、c、d),被认为是世界空间中的。
如果使用顶点着色器代替了固定管线,裁剪面的方程式就被当成裁剪空间中的,个人觉得所谓的裁剪空间也就是透视投影空间。


方法2:不管那么多,直接定义裁剪平面方程,将这个平面传到shader里面,直接在shader里面计算每一个点是否应该被裁剪掉,

或者直接根据裁剪平面方程,定义一个矩阵,使位于平面以下的点经过这个矩阵变换后被裁剪掉,然后直接把这个矩阵传到shader里面,

在vertex shader里面进行这个矩阵的变换来裁剪掉你不需要的点



当" locking vertex buffers_锁定顶点缓存"时:

如果顶点缓存的创建标志是POOL_DEFAULT: 

如果没有指定任何标志,程序将被暂停,因为它强制程序和GPU同步操作。->低效

如果指定D3DLOCK_NOOVERWRITE 标志,应用程序不会改变缓存区已存在的内容,运行库会在以后继续使用这块缓存。当应用程序调用这个函数时,驱动程序立即返回。->高效

如果指定D3DLOCK_DISCARD 标志,程序将会更新整个缓存的值,实际上是分配一块新的缓存,即驱动程序重命名它。->高效

[注解:因为CPU和GPU是异步的操作,所以当CPU通过系统总线和GPU同步时,需要等到GPU把当前的工作做完。例如,当GPU正在对一块缓存进行DMA操作时,但往往CPU并不对GPU操作的那块缓存进行操作,所以CPU可以和GPU一起工作。当不指定操作标志时,CPU等待GPU完成绘制工作才更新顶点缓存,所以低效。如果指定D3DLOCK_NOOVERWRITE,表示CPU只更新顶点缓存中剩余的缓存,不考虑是否有其他图形绘制是正在使用这个的缓冲区段绘制图形,强制更新那段缓存并返回,而不像默认参数0那样等待前面的绘制结束,而不更新已经写入的顶点值,所以在CPU写入的时候,GPU可以并行的对那些已经存在的顶点值进行DMA等操作,所以高效;如果使用D3DLOCK_DISCARD 标志,说明当前分配的缓存大小不够了,需要重新使用缓存,CPU对这些新分配的缓存区域进行写操作,GPU这时可能还在异步处理旧的缓存区,所以这种调用也是高效的。调用完毕,收回释放的缓存。]

D3DLOCK_NOOVERWRITE:表示你的程序可以肯定,对于你将要锁定的buffer中进行修改的那部分数据,绝对之前没有在提交的dp范围内,就是绝对不会over write已经dp的数据,这样GPU的DMA操作不会被打断,它可以放心把这个大buffer交给你控制,而你只写入一部分,同时gpu可以读取同一buffer的其他部分(你刚才dp提交的),这样保证gpu和cpu协同工作,没有idle产生。

D3DLOCK_DISCARD:当你对于这个buffer已经有的内容不感兴趣,比如对于cpu驱动的骨骼动画,你cpu保留了所有数据,cpu计算完毕,准备上传新一帧的动画,那就discard原先的数据,那gpu如果用完了buffer,那它直接把这个buffer返回给你,如果它正忙着使用这个buffer,那它给你创建个新buffer,对于而言,这些透明。


这俩flags非常关键,不用不行。特别对于粒子系统这样需要频繁更新的系统,一定要慎重。


CPU 和GPU是可以并行工作的,如果GPU正在使用一块内存上的数据绘制,而CPU要修改这块数据,就会出现CPU等待GPU或GPU等待CPU的情况,造成资源浪费。
如果GPU在使用一块内存的时候,用D3DLOCK_DISCARD标志锁定,表示我们对原来的数据不敢兴趣了,丢弃是相对于我们来说的,GPU会分配一块新内存,用来保存我们更新的数据,这时GPU可以使用原来的内存继续绘制。
用D3DLOCK_NOOVERWRITE锁定表示,我们需要修改数据,并且仍然需要原来的数据,这时锁定GPU占用的内存会失败,并立即返回,如果不用该标志,CPU会等待GPU用完了才返回,如果锁定的内存没有被GPU占用,则会成功锁定。

一点理解仅供参考。

lock和unlock是为了CPU和GPU的同步设计的



//将光源位置转换成ARGB的辅助函数:
DWORD Vector2ARGB(D3DXVECTOR3 *v,float height)
{
DWORD r=(DWORD)(127.0f*v->x+128.0f);
DWORD g=(DWORD)(127.0f*v->y+128.0f);
DWORD b=(DWORD)(127.0f*v->z+128.0f);
DWORD a=(DWORD)(255.0f*height);
return((a<<24L)+(r<<16L)+(g<<8L)+b);
}
//生成法线图:
D3DXComputeNormalMap(pNormalMap,pHeightMap,NULL,0,D3D_CHANNEL_RED,1.0f);//pHeightMap为原高度图的指针,pNormalMap为一张空纹理,用于存放法线图
//在渲染程序段中实现:
{
DWORD F=Vector2ARGB(&light,0.0f); //light是单位化的光源向量
pD->SetRenderState(D3DRS_TEXTUREFACTOR,F);//pD是D3D的设备指针,这句将光源法线参数输入
pD->SetTexture(1,pTEXTURE);//设置原纹理,如上面的球,如有需要可以贴上纹理样式
pD->SetTexture(0,pNormalMap);//使用上面生成好的法线图
pD->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);//设置“来源1”为法线图
pD->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_DOTPRODUCT3);//将“来源1”(法线图)与“来源2”(光源法线)进行点乘
pD->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_TFACTOR);//设置“来源2”为光线的光源法线参数
pD->SetTextureStageState(1,D3DTSS_COLORARG1,D3DTA_TEXTURE);//这步和下面几步将图片的原纹理加上
pD->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_MODULATE);
pD->SetTextureStageState(1,D3DTSS_COLORARG2,D3DTA_CURRENT);
}


纹理颜色混合
这是我对纹理混合的理解,如果我的理解有误,请一定给我来信(gao_xudong2004@163.com),敬请赐教:
             D3D中,一个物体表面可以使用多个纹理,使用多个纹理时,某点最后的显示颜色可以由两个纹理的同一纹理坐标位置的两个颜色的混合值来决定,并且两纹理的操作结果可以作为下一次纹理颜色混合的来源颜色(我理解为,将操作结果保存在帧缓冲中,下一次操作可以调出这一次的操作结果作为颜色来源)
1、   纹理颜色值与纹理颜色值的混合操作
g_pMyd3dDevice->SetTexture( 0, g_pTextureWall0 );//将g_pTextureWall0 纹理与0号纹理阶段关联
g_pMyd3dDevice->SetSamplerState ( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pMyd3dDevice
->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );//指定第一来源色为当前纹理  将纹理层0的颜色作为第一来源色
g_pMyd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,D3DTOP_SELECTARG1 );//将第一来源色作为输出(我理解为将第一来源色送入帧缓冲中去) 我的理解是将这个颜色作为这一层纹理的输出颜色
g_pMyd3dDevice->SetTexture( 1, g_pTextureWall1 ););//将g_pTextureWall1纹理与stage1关联
g_pMyd3dDevice->SetSamplerState ( 1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
g_pMyd3dDevice
->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );//指定第一来源色为1号纹理阶段中纹理  将纹理层1的颜色作为第一来源色 ,至于第二来源色在下面,D3DTA_CURRENT表示将上一层的 输出颜色作为乘法第二来源色
g_pMyd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_MODULATE );//将第一来源色和第二来源色的乘积作为输出来更新帧缓冲
g_pMyd3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );//D3DTA_CURRENT表示将上一层的 输出作为乘法第二来源色
 
 
2、   纹理颜色值与指定颜色值的混合操作 
DWORD dwFactor=0x000000ff;
g_pd3dDevice
->SetRenderState( D3DRS_TEXTUREFACTOR, dwFactor );    //设置一个颜色值
g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TFACTOR );//将上面设置的颜色值作为第一来源色
g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_TEXTURE );//将0号纹理阶段的纹理颜色值作为第二来源色
g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_ADD);//将第一来源色与第二来源色的和作为输出。
第一来源色可以看作是一张所有像素颜色为dwFactor值的纹理图片。
3、累积乘法操作
g_pd3dDevice->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_MULTIPLYADD);// 
g_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
g_pd3dDevice
->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
D3DTOP_MULTIPLYADD操作的计算公式为:SRGBA = Arg1 + Arg2 * Arg3 。
其中Arg1是上一次纹理操作的结果(帧缓冲中的内容)Arg2  Arg3 是本层纹理里你设置的ColorArg1  ColorArg2,故以上代码实现的纹理颜色混合的操作结果为:SRGBA =上一次纹理操作的结果+本层纹理的颜色值*上一次纹理操作的结果 


下面是发线图的生成方法(根据高度图生成的)

//将光源位置转换成ARGB的辅助函数:
DWORD Vector2ARGB(D3DXVECTOR3 *v,float height)
{
DWORD r=(DWORD)(127.0f*v->x+128.0f);
DWORD g=(DWORD)(127.0f*v->y+128.0f);
DWORD b=(DWORD)(127.0f*v->z+128.0f);
DWORD a=(DWORD)(255.0f*height);
return((a<<24L)+(r<<16L)+(g<<8L)+b);
}
//生成法线图:
D3DXComputeNormalMap(pNormalMap,pHeightMap,NULL,0,D3D_CHANNEL_RED,1.0f);//pHeightMap为原高度图的指针,pNormalMap为一张空纹理,用于存放法线图
//在渲染程序段中实现:
{
DWORD F=Vector2ARGB(&light,0.0f); //light是单位化的光源向量
pD->SetRenderState(D3DRS_TEXTUREFACTOR,F);//pD是D3D的设备指针,这句将光源法线参数输入
pD->SetTexture(1,pTEXTURE);//设置原纹理,如上面的球,如有需要可以贴上纹理样式
pD->SetTexture(0,pNormalMap);//使用上面生成好的法线图
pD->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);//将纹理层0的纹理颜色作为第一个颜色来源
pD->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_DOTPRODUCT3);//将“来源1”(法线图)与“来源2”(光源法线)点乘
pD->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_TFACTOR);//将D3DRS_TEXTUREFACTOR作为第二个颜色来源


pD->SetTextureStageState(1,D3DTSS_COLORARG1,D3DTA_TEXTURE);//将纹理层1的纹理颜色作为第一个颜色来源
pD->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_MODULATE);
pD->SetTextureStageState(1,D3DTSS_COLORARG2,D3DTA_CURRENT);//将上一个纹理层的输出颜色作为第二个颜色来源
}


不要混淆了法线映射和凸凹映射这两种技术。虽然这两种技术都使用一种名为“凸凹贴图”或“法线贴图”的图像,但它们还是存有差别。差别在于图像的创建方式。凸凹映射技术使用的图像创建于灰度(从黑到白)2D纹理图像,有时候这个图像也称为“高度图”。

法线映射技术使用的图像创建于高分辨率模型(角色、物体等)中的细节,并将其保存到纹理中,这样在凸凹映射中使用时,低分辨率模型看上去就像是或是类似于高分辨率模型。这两个主题都已经超出了本书的讨论范围,但要注意的是法线映射是一种凸凹映射,它用于使低分辨率模型看上去像是高分辨率模型,而凸凹映射提取所有的2D图像,并从中创建法线。


而凹凸贴图本身就是一张2D的黑白图(又叫高度图),他并不会改变物体的表面,而只是影响光照的结果

最简单的做法是,直接把Bump Map叠加在已经渲染好的表面上,造成亮度上的扰动,从而让人以为是凹凸的,只是做简单的加减法

法线贴图里存储的是高分辨率下模型的法向量,而当我们想要渲染低分辨率下的模型的时候,用法线贴图里面的法向量来代替模型的法向量进行光照计算,这个是从根本上改变了物体的表面,所以法线贴图更真实



Stencil buffer,中文翻译为“模板缓冲区”,它是一个额外的buffer,通常附加
到z buffer 中,例如:15 位的z buffer 加上1 位的stencil buffer(总共2 个字节);
或者24 位的z buffer 加上8 位的stencil buffer(总共4 个字节)。每个像素对应

一个stencil buffer(其实就是对应一个Z buffer)。 Z buffer 和stencil buffer 通常在
显存中共享同一片区域。Stencil buffer 对大部分人而言应该比较陌生,这是一个
用来“做记号”的buffer,例如:在一个像素的stencil buffer 中存放1,表示该像素
对应的空间点处于阴影体(shadow volume)中。

Frame buffer,称为帧缓冲器,用于存放显示输出的数据,这个buffer 中的
数据一般是像素颜色值。Frame buffer 有时也被认为是color buffer(颜色缓冲器)
和z buffer 的组合(《实时计算机图形学(第二版)》12 页)。那么frame buffer 位
于什么地方呢?在webMediaBrands 网站上摘录了一段英文说明,即frame buffer
通常都在显卡上,但是有时显卡会集成到主板上,所以这种情况下frame buffer

被放在内存区域(general main memory)

一般情况下着色器分为两种:顶点着色器和像素着色器,有时候又会有个叫片段着色器的东东Fragment Shader

附:什么是片断?片断和像素有什么不一样?所谓片断就是所有的三维顶点
在光栅化之后的数据集合,这些数据还没有经过深度值比较,而屏幕显示的像素
都是经过深度比较的。

原创粉丝点击