vertex shader(4)
Swizzling and Masking
如果你使用输入、常量、临时寄存器作为源寄存器,你可以彼此独立地swizzle .x,.y,.z,.w值。如果你使用输出、临时寄存器作为目标寄存器,你可以把.x,.y,.z,.w值用作
write masks。 下面是一些细节:
Swizzling (only source registers : vn,cn,rn)
例如:
mov R1, -R2.wxyz
目标寄存器为R1,R2为源寄存器(在指令语法中,源寄存器在目标寄存器的右边)。执行该mov指令后,会将R2中-w,-x,-y,-z的值分别赋给R1的x,y,z,w。
Masking (only destination registers : on,rn)
例如:
mov R1.xw, R2
使用R1为目标寄存器,R2位源寄存器。执行该mov指令后,会仅仅将R2中的x,w的值赋给R1。在目标寄存器中不支持swizzling和负数。
【对于编写顶点着色器的指导】
你需要记住这些:
1。有128条指令的限制。
2。每条指令的源寄存器不能超过一个常量寄存器(比如:add r0, c4, c3是错误的)
3。每条指令的源寄存器不能超过一个输入寄存器(比如:add r0, v1, v2是错误的)
4。没有类C的条件语句,但你可以用sge指令模仿r0 = (r1>=r2) ? r3 : r4
5。所有在顶点着色器中的值被固定在[0,1]
6。Every vertex shader must write at least to one component of oPos, or you will get an error message by the assembler.
有一些优化顶点着色器的方法:
1。当设置顶点着色器的常量数据时,尝试在一个SetVertexShaderConstant()的调用中设置所有数据。
2。使用一个mov指令之前停下来想想,或许你可以避免使用它。
3。选择多个指令运算,而不是单个指令运算。比如:
mad r4,r3,c9,r4
mov oD0,r4
==
mad oD0,r3,c9,r4
4。考虑优化之前,先移除像m4x4或者m3x3这样复杂的指令。
5。在着色器中的许多计算都可以被pulled outside并且用每个物体的形式表示,而不是每个顶点,而且可以把它们放入常量寄存器。如果你在做一些基于物体而不是基于顶点的计算,在CPU里进行,并把它作为常量上载到顶点着色器。
【编译顶点着色器】
Direct3D使用字节码(bytecodes),而OpenGL解析字符串。因此,Direct3D的开发者需要使用Assembler来assemble顶点着色器的资源。这可以帮助你更容易找到bug,也缩短了载入时间。
有三种不同的方法编译顶点着色器:
1。将顶点着色器资源写入一个独立的ASCII文件(比如 test.vsh),然后用顶点着色器Assembler将它编译进一个二进制文件(比如test.vso)。这个二进制文件将在游戏启动时被打开和读取。
使用这个方法,不是每个人都可以看到并且修改你的顶点着色器资源。
2。将顶点着色器资源写入一个独立的ASCII文件或者作为一个字符串写入你的*.cpp文件,当应用程序启动时,用D3DXAssemblerShader*()函数编译它。
3。将顶点着色器资源写入一个效果文件,当应用程序启动时打开这个效果文件。可以通过调用D3DXCreateEffectFromFile()读取效果文件来编译顶点着色器。
注:另一个方法是使用d3dtypes.h中展示的操作码构建你自己的顶点assembler/disassembler。
让我们回顾一下我们至今讲解过的知识,首先,我们通过D3DCAPS8::VertexShaderVersion检查设备是否支持顶点着色器,然后我们用D3DVSD_*宏声明一个顶点着色器。然后
我们用SetVertexShaderConstant()设置常量寄存器,然后写、编译顶点着色器。
现在,我们需要一个句柄去call顶点着色器。
【创建顶点着色器】
CreateVertexShader()函数被用来创建并且使一个顶点着色器生效。
HRESULT CreateVertexShader(
CONST DWORD* pDeclaration,
CONST DWORD* pFunction,
DWORD* pHandle,
DWORD Usage);
pDeclaration :顶点着色器声明的指针(它将顶点缓冲区映射到不同的顶点输入寄存器)。
pFunction : 通过D3DXAssembleShader() / D3DXAssembleShaderFromFile()使得顶点着色器指令被编译。
pHandle : 返回的顶点着色器的句柄。
Usage : 你可以使用D3DUSAGE_ SOFTWAREPROCESSING来强制使用软件进行顶点处理(当D3DRS_SOFTWAREVERTEXPROCESSING被设置为true时,它必须被使用)。但是使用GPU硬件处理速度更快。
【设置顶点着色器】
在DrawPrimitive*()调用之前,需要调用SetVetexShader()。这个函数动态地载入顶点着色器,用来设置使用哪个顶点着色器。你必须提供由CreateVertexShader()创建的顶点着色器句柄。
m_pd3dDevice->SetVertexShader( m_dwVertexShader );
顶点着色器可以被SetVertexShader()执行很多次,这个次数和顶点数一样多。
【释放顶点着色器资源】
当游戏终止或者设备(device)被改变,被顶点着色器使用的资源必须被释放掉,可以通过DeleteVertexShader()来执行。
if(m_pd3dDevice->m_dwVertexShader != 0xffffffff)
{
m_pd3dDevice->DeleteVertexShader(m_dwVertexShader);
m_pd3dDevice->m_dwVertexShader = 0xffffffff;
}
编写一个顶点着色器
1. Check for vertex shader support by checking the D3DCAPS8::VertexShaderVersion field.
2. Declare the vertex shader with the D3DVSD_* macros to map vertex buffer streams to
input registers.
3. Set the vertex shader constant registers with SetVertexShaderConstant().
Compile an already written vertex shader with D3DXAssembleShader*() (alternatively : could be precompiled using a shader assembler).
4. Create a vertex shader handle with CreateVertexShader().
5. Set a vertex shader with SetVertexShader() for a specific object.
6. Free vertex shader resources handled by the Direct3D engine with DeleteVertexShader().
【检查设备支持】
如果使用软件实现,则不需要检查设备支持。
这里跳过该步骤,因为directX 9以上就不需要检查设备支持了。
【声明顶点着色器】
声明顶点着色器意味着将顶点数据映射到特定的输入寄存器。因此,顶点着色器声明必须反映顶点缓冲区布局。
着色器声明:
DWORD dwDecl[] =
{
D3DVSD_STREAM(),
D3DVSD_REG(,D3DVSDT_FLOAT3), // D3DVSDE_POSITION,0
D3DVSD_END()
};
顶点缓冲区布局:
struct VERTEX
{
FLOAT x,y,z;
};
顶点格式声明:
#define D3DFVF_VERTEX (D3DFVF_XYZ)
位置的值将被存储进顶点缓冲区,并且会通过函数SetStreamSource()绑定到设备数据流端口。(可能是Higher-Order Surfaces(HOS)阶段,也可能是顶点着色器,取决于usage of HOS,可参考page 6中图片1中的Direct3D 管线)
【设置常量寄存器】
常量寄存器必须通过调用SetVertexShaderConstant()来进行填充。
下面的例子将材质颜色设置进常量寄存器c8.
FLOAT fMaterial[]={,,,};
m_pd3dDevice->SetVertexShaderConstant(,fMaterial,);
SetVertexShaderConstant的声明如下:
HRESULT SetVertexShaderConstant (DWORD Register,
CONST void* pConstantData,
DWORD ConstantCount);
第一个参数指要被使用的常量寄存器的数字编号。
第二个参数将128-bits的数据存储进该常量寄存器。
第三个参数是所要存储的128-bits数据的数量。比如,要将一个4x4矩阵存储进常量寄存器,
可以将第三个参数置为4:m_pd3dDevice->SetVertexShaderConstant(4, matTemp, 4); 如果这样的话,c4,c5,c6,c7都被用来存储这个矩阵。
【顶点着色器】
下面的例子中,顶点着色器被存储进一个字符数组中。
// reg c4-7 = WorldViewProj matrix
// reg c8 = constant color
// reg v0 = input register const char BasicVertexShader[] =
"vs.1.1 " \
"dp4 oPos.x, v0, c4 " \
"dp4 oPos.y, v0, c5 " \
"dp4 oPos.z, v0, c6 " \
"dp4 oPos.w, v0, c7 " \
"mov oD0, c8 ";
It is used inline in a constant char array. 这个顶点着色器遵循vs.1.1版本的实现规则。它用4个dp4指令将“整合在一起并且进行过转置”的World,View,Projection 矩阵转换到clip matrix(或者clip sapce),并且用mov指令将c8赋给一个oD0材质颜色。
D3DXMatrixRotationY( &m_matWorld, m_fTime * 1.5f );
D3DXMATRIX matTemp; // set the clip matrix
D3DXMatrixTranspose( &matTemp , &(m_matWorld * m_matView * m_matProj) );
m_pd3dDevice->SetVertexShaderConstant(, matTemp, );
将物体绕Y轴旋转,需要调用D3DMatrixRotationY()。
它的实现就像这样:fRads代表你要旋转的角度。
VOID D3DMatrixRotationY(D3DMATRIX * mat, FLOAT fRads)
{
D3DXMatrixIdentity(mat);
mat._11 = cosf(fRads);
mat._13 = -sinf(fRads);
mat._31 = sinf(fRads);
mat._33 = cosf(fRads);
}=
cosf(fRads) -sinf(fRads) sinf(fRads) cosf(fRads)
旋转之后,我们需要用D3DXMatrixTranspose()将矩阵转置。为什么必须将它转置呢?理由如下:一个4x4矩阵:
a b c d
e f g h
i j k l
m n o p
将一个向量vector通过该矩阵转换的公式为:将向量与矩阵相乘
dest.x = (v0.x * a) + (v0.y * e) + (v0.z * i) + (v0.w * m)
dest.y = (v0.x * b) + (v0.y * f) + (v0.z * j) + (v0.w * n)
dest.z = (v0.x * c) + (v0.y * g) + (v0.z * k) + (v0.w * o)
dest.w = (v0.x * d) + (v0.y * h) + (v0.z * l) + (v0.w * p)
但在着色器中,我们使用4个dp4的话:(在寄存器中,我们存储矩阵是一行一行地存储)
dest.x = (v0.x * a) + (v0.y * b) + (v0.z * c) + (v0.w * d)
dest.y = (v0.x * e) + (v0.y * f) + (v0.z * g) + (v0.w * h)
dest.z = (v0.x * i) + (v0.y * j) + (v0.z * k) + (v0.w * l)
dest.w = (v0.x * m) + (v0.y * n) + (v0.z * o) + (v0.w * p)
所以如果不进行转置是错误的。我们必须将矩阵进行转置为:
a e i m // c4
b f j n // c5
c g k o // c6
d h l p // c7
这样的话,dp4就会正确运算
dest.x = (v0.x * a) + (v0.y * e) + (v0.z * i) + (v0.w * m)
dest.y = (v0.x * b) + (v0.y * f) + (v0.z * j) + (v0.w * n)
dest.z = (v0.x * c) + (v0.y * g) + (v0.z * k) + (v0.w * o)
dest.w = (v0.x * d) + (v0.y * h) + (v0.z * l) + (v0.w * p)
或者
oPos.x = (v0.x * c4.x) + (v0.y * c4.y) + (v0.z * c4.z) + (v0.w * c4.w)
oPos.y = (v0.x * c5.x) + (v0.y * c5.y) + (v0.z * c5.z) + (v0.w * c5.w)
oPos.z = (v0.x * c6.x) + (v0.y * c6.y) + (v0.z * c6.z) + (v0.w * c6.w)
oPos.w = (v0.x * c7.x) + (v0.y * c7.y) + (v0.z * c7.z) + (v0.w * c7.w)
【编译顶点着色器】
被存储进字符数组的顶点着色器可以用下面的代码片段进行编译:
rc = D3DXAssembleShader( BasicVertexShader , sizeof(BasicVertexShader) -,
, NULL , &pVS , &pErrors );
if ( FAILED(rc) )
{
OutputDebugString( "Failed to assemble the vertex shader, errors:\n" );
OutputDebugString( (char*)pErrors->GetBufferPointer() );
OutputDebugString( "\n" );
}
D3DXAssembleShader()通过接口ID3DXBuffer在一个缓冲器对象(buffer object)中创建一个二进制版本的着色器。
D3DXAssembleShader()的声明:
HRESULT D3DXAssembleShader(
LPCVOID pSrcData,
UINT SrcDataLen,
DWORD Flags,
LPD3DXBUFFER* ppConstants,
LPD3DXBUFFER* ppCompiledShader,
LPD3DXBUFFER* ppCompilationErrors
);
第一个参数表示资源数据。
第二个参数表示数据的总大小(单位为字节byte)。
第三个参数有两个可选:
#define D3DXASM_DEBUG 1
#define D3DXASM_SKIPVALIDATION 2
第一个将调试信息作为注释插入着色器,第二个跳过合理检测。(The first one inserts debug info as comments into the shader and the second one skips validation. )
第四个参数,ID3DXBuffer接口可以得到顶点着色器的常量声明片段。为了忽略这个参数,这里将它设为NULL。
第五个参数为被编译的着色器。
第六个参数为存储在ID3DXBuffer接口的缓冲器对象的错误说明信息。
【创建顶点着色器】
代码如下:
rc = m_pd3dDevice->CreateVertexShader( dwDecl, (DWORD*)pVS->GetBufferPointer(),
&m_dwVertexShader, );
if ( FAILED(rc) )
{
OutputDebugString( "Failed to create the vertex shader, errors:\n" );
D3DXGetErrorStringA(rc,szBuffer,sizeof(szBuffer));
OutputDebugString( szBuffer );
OutputDebugString( "\n" );
}
这个函数通过dwDecl得到顶点着色器声明,通过ID3DXBuffer接口得到二进制版本的着色器的指针。如果有错误发生,就会通过pVS->GetBufferPointer()得到错误信息。
D3DXGetErrorStringA()解释所有Direct3D和Direct3DX的HRESULTS并且返回一个szBuffer,里面存储着错误信息。
【设置顶点着色器】
m_pd3dDevice->SetVertexShader( m_dwVertexShader );
唯一需要提供的参数是顶点着色器的句柄。
这个函数执行的次数和顶点的次数一样。
【释放顶点着色器资源】
if ( m_dwVertexShader != 0xffffffff )
{
m_pd3dDevice->DeleteVertexShader( m_dwVertexShader );
m_dwVertexShader = 0xffffffff;
}
如果窗口大小或者设备改变,它一定会被调用。
vertex shader(4)的更多相关文章
- vertex shader(3)
之前我们学习了如何声明顶点着色器.如何设置常量寄存器中的常量.接下来我们学习如何写和编译一个顶点着色器程序. 在我们编译一个顶点着色器之前,首先需要写一个. 有17种不同的指令(instruction ...
- vertex shader(2)
一次只有一个vertex shader是活跃的.你可以有多个vertex shader,如果一个物体特殊的变换或者灯光,你可以选择合适的vertex shader来完成这个任务. 你可能想使用vert ...
- vertex shader(1)
Vertex shader Architecture: 所有在vertex shader中的数据都用128-bit的quad-floats表示(4x32-bit). vertex shader线性地执 ...
- 【Unity Shader实战】卡通风格的Shader(二)
写在前面 本系列其他文章: 卡通风格的Shader(一) 好久没写博客了,一定是因为课程作业比较多,一定不是因为我懒,恩恩. 三个月以前,在一篇讲卡通风格的Shader的最后,我们说到在Surface ...
- 【Unity Shader实战】卡通风格的Shader(一)
写在前面 本系列其他文章: 卡通风格的Shader(二) 呜,其实很早就看到了这类Shader,实现方法很多,效果也有些许不一样.从这篇开始,陆续学习一下接触到的卡通类型Shader的编写. 本篇的最 ...
- 学习笔记:GLSL Core Tutorial – Vertex Shader(内置变量说明)
1.每个Vertex Shader都有用户定义的输入属性,例如:位置,法线向量和纹理坐标等.Vertex Shaders也接收一致变量(uniform variables). uniform vari ...
- 基于Cocos2d-x学习OpenGL ES 2.0系列——编写自己的shader(2)
在上篇文章中,我给大家介绍了如何在Cocos2d-x里面绘制一个三角形,当时我们使用的是Cocos2d-x引擎自带的shader和一些辅助函数.在本文中,我将演示一下如何编写自己的shader,同时, ...
- Unity Shader (三)Surface Shader机制
转自:http://blog.csdn.net/candycat1992/article/details/39994049 写在前面 一直以来,Unity Surface Shader背后的机制一直是 ...
- Unity Shader (一)ShaderLab 语法
一.什么是Shader Shader(着色器):是可以在GPU上运行的一段程序,通过Shader可以进行一些渲染相关的设置. 二.什么是ShaderLab 目前面向GPU的编程有三种高级图像语言:HL ...
随机推荐
- LOJ2613 NOIP2013 华容道 【最短路】*
LOJ2613 NOIP2013 华容道 LINK 这是个好题,具体题意比较麻烦可以直接看LINK中的链接 然后考虑我们可能的移动方式 首先我们需要把白块移动到需要移动块S的附近(附近四格) 然后我们 ...
- Windows7 下python3和python2同时 安装python3和python2
1.下载python3和python2 进入python官网,链接https://www.python.org/ 选择Downloads--->Windows,点击进入就可以看到寻找想要的pyt ...
- Tornado的入门研究
1.为啥要了解Tornado 首先,Tornado是大神写出来的,如果学习python的话,参照Tornado的源码是一件非常好的事情,属于FaceBook的开源代码 其次,Tornado就是我们在 ...
- 在后台new出页面(组件)
Page p = new Page(); Control u = p.LoadControl("~/folderName/controlName.ascx") ...
- torcs代码
/** Info returned by driver during the race */ typedef struct { tdble steer; /**< Steer command [ ...
- SharePoint无法搜索解决
重启服务器后,站点搜索时提示错误,无法进行搜索,进入管理中心搜索管理看到,"查询处理"出错. 解决方法: 停止搜索服务,重新启动,如下图所示 重启服务后,过了几分钟重新查询,查询正 ...
- 如何用TortoiseSVN管理本地文档
1.安装(略) 2.搭建本地SVN版本管理数据库(服务器) (1)在本地磁盘上新建一个目录,例如E:\SVN,用来存储各种需要进行版本管理的文档:接着在该目录下再创建一个新的空目录,例如创建一个E:\ ...
- openresty+lua做接口调用权限限制
说明:openresty可以理解为一个服务器它将nginx的核心包含了过来,并结合lua脚本语言实现一些对性能要求高的功能,该篇文章介绍了使用openresty 1.purview.lua --调用j ...
- Apache DBUtils使用总结 【转】
Apache DBUtils使用总结 DBUtils是个小巧的JDBC轻量级封装的工具包,其最核心的特性是结果集的封装,可以直接将查询出来的结果集封装成JavaBean,这就为我们做了最枯燥乏味. ...
- 小小的学习FPGA建议
学习FPGA,一点小小的 建议或者总结分享. 语法层面搞懂阻塞和非阻塞语句,以及Verilog语言的时序描述方法,把自己想象成编译器,尝试去编译自己写的Module,不断总结自己设计的逻辑会综合出怎么 ...