HLSL中的镜面反射

时间:2014-10-29 19:53:59

标签: c++ graphics directx directx-11 hlsl

我试图了解HLSL和DirectX11中的镜面反射

cbuffer ConstantBuffer : register( b0 )
{
    matrix World;           // Матрица мира
    matrix View;            // Матрица вида
    matrix Projection;      // Матрица проекции
    float4 vLightDir[3];    // Dir of light
    float4 vLightColor[3];  // color of light
    float4 vOutputColor;    // Active color
    //float3 Eye;
}

struct VS_INPUT                 // Входящие данные вершинного шейдера
{
    float4 Pos : POSITION;      // Позиция по X, Y, Z
    float3 Norm : NORMAL;       // Нормаль по X, Y, Z
};

struct PS_INPUT                 // Входящие данные пиксельного шейдера
{
    float4 Pos : SV_POSITION;   // Позиция пикселя в проекции (экранная)
    float3 Norm : TEXCOORD0;    // Относительная нормаль пикселя по tu, tv
};


PS_INPUT VS( VS_INPUT input )
{
    PS_INPUT output = (PS_INPUT)0;
    output.Pos = mul( input.Pos, World );
    output.Pos = mul( output.Pos, View );
    output.Pos = mul( output.Pos, Projection );
    output.Norm = mul( input.Norm, World );

    return output;
}


float4 Diffuse( PS_INPUT input) : SV_Target
{
    float4 diffuse = float4(1.0, 1.0, 1.0, 1.0);
    float4 finalColor = diffuse *0.1;

    // Adding all light colours
    for(int i=0; i<3; i++)
    {
        finalColor += saturate( dot( (float3)vLightDir[i], input.Norm) * vLightColor[i] );
    }
    finalColor.a = 1;
    return finalColor;
}

float4 Specular(PS_INPUT input) : SV_Target
{   
    float4 diffuse = float4(1.0, 0.0, 0.0, 1.0);
    float4 finalColor = diffuse *0.1;
    float3 Eye = float3(0.0f, 4.0f, -20.0f);
    float4 intensity = 0.1;
    float power = 4;

    float3 R = reflect(-normalize(Eye), input.Norm);
    for (int i = 0; i < 3; i++)
    {
        finalColor += saturate(intensity * vLightColor[i] * pow(dot(R, Eye), power));
    }   
    return finalColor;
}


float4 PSSolid( PS_INPUT input) : SV_Target
{
    return vOutputColor;
}

现在我只有漫反射,镜面反射只显示白色立方体。 :( 我在哪里可以获得关于镜面反射的示例或辅导?

1 个答案:

答案 0 :(得分:7)

您要实现的目标称为Phong Reflection Model,它通过计算法向量和光方向向量之间的反射向量来生成镜面反射高光。然后使用点积来计算反射矢量和从表面到眼睛位置的矢量之间的角度的余弦。

您的Specular着色器存在一些问题。

首先,您的Eye向量不正确。它需要是从表面到眼睛位置的矢量。目前,您只使用一个职位。要完成此操作,您需要修改顶点着色器以输出顶点位置世界空间位置。为此,请添加:

float3 WorldPos : TEXCOORD1;

PS_INPUT。现在计算顶点着色器中的世界空间顶点位置:

output.WorldPos = mul( input.Pos, World );

此值将被发送到像素着色器并在像素上进行插值,以便它给出像素的世界位置。使用此功能,您可以计算像素着色器中像素到眼睛位置的视图向量 V 。要做到这一点:

float3 V = normalize( Eye - input.WorldPos );

现在,反射向量 R 是通过反射表面法线(不是眼睛位置)周围的入射光方向而创建的,并且必须针对循环中的每个光线计算:

float3 R = reflect( normalize( vLightDir[i] ), normalize( input.Norm ) );

请注意,在此等式中,光矢量指向光线到表面(因此不要如下所述否定它)。

现在,您已准备好使用 R V 之间的点积来计算每个灯光所创建的镜面反射分量:

finalColor += intensity * vLightColor[i] * pow( saturate( dot( R, V ) ), power );

我看到的最后一个问题是您没有在Specular着色器中正确计算漫反射。如果您希望不面向灯光的区域更暗,则需要像Diffuse着色器一样进行此操作。但是,不是直接使用vLightDir[i],而是需要对其进行否定,使其从曲面指向光源:

float3 L = -normalize( vLightDir[i].xyz );

Phong Reflection Model 更便宜的方法称为Blinn-Phong Reflection Model。它不是使用 R ,而是使用这个简单的等式计算 V L 之间的半矢量:

float3 H = normalize( L + V );

现在,您不必在计算高光时使用dot( R, V ),而是使用:

dot( N, H );