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