像素着色器实际上就是对每一个像素进行光栅化的处理期间,在GPU上运算的一段程序。
不同与顶点着色器,像素着色器不会以软件的形式来模拟像素着色器。
像素着色器实质上是取代了固定功能流水线中多重纹理的环节,而且赋予了我们访问单个像素以及访问每一个像素纹理坐标的能力
多重纹理的概述
多重纹理就是我们同时启用多层纹理,然后规定他们的融合方式,以得到特殊效果。
多重纹理最典型的应用就是光照运算,在顶点运算阶段,我们不打算使用Direct3D的光照模型,而是使用光照纹理图,
他规定了某一面是如何被照亮的。
例如:我们想在板条箱的上面投射聚光灯,我们可用结构D3DLIGHT9来定义该聚光灯,也可以将代表光照的纹理和板条箱的纹理进行融合
通过多重纹理进行融合比比Direct3D光照计算模型的优点:
1、光照已经预先计算好,并保存在聚光灯的纹理图中,这样程序在运行的时候,就不需要在对光照进行计算了,节省了时间
2、由于光照纹理图是预先计算好的,我们可以采用比Direct3D更精确的光照模型,从而得到更逼真的效果
多重纹理环节一般适用于为静态的物体实现一个完整的光照引擎。假如我们现在手上有多张已经计算好的纹理图,
比如:物体本身的纹理图,漫反射光照的纹理图,还有就是镜面光照的纹理图,当我们需要这些纹理的合成的时候,
只需对这些预先计算好的纹理图进行查询即可高效的为场景增加光照和增加细节。
多重纹理中有一点就是多重纹理坐标,用最简单的三角形来看的话,简单的绘制三角形,我们只需要一层纹理,此时只需要
一个纹理坐标,当我们需要三层纹理融合的时候,按照这种思路的话,每一个顶点里面就需要三对儿纹理坐标,
每一个纹理坐标都对应一层纹理的中的一个顶点。
固定的流水线至多支持8层纹理坐标,再多的话就只能使用顶点声明了。
但是在新的像素着色器中,一个纹理坐标可以对应多个索引来引用纹理,这样就无需多个纹理坐标了,当然这是建立在每一个纹理层
都使用的相同的纹理坐标,如果每层纹理中的纹理坐标有差异,还是需要多个纹理坐标。
像素着色器的输入和输出
像素着色器的输入包括每个像素的颜色以及纹理坐标。
每一个像素的纹理坐标其实就是指定了纹理中将被映射到当前像素的纹理元的坐标。
在进入像素着色器之前。Direct3D先根据顶点的颜色以及顶点的纹理坐标计算出每一个像素的颜色以及纹理坐标。
输入像素着色器的颜色和纹理坐标的个数 是由 顶点着色器输出的颜色和纹理坐标的个数决定。
如果一个顶点着色器输出两种颜色和3个纹理坐标,则Direct3D计算出每一个像素的两种颜色以及3个纹理坐标对,
然后将结果输入到像素着色器中。
此时我们需要代码来将刚才的结果作为像素着色器的输入:
struct PS_INPUT
{
vector c0:COLORO;
vector c1:COLOR1;
float2 t0:TEXCOORDO;
float2 t1:TEXCOORD1;
float2 t2:TEXCOORD2;
};
就输出而言,像素着色器将会输出每一个像素的单个值(不太明白,应该是两种颜色融合之后的值吧)
struct PS_OUTPUT
{
vector finalPixelColor:COLORO;
}
使用像素着色器的步骤
1、编写像素着色器程序,然后进行编译
2、创建一个IDirect3DPixerShader9的接口对象,表示经过编译之后的像素着色器
3、使用SetPixerShader方法来启用像素着色器
Tip:像素着色器使用完成之后必须进行销毁
像素着色器的编写和顶点着色器的编写完全相同,具体可以参见顶点着色器程序的编写。
代码编写完成之后,我们可以使用D3DXCompileShaderFromFile对着色器进行编译,编译的版本应该是:ps_2_0,应该
区别于顶点着色器的VS_2_0。
像素着色器的创建
着色器代码经过了编译,我们就可借助下述方法获取指向IDirect3DPixelShader9接口的指针,
该接口代表了一个像素着色器。
HRESULT CreatePixelshader(
CONST DWORD*pEunction,IDirect3DPixelShader9**ppShader
);
·pFunction指向经过编译的着色器代码的指针。
·ppShader 返回一个指向IDirect3DPixelShader9接口的指针。
例如,假定变量shader是1D3DXBuffer接口的对象,它包含了经过编译的着色器代码。
为了获取IDirect3DPixelShader9接口的指针,我们可以这样做:
IDirect3DPixelShader9* MultiTexPS=0;
hr=Device->CreatePixelShader(
(DWORD*)Shader->GetBufferPointer(),
&MultiTexPS);
像素着色器的设置
当获取了代表像素着色器的接口IDirect3DPixelShader9的指针后,我们可用下述方法将其启用:
HRESULT SetPixelShader(
IDirect3DPixelShader9*pShader
);
该方法只接收一个参数,我们可将指向希望启用的像素着色器的指针传给该参数。
为了启用我们刚才创建的着色器,可以这样:
Device->SetPixelShader(MultiTexpS);
像素着色器的销毁
像Direct3D的所有其他接口一样,接口IDirect3DPixelShader9在使用完毕之后也必须
调用其自身的Release方法来释放它所占用的资源。
d3d::Release<IDirect3DPixelShader9*>(MultiTexPS);
HLSL采样对象
像素着色器中对纹理进行采样,HLSL里面有内置函数。
Tip:采样就是根据像素的能纹理坐标和采样状态来检索像素所对应的纹理元
纹理坐标是作为像素着色器的输入。而我们检索的特定的纹理在像素着色器中用一个HLSL对象——采样器(sampler)来标识
例如:我们现在使用3层纹理,在像素着色器中我们这么写:
sampler FirstTex;
sampler SecondTex;
sampler ThirdTex;
Direct3D将每一个sampler的对象唯一的与某一层纹理关联起来,在应用程序中我们找出sampler对象所对应的纹理层,
然后为该纹理层设置合适纹理以及采样器的状态。
下面代码就做了这些:
//创建纹理
IDirect3DTexture9* Tex;
D3DXCreateTextureFromFile(Device,"tex.bmp",&Tex);
//获取常量的句柄
FirstTexHandle =MultiTexCT->GetConstantByName(0,"FirstTex");
//获取常量的描述
D3DXCONSTANT_DESC FirstTexDesc;
UINT count;
MultiTexCT->GetConstantDesc(FirstTexHandle,&FirstTexDesc,& count);
//为采样器FirstTex设置纹理/采样器状态
//RegisterIndex是D3DXCONSTANT_DESC的成员
Device->SetTexture(FirstTexDesc. RegisterIndex, Tex);
Device->SetSamplerState(FirstTexDesc. RegisterIndex, D3DSAMP_MAGEILTER,D3DTEXF_LINEAR);
Device->SetSamplerState(FirstTexDesc. RegisterIndex, D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
Device->SetSamplerState(FirstTexDesc. RegisterIndex, D3DSAMP_MIPFILTER,D3DTEXF_LINEAR);
小结:
像素着色器取代了固定流水线中的多重纹理功能,使我们能够对像素进行单个的访问,并且可以操纵像素的纹理坐标,使我们更具有灵活性。
多重纹理是让好几层纹理进行融合,然后产生效果,我们经常使用多重纹理对静态物体进行完整光照计算引擎
HLSL里面的sampler对象标识了一个特定的纹理层。