8.1 颜色,发光,凹凸和反射纹理贴图
颜色贴图

我们想在一个模型上贴上一张纹理,这个例子中是将一张地球纹理贴在一个球上。你要做的就是将颜色贴图作为模型的漫反射颜色,所以我们获取漫反射值并将它乘以纹理颜色获取最终的颜色。我们需要World * View * Projection矩阵和World矩阵,这里我仍然包含了环境光,但没有用它,只使用了默认值,因为我们要用到漫反射光,所以需要知道光线方向。下面有两个新东西:texture和sampler。其中texture是传递给shader的纹理的参数,这个例子中是ColorMap。Sampler用在pixel shader中获取纹理的像素。
float4x4 wvp : WorldViewProjection;
float4x4 world : World;
float AmbientIntensity = 1;
float4 AmbientColor : AMBIENT = float4(0,0,0,1);
float3 LightDirection : Direction = float3(0,1,1);
texture ColorMap : Diffuse;
sampler ColorMapSampler = sampler_state
{
texture = <ColorMap>;
};
VS_IN中添加了一个新成员:TexCoord,你的模型应该包含这个信息,否则无法正确加上纹理。TexCoord的数据类型是float2 (Vector2),每个分量的变化范围是0到1,表示纹理坐标。如果分量的值是0.5,0.5表示处于纹理的中央。这两个量也叫做U和V,类似于X和Y,但在UV映射中称为U和V。输出的VS_OUT结构包含Position,Light和Normal,还增加了一个TexCoord。
struct VS_IN
{
float4 Position : POSITION;
float2 TexCoord : TEXCOORD0;
float3 Normal : NORMAL;
};
struct VS_OUT
{
float4 Position : POSITION;
float2 TexCoord : TEXCOORD0;
float3 Light : TEXCOORD1;
float3 Normal : TEXCOORD2;
};
struct PS_OUT
{
float4 Color : COLOR;
};
在vertex shader 中将TexCoord传递到pixel shader。
VS_OUT VS_ColorMap(VS_IN input)
{
VS_OUT output = (VS_OUT)0;
output.Position = mul(input.Position,wvp);
output.Light = LightDirection;
output.TexCoord = input.TexCoord;
output.Normal = mul(input.Normal,world);
return output;
}
在pixel shader中我们计算了光线方向、漫反射和环境光颜色,但我们从当前像素的采样器中获取当前像素的纹理坐标,并乘以刚才计算的漫反射值调整像素的颜色,接着加上环境光颜色获得像素的最终颜色。
PS_OUT PS_ColorMap(VS_OUT input)
{
PS_OUT output = (PS_OUT)0;
float3 LightDir = normalize(input.Light);
float Diffuse = saturate(dot(LightDir,normalize(input.Normal)));
float4 texCol = tex2D(ColorMapSampler,input.TexCoord);
float4 Ambient = AmbientIntensity * AmbientColor;
texCol *= Diffuse;
output.Color = Ambient + texCol;
return output;
}

发光贴图(Glow Map)

我们可以在shader中添加另一个纹理和采样器来使用发光贴图。
texture GlowMap : Diffuse;
sampler GlowMapSampler = sampler_state
{
texture = <GlowMap>;
};
Shader结构并没有变化,我们只是在pixel shader 中加入了发光贴图的计算。发光贴图将贴图中的像素颜色乘以(1-Diffuse),pixel shader代码如下:
PS_OUT PS_ColorGlowMap(VS_OUT input)
{
PS_OUT output = (PS_OUT)0;
float3 LightDir = normalize(input.Light);
float Diffuse = saturate(dot(LightDir,normalize(input.Normal)));
float4 texCol = tex2D(ColorMapSampler,input.TexCoord);
float4 glowCol = tex2D(GlowMapSampler,input.TexCoord);
float4 Ambient = AmbientIntensity * AmbientColor;
float4 glow = glowCol * saturate(1-Diffuse);
texCol *= Diffuse;
output.Color = Ambient + texCol + glow;
return output;
}
程序截图如下:

凹凸贴图(Bump Map)

要让凹凸贴图工作正常你必须先对shader和模型做件事。首先要求模型有“切线”数据,切线用来产生正确的“切线空间”,Wolfgang比我解释得清楚得多,建议你去买一本他的书。要让素材管道知道我们想让模型使用切线,只需选择模型,在属性面板中展开Content Processor并将Generate Tangent Frame 这个选项设置为True,默认是false。

现在当模型被编译后(如果Vertex Channel不正确你会得到一个编译错误,仍得不到切线数据)就会获取切线数据。我们为凹凸贴图添加一个纹理:
texture BumpMap ;
sampler BumpMapSampler = sampler_state
{
Texture = <BumpMap>;
};
结构也要改变:
struct VS_IN
{
float4 Position : POSITION;
float2 TexCoord : TEXCOORD0;
float3 Normal : NORMAL;
float3 Tangent : TANGENT;
};
struct VS_OUT
{
float4 Position : POSITION;
float2 TexCoord : TEXCOORD0;
float3 Light : TEXCOORD1;
};
我们需要将切线数据添加到VS_IN结构中,这样我们可以使用新生成的切线数据,在VS_OUT中移除Normal,因为我们将在vertex shader中使用世界切线空间转换光线,使用凹凸(法线)贴图获取法线。
vertex shader如下:
VS_OUT VS_ColorGlowBump(VS_IN input)
{
VS_OUT output = (VS_OUT)0;
output.Position = mul(input.Position,wvp);
float3x3 worldToTangentSpace;
worldToTangentSpace[0] = mul(input.Tangent,world);
worldToTangentSpace[1] = mul(cross(input.Tangent,input.Normal),world);
worldToTangentSpace[2] = mul(input.Normal,world);
output.Light = mul(worldToTangentSpace,LightDirection);
output.TexCoord = input.TexCoord;
return output;
}
如你所见,我们用前面相同的方式转换了位置,接着计算切线空间矩阵,并用这个矩阵转换光线方向。
PS_OUT PS_ColorGlowBump(VS_OUT input)
{
PS_OUT output = (PS_OUT)0;
float3 Normal = (2 * (tex2D(BumpMapSampler,input.TexCoord))) - 1.0;
float3 LightDir = normalize(input.Light);
float Diffuse = saturate(dot(LightDir,Normal));
float4 texCol = tex2D(ColorMapSampler,input.TexCoord);
float4 glowCol = tex2D(GlowMapSampler,input.TexCoord);
float4 Ambient = AmbientIntensity * AmbientColor;
float4 glow = glowCol * saturate(1-Diffuse);
texCol *= Diffuse;
output.Color = Ambient + texCol + glow;
return output;
}
在pixel shader中我们从凹凸贴图中获得法线,归一化光线方向,然后用前面相同的方式计算漫反射,只不过这次是使用从切线空间产生的数据。

反射贴图(Reflective Map)

这个贴图与颜色和发光贴图一样简单,我们通过一个反射贴图实现一个镜面高光,再次添加一个纹理和采样器管理反射贴图:
texture ReflectionMap : Diffuse;
sampler ReflectionMapSampler = sampler_state
{
texture = <ReflectionMap>;
};
VS_IN结构保持不变而VS_OUT需要为镜面反射做出改变:
struct VS_OUT
{
float4 Position : POSITION;
float2 TexCoord : TEXCOORD0;
float3 Light : TEXCOORD1;
float3 CamView : TEXCOORD2;
float4 posS : TEXCOORD3;
float3 Normal : TEXCOORD4;
};
接着在vertex shader 中添加镜面反光代码
PS_OUT PS_Ambient(VS_OUT input)
{
PS_OUT output = (PS_OUT)0;
float3 Normal = (2 * (tex2D(BumpMapSampler,input.TexCoord))) - 1.0;
float3 LightDir = normalize(input.Light);
float Diffuse = saturate(dot(LightDir,Normal));
float4 texCol = tex2D(ColorMapSampler,input.TexCoord);
float4 glowCol = tex2D(GlowMapSampler,input.TexCoord);
float4 Ambient = AmbientIntensity * AmbientColor;
float4 glow = glowCol * saturate(1-Diffuse);
texCol *= Diffuse;
float3 Half = normalize(normalize(LightDirection) + normalize(input.CamView));
float specular = pow(saturate(dot(normalize(input.Normal),Half)),25);
float4 specCol = 2 * tex2D(ReflectionMapSampler,input.TexCoord) * (specular * Diffuse);
output.Color = Ambient + texCol + glow + specCol;
return output;
}
截图如下:

另外,如果你看一下图片的话会发现我在属性面板的Content Processor中将Resize To Power of Two设置成了True,这样做对图像处理更友好。
发布时间:2009/5/8 下午4:31:35 阅读次数:15061
