• 在场景中添加光线——添加HLSL逐像素光照


    问题

    如教程6-3所示,要获得最好的光照效果应该使用逐像素光照,特别是对那些由大三角形构成的曲线的情况中。你想使用自己的effect添加逐像素光照。

    解决方案

    前两个教程中,你在每个顶点中计算明暗值(shading value,也可以翻译成着色值)。三角形三个顶点的明暗值会进行线性以获取每个像素的明暗值。

    在逐像素光照中,你想对三个顶点的法线进行插值以获取每个像素的法线,这样就可以基于每个像素的法线计算光照因子。但是,当从一个顶点到另一个顶点进行法线插值时结果是有缺陷的。如图6-9中的左图所示,图中的水平直线表示一个顶点包含法线的三角形。当你在像素上对这左右两个顶点的法线进行插值时,插值的法线总会沿着虚线。导致在三角形中间的法线法线是正确的,其他位置的法线会小于实际值,如图所示。

    image

    图6-9 从顶点到像素的线性插值是错误的

    解决方法是在pixel shader中处理这个插过值的法线。因为它的方向是正确的,你可以归一化这个向量。这是因为每个像素的缩放因子是不同的,所以这一步需要在pixel shader中进行。

    注意:要理解为什么将法线长度变为1,可参见教程6-1中的“归一化法线”一节。

    当你想让光照强度只取决于法线和入射光夹角时,更小的法线导致更小的光照强度。

    光照方向也会遇到同样的问题,如图6-9右图所示。插过值的光线方向的长度沿着虚线曲线,这会导致向量比实际的小。你仍需要归一化这个插过值的光线方向。

    工作原理

    和以往一样,你的项目必须从至少包含3D位置和法线的顶点中与显卡进行交互。你想让XNA代码可以设置World,View,Projection矩阵,光源的3D位置和环境光:

    float4x4 xWorld; float4x4 xView;
    float4x4 xProjection; 
    float3 xLightPosition; 
    float xAmbient; 
    
    struct PPSVertexToPixel 
    {
        float4 Position: POSITION; 
        float3 Normal: TEXCOORD0; 
        float3 LightDirection: TEXCOORD1; 
    }; 
    
    struct PPSPixelToFrame 
    {
        float4 Color: COLOR0; 
    };

    如前所述,vertex shader会输出法线,这个法线已经进行了插值,光线方向也进行了插值。pixel shader只需计算每个像素最后的颜色。

    注意:在单向光的简单例子中,光源的方向是XNA-to-HLSL变量,对顶点和像素来说都是不变的。所以vertex shader无需计算这个值。

    Vertex Shader

    vertex shader从顶点中接受法线,根据世界矩阵中的旋转值旋转这个法线(见教程6-5),并将它传递到pixel shader。 在vertex shader中还通过将顶点位置减去点光源的位置计算了光线方向(见教程6-5)。根据当前世界矩阵获取顶点的最终3D位置。

    PPSVertexToPixel PPSVertexShader(float4 inPos: POSITION0, float3 inNormal: NORMAL0) 
    {
        PPSVertexToPixel Output = (PPSVertexToPixel)0; 
        
        float4x4 preViewProjection = mul(xView, xProjection); 
        float4x4 preWorldViewProjection = mul(xWorld, preViewProjection);
        
        Output.Position = mul(inPos, preWorldViewProjection);
        float3 final3DPos = mul(inPos, xWorld); 
        Output.LightDirection = final3DPos - xLightPosition; 
        float3x3 rotMatrix = (float3x3)xWorld; 
        float3 rotNormal = mul(inNormal, rotMatrix);
        Output.Normal = rotNormal;
        
        return Output; 
    }
    Pixel Shader

    法线和光线方向在三个顶点间进行插值,作用在三角形的所有像素上。如前所述,因为被插值的向量的长度会比实际的小,所以会发生错误。你可以通过归一化操作解决这个问题。将两个方向归一化之后,就可以点乘两者获取光照因子:

    PPSPixelToFrame PPSPixelShader(PPSVertexToPixel PSIn) : COLOR0 
    {
        PPSPixelToFrame Output = (PPSPixelToFrame)0; 
        float4 baseColor = float4(0,0,1,1); 
        float3 normal = normalize(PSIn.Normal); 
        float3 lightDirection = normalize(PSIn.LightDirection); 
        float lightFactor = dot(normal, -lightDirection);
       
        Output.Color = baseColor*(lightFactor+xAmbient); 
        
        return Output; 
    }
    定义Technique

    这个technique需要Shader 2.0–compatible的显卡:

    technique PerPixelShading
    {
        pass Pass0 
        {
            VertexShader = compile vs_2_0 PPSVertexShader(); 
            PixelShader = compile ps_2_0 PPSPixelShader(); 
        }
    }
    代码

    前面已经写过. fx文件中的HLSL代码了,所以下面只是绘制三角形的XNA代码:

    effect.CurrentTechnique = effect.Techniques["PerPixelShading"]; 
    effect.Parameters["xWorld"].SetValue(Matrix.Identity); 
    effect.Parameters["xView"].SetValue(fpsCam.ViewMatrix); 
    effect.Parameters["xProjection"].SetValue(fpsCam.ProjectionMatrix); 
    effect.Parameters["xAmbient"].SetValue(0.0f); 
    effect.Parameters["xLightPosition"].SetValue(new Vector3(6.0f, 1.0f, -5.0f));
    
    effect.Begin(); 
    foreach (EffectPass pass in effect.CurrentTechnique.Passes) 
    {
        pass.Begin(); 
        device.VertexDeclaration = myVertexDeclaration; 
        device.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleStrip, vertices, 0, 6); 
        pass.End(); 
    }
    effect.End();

    你可以试着改变光源的位置查看效果。本例中的光源前后移动。

    image

  • 相关阅读:
    【转】属性与字段的区别
    学习C/C++的经验谈(转)
    [C++语法] 关键字typedef用法(转)
    让我们习惯在底层用C++宏生成代码 (转)
    C/C++笔试题 (二)【转】
    C/C++笔试题 (三)【转】
    C语言 printf格式控制符 完全解析
    C/C++笔试题 (一)【转】
    C++内存管理详解(转)
    【转】 Source Insight设置
  • 原文地址:https://www.cnblogs.com/AlexCheng/p/2120096.html
Copyright © 2020-2023  润新知