深入Managed DirectX9(二十)

使用可编程管道渲染Mesh

  简单三角形例子唯一的特点就是:简单。你不可能只使用一个顶点程序和一个三角就完成一个游戏,因此,这一节我们将渲染整个mesh。删除文件里关于顶点和顶点缓冲的声明,添加mesh对象:

private Mesh mesh = null;

对于渲染mesh来说,有一个方便的地方就是在渲染时会自动设置vertex declaration。自然,删除所有处理顶点声明和顶点缓冲的代码。同时,更新InitializeGraphics创建mesh。创建了mesh和effect对象之后,添加代码:

mesh = Mesh.Teapot(device);

最后,修改OnPaint方法,使用DrawSubset方法代替DrawPrimitives:

mesh.DrawSubset(0);

  你看这里并没有多少需要修改的地方,现在运行程序,可以得到一个和三角形颜色效果差不多的茶壶。由于茶壶所以位置的颜色都一样,导致它看起来不太像一个茶壶。还记得我们以前也遇到过这种情况吗,添加一个方向灯情况就会好的多。

  由于本章的目的是不使用固定管道来进行渲染,所以不使用device的lights属性。我们将更新HLSH代码来实现一个白色的方向灯。在HLSL代码中添加如下变量:

float3 LightDir = {0.0f,0.0f,-1.0f);

接下来,使用以下代码代替现有的方法:

VS_OUTPUT Transform( float4 Pos : POSITION, float3 Normal : NORMAL )
{
    VS_OUTPUT Out = (VS_OUTPUT)0;
    float4 transformedNormal = mul(Normal, WorldViewProj);
    Out.diff.rb = 1.0f;
    Out.diff.ga = 0.0f;
    Out.diff *= dot(transformedNormal, LightDir);
    Out.pos = mul(Pos, WorldViewProj);
    return Out;
}

  注意我们使用了一个新的参数作为法线数据。你必须保证每个使用这个technique的顶点都有法线数据,否则将导致异常。Mesh类创建的茶壶已经包含了法线信息,所以这里不会出什么问题。接下来,需要把法线数据和顶点转换到同一个坐标系中。对不同坐标系下的顶点和法线进行光照计算是没有意义的。

  如果你看过SDK中关于实现方向光的数学原理,就知道它实际上是使用顶点法线和灯光方向的点积再乘以光的颜色来实现的。可以把rgba混合值都设置为1,并且通过计算得到最后的值。

  这个程序现在因该渲染了一个旋转的白色茶壶。添加了灯光之后,茶壶看起来要真实多了。你可以任意修改灯光的颜色来看看。


使用HLSL编写Pixel Shader

  至今为止,我们之使用了顶点程序来控制顶点,然而,这只是可编程管道的一半而已。如何处理每一个独立像素的颜色呢?我们还是使用第五章MeshFile的例子作为基础,把它更新为使用编成管道的程序。使用这个例子的原因是他不仅渲染了顶点,还使用纹理来着色,而这正是使用pixel shader的地方。

打开项目知道,线添加一些必要的变量:

private Effect effect = null;
private Matrix worldMatrix;
private Matrix viewMatrix;
private Matrix projMatrix;

  同样根据上一节的方法,更新initializeGraphics方法,检查显卡是否支持编成管道。这一次我们需要同时检查对vertex shader和pixel shader的支持。按照上一节中的方法,更改以下代码:

if((hardware.VertexShaderVersion >= new Version(1,1)) && (hardware.PixelShaderVersion >= new Version(1,1)))
    ……………
effect = Effect.FromFile(device,@"..\..\simple.fx",null,ShaderFlags.None,null);
effect.Technique = "TransformTexture";
projMatrix = Matrix.PerspectiveFovLH((float)Math.PI/4,this.Width / this.Height,1.0f,10000.0f);
viewMatrix = Matrix.LookAtLH(new Vector3(0,0,580.0f),new Vector3(), new Vector3(0,1.0f,0));

  这里和之前的方法很类似。由于SetupCamera方法使用了固定管道中的变换和光照,所以可以把它完全删除了。自然,还需要修改OnPaint方法。接下来,更新DrawMesh方法,使用HLSL来进行渲染。

  同样,每帧调用这个方法的时候都会更新合并之后的变换矩阵以及HLSL中的变量。接下来使用technique中的每一个pass渲染每一个mesh中的子集。好了,可以添加HLSL代码了,添加“simple.fx”文件:

float4x4 WorldViewProj : WORLDVIEWPROJECTION;
sampler TextureSampler;
void Transform(
    in float4 inputPosition : POSITION,
    in float2 inputTexCoord : TEXCOORD0,
    out float4 outputPosition : POSITION,
    out float2 outputTexCoord : TEXCOORD0
)
{
    // Transform our position
    outputPosition = mul(inputPosition, WorldViewProj);
    // Set our texture coordinates
    outputTexCoord = inputTexCoord;
}

void TextureColor(
    in float2 textureCoords : TEXCOORD0,
    out float4 diffuseColor : COLOR0)
{
    // Get the texture color
    diffuseColor = tex2D(TextureSampler, textureCoords);
};

technique TransformTexture
{
    pass P0
    {
        // shaders
        VertexShader = compile vs_1_1 Transform();
        PixelShader = compile ps_1_1 TextureColor();
    }
}

  你可能马上就注意到了这里的着色程序和先前的不同。这次我们没有使用一个结构来作为返回值,而是把要返回的值作为方法的out参数。对于顶点程序来说,我们只关心顶点位置和纹理坐标。接下来的则是pixel shader。他接受当前像素的纹理坐标,并返回这个像素颜色用于渲染。这里,我们只使用了在这个纹理坐标上的默认颜色,因此使用了内建的tex2D方法。它在给定坐标对纹理进行采样,并且返回这个位置的颜色。这几乎是最简单的pixel shader了。运行程序看看吧。


  但是,pixel shader并没有完成什么用固定管道不能完成的功能。让我们来做点有趣的工作吧。你将在这个HLSL中使用多个technique,添加如下代码:

void InverseTextureColor(
    in float2 textureCoords : TEXCOORD0,
    out float4 diffuseColor : COLOR0)
{
    // Get the inverse texture color
    diffuseColor = 1.0f - tex2D(TextureSampler, textureCoords);
};

technique TransformInverseTexture
{
    pass P0
    {
        // shaders
        VertexShader = compile vs_1_1 Transform();
        PixelShader = compile ps_1_1 InverseTextureColor();
    }
}

  这里的代码和第一个pixel shader差不多,唯一的区别就是使用1.0f减去所采样的颜色值,这样会得到这个像素 的反转颜色。同时还创建了一个新的technique来调用新的pixel shader。接下来,更新C#代码,实现在两种效果之间跳转:

protected override void onKeyPress(KeyPressEventArgs e)
{
    switch(e.KeyChar)
    {
    case '1':
        effect.Technique = "TransformTexture";
    break;
    case '2':
        effect.Technique = "TransformInverseTexture";
    break;
    }
    base.onKeyPress (e);
}

  现在运行程序看看两种不同的效果吧。在源码中,我们还包含了只允许或不允许蓝色通道的遮罩效果。

~~~~~~~~~~~~~~~~~~~~~~~第十一章完~~~~~~~~~~~~~~~~~~~~~~~~~
下一章我们将讨论更多关于HLSL的内容

 下载代码

posted on 2007-11-07 00:33  yurow  阅读(301)  评论(0编辑  收藏  举报

导航