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  阅读次数:14468

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号