Shader – asm
asm文档在MSDN上的地址:
http://msdn.microsoft.com/en-us/library/bb219840(v=vs.85).aspx
微软垄断是真的,但是文档好也是真的。
1、asm离不开GPU内部的architecture,看一下vs 1.1的,在汇编语言中,comment是用分号而不是C中的双斜杠。

所有数据在vs中都是128位的。
相对应的寄存器如下:
Input V0 ~ V15 共16个
Output O* 9个(11个 Radeon)
Constant C0 ~ C95 96个(ATI的有196个的)
Temporary r0 ~ r11 12个
Address a0.x
运用VShader的流程都遵循如下:
(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. Eg.
float c[4] = {0.0f,0.5f,1.0f,2.0f};
DWORD dwDecl0[] = {
D3DVSD_STREAM(0),
D3DVSD_REG(0, D3DVSDT_FLOAT3 ), // input register v0
D3DVSD_REG(5, D3DVSDT_D3DCOLOR ), // input Register v5
// set a few constants
D3DVSD_CONST(0,1),*(DWORD*)&c[0],*(DWORD*)&c[1],*(DWORD*)&c[2],*(DWORD*)&c[3],
D3DVSD_END()
};
D3DVSD_STREAM()告知了shader输入(input)的来源,在程序中,跟在后面的vertex buffer相关代码会把对应的vertex buffer绑定到该通道上,device->SetStreamSource(channel, vb, xx)这个D3D接口就是做绑定只用的,第一个参数指定流的索引,也就是D3DVSD_STREAM()的参数值。第二个是被绑定的vertex buffer。然后,启动整个shader程序后,shader程序的每个输入都是像流水线一样:一个顶点从vertex buffer中取出 / 作为参数送入vertex shader程序 / 跟vertex shader中的其他寄存器的数据做相应运算 / 输出一个顶点结果(进入VRAM的某个地方暂存)/ 从头开始。也因此,stream这个单词是非常精确的。
vertex shader对空间的顶点操作,不改变点的空间性质。因此输出的顶点能参与culling等动作,因为原先在空间中的各个面,现在还是那个面,只不过顶点的颜色变了。
注:在Fixed pipeline中,D3DVSD_REG()的第一个参数,其实是固定的,在d3d8types.h中,如
#define D3DVSDE_POSITION 0
也就是position是固定输入到Register 0中。
(3)Set the vertex shader constant registers with SetVertexShaderConstant().
上述的D3DVSD_CONST(0, 1)就是输入值到constant寄存器,第一个参数可以是0到95,后面的表示几个128位数,因为是4个dword(32位)的组合,所以只要写1。
CONSTANT寄存器也可以通过 SetVertexShaderConstant(DWORD Register,CONST void* pConstantData,
DWORD ConstantCount)来动态设置,如
m_pd3dDevice->SetVertexShaderConstant( 28, &matProjTranspose, 4 );
这个就把shader外部的代码变量输入到constant寄存器中,寄存器开始为c28,占4个寄存器,也就是c28\c29\c30\c31。
置入CONSTANT的一般为矩阵,材质颜色,
(4)Compile previously written vertex shader with D3DXAssembleShader*() (this could be precompiled using a shader assembler).
(5)Create a vertex shader handle with CreateVertexShader().
(6)Set a vertex shader with SetVertexShader() for a specific object.
(7)Delete a vertex shader with DeleteVertexShader().
2、Vertex Shader后的事情
VS输出后的每个顶点,我们是没有办法在API层级上得到的。这些顶点在GPU中,会根据他们组成的面进行face culling(去掉背向的),user plane clip(去掉给定的面后的),frustum clipping(去掉在投影体外的),Homogeneous devide(每个顶点是一个四元数,需要除以w后归一化),viewport mapping(根据用户设置的显示长宽把点mapping到实际长宽的面上)。到这里,每个三角形还是三角形,不过已经是我们在平面上看到的三角形了,而且三角形的坐标认为是这样(x/w, y/w, z/w, 1)。
接下来就是GPU的triangle setup阶段,这个阶段根据上面的三角形顶点信息,计算每个三角形的边,然后对内部的每条线进行插值运算出像素值,于此同时,texture的UV坐标也用同样的插值方式计算出来,作为后面pixel shader的输入。如果用户指定multiple sampler,那么在此处也一并进行(multipleSample是三角形级别的,不是line级别的)。
在支持Occlusion Culling的GPU中,triangle setup和pixel shader开始之间,有occlusion culling阶段。
然后,我们正式进入pixel shader了,因为要渲染的像素界别的原始资料都有了,包括每个像素的(x,y)连同z值,每个像素的颜色,每个像素的UV值。
3、ps 1.1的内部

相对应的寄存器如下:
Input v0 ~ v1 2个
Output r0 1个(是temporary共用的)
Constant c0 ~ c7 8个
Temporary r0 ~ r5 6个
TextureStage t0 ~ t5 6个
Constant寄存器是128位的,可以通过SetPixelShaderConstant()来设置shader外的值进来。
r0.rgba是最后的像素输出。
v0可以看做对应diffuse,v1对应specular。
使用pixel shader的流程遵循如下:
(1) Check for pixel shader support.
(2) Set texture operation flags (with D3DTSS_* flags).
D3DTSS_COLORARG1 及下面提到的几个TSS可以用pixel shader中的指令来做,其他的还要在外面设置。
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE);
m_pd3dDevice->SetTexture( 0, m_pWallTexture);
(3) Set texture (with SetTexture()).
HRESULT SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture);
第一个参数是stage 0 ~ 7, 后面一个参数是texture的指针;
(4) Define constants (with SetPixelShaderConstant()/def).
SetPixelShaderConstant(DWORD Register,CONST void* pConstantData,DWORD ConstantCount);
(5) Pixel shader instructions.
(6) Texture address instructions
(7) Arithmetic instructions
(8) Assemble pixel shader.
(9) Create pixel shader.
(10) Set pixel shader.
(11) Free pixel shader resources.
4、像素着色器后的GPU行为
(1) Fog,根据z值(w值或其他能知道视点离像素的距离的值)和用户设置的雾效因子(fog factor),雾的颜色和每个像素会进行混合(blending)。注意,fog也可以是base在vertex上的。
(2) Alpha Test, 进入的alpha值会和参考的alpha值进行比较,比较的方式用D3DRS_ALPHAFUNC指定,如果通过比较,则像素进入下一步,否则这个像素被抛弃。
(3) Stencil Test,
(4) Depth Test, 分为z-buffer和w-buffer。每个像素根据深度值(左手坐标,由视点开始往远处是0 – 无限),判断自己是否在原来的像素的前面。是则覆盖,不是就抛弃。
(5) Alpha Blending, 这个过程把目前的像素跟已经在背景上的像素进行混合。
(6) Dithering,这是一种障眼法,可也通过D3DRS_DITHERENABLE来开启。主要用在色彩不够丰富的场合(8bit,4bit)。
(7) Channel Masking,
(8) Render,这个是写到backbuffer或者是texture里,输入就是上面的shader的结果。
相关日志
If you enjoyed this post, please consider to leave a comment or subscribe to the feed and get future articles delivered to your feed reader.

Terrific work! This is the type of information that should be shared around the web. Shame on the search engines for not positioning this post higher!