Monogame 和 HLSL - 点光阴影贴图工件

Monogame and HLSL - Point Light Shadow Mapping artifacts

我正在用 Monogame 制作游戏。我正在使用正向渲染,我决定编写一个着色器,使用 Blinn-phong 着色模型来调用闪电。我已经实现了这个模型来处理三种类型的光——定向光、点光和聚光灯。在此之后,有时间为我们的游戏添加阴影。我决定使用阴影贴图和百分比更接近过滤技术。我已经实现了它,但不幸的是,为点光源投射阴影存在问题。我从点光源透视图(六个 ViewProjectionMatrices,每个面向一个方向)将我的场景渲染到立方体贴图,然后我将其与当前渲染的对象进行比较。我面临两个问题:

  1. 平面边缘附近有一些奇怪的伪像。尽管被另一个表面覆盖,但表面有一个圆形部分没有被遮挡。问题截图: Point Light Shadow Mapping Issue #1 - note rounded light artifacts (this grey sphere is the position of the light)

  2. 为了使阴影起作用,我必须有某种光边界对象,它位于离点光源最远的地方,光的平截头体可以看到它。如果我不绘制这个物体,它会导致某种反向阴影——我会在表面上看到光,在这个表面后面的另一个物体的位置。为了更好的理解截图: Point Light Shadow Mapping Issue #2 - no light boundaries - scene is not lightened, the character, which shape is visible, is located behind the wall (again, grey sphere is the position of the light

这是我的 HLSL 代码(我将用简短的注释 + "here" 替换与点光源直接无关的部分代码):

#define MAX_DIRECTIONAL_LIGHTS 3
#define MAX_POINT_LIGHTS 4
#define MAX_SPOT_LIGHTS 4

matrix worldMatrix;
matrix viewProjectionMatrix;
matrix currentLightVievProjectionMatrix;
float4 currentLightPosition;

float4 cameraPosition;

texture diffuseTexture;
texture normalTexture;
texture specularTexture;
texture opacityTexture;

float4 globalAmbient;

//Directional Lights related variables here

int currentPointLightsNumber;
float4 pointLightPosition[MAX_POINT_LIGHTS];
float4 pointLightAmbientColor[MAX_POINT_LIGHTS];
float4 pointLightDiffuseColor[MAX_POINT_LIGHTS];
float4 pointLightSpecularColor[MAX_POINT_LIGHTS];
float pointLightRadius[MAX_POINT_LIGHTS];
float pointLightTexelSize[MAX_POINT_LIGHTS];
matrix pointLightViewProjection0;
matrix pointLightViewProjection1;
matrix pointLightViewProjection2;
matrix pointLightViewProjection3;
texture pointLightShadowMap0;
texture pointLightShadowMap1;
texture pointLightShadowMap2;
texture pointLightShadowMap3;

//Spot lights related variables here

float materialShininessFactor;
float DepthBias = float(0.0004F);

sampler2D DiffuseMapSampler = sampler_state
{
    Texture = <diffuseTexture>;
    MinFilter = Anisotropic;
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU = wrap;
    AddressV = wrap;
    MaxAnisotropy = 16;
};

sampler2D NormalMapSampler = sampler_state
{
    Texture = <normalTexture>;
    MinFilter = Anisotropic;
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU = wrap;
    AddressV = wrap;
    MaxAnisotropy = 4;
};

sampler2D SecularMapSampler = sampler_state
{
    Texture = <specularTexture>;
    MinFilter = Linear;
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU = wrap;
    AddressV = wrap;
};

sampler2D OpacityMapSampler = sampler_state
{
    Texture = <opacityTexture>;
    MinFilter = Linear;
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU = wrap;
    AddressV = wrap;
};

//Directional light shadow map samplers here

samplerCUBE PointLightShadowMapSampler0 = sampler_state
{
    Texture = <pointLightShadowMap0>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = None;
    AddressU = clamp;
    AddressV = clamp;
};

samplerCUBE PointLightShadowMapSampler1 = sampler_state
{
    Texture = <pointLightShadowMap1>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = None;
    AddressU = clamp;
    AddressV = clamp;
};

samplerCUBE PointLightShadowMapSampler2 = sampler_state
{
    Texture = <pointLightShadowMap2>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = None;
    AddressU = clamp;
    AddressV = clamp;
};

samplerCUBE PointLightShadowMapSampler3 = sampler_state
{
    Texture = <pointLightShadowMap3>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = None;
    AddressU = clamp;
    AddressV = clamp;
};

//Spot light shadow map samplers here

struct BlinnPhongVertexShaderInput
{
    float4 position : POSITION;
    float2 textureCoordinates : TEXCOORD;
    float3 normal : NORMAL;
    float3 tangent : TANGENT;
    float3 binormal : BINORMAL;
};

struct BlinnPhongPixelShaderInput
{
    float4 position : SV_POSITION;
    float4 worldPosition : TEXCOORD0;
    float2 textureCoordinates : TEXCOORD1;
    float4 viewDirection : TEXCOORD2;
    float3 normal : TEXCOORD3;
    float3 tangent : TANGENT;
    float3 binormal : BINORMAL;
};

struct CreateShadowMapPixelShaderInput
{
    float4 Position : POSITION;
    float Depth : TEXCOORD0;
};

//Vertex shader for directional and spot lights here

CreateShadowMapPixelShaderInput CreateShadowMapForPointLightVertexShaderFunction(float4 Position : POSITION)
{
    CreateShadowMapPixelShaderInput OUT;
    OUT.Position = mul(Position, worldMatrix);
    OUT.Depth = length(OUT.Position.xyz - currentLightPosition.xyz);
    OUT.Position = mul(OUT.Position, currentLightVievProjectionMatrix);
    return OUT;
}

float4 CreateShadowMapPixelShaderFunction(CreateShadowMapPixelShaderInput input) : COLOR
{
    return float4(input.Depth, 0.0F, 0.0F, 0.0F);
}

BlinnPhongPixelShaderInput BlinnPhongVertexShaderFunction(BlinnPhongVertexShaderInput input)
{
    BlinnPhongPixelShaderInput output;

    float4 worldPosition = mul(input.position, worldMatrix);
    output.position = mul(worldPosition, viewProjectionMatrix);
    output.worldPosition = worldPosition;

    output.textureCoordinates = input.textureCoordinates;
    output.viewDirection = cameraPosition - output.worldPosition;

    output.normal = mul(input.normal, (float3x3)worldMatrix);
    output.tangent = mul(input.tangent, (float3x3)worldMatrix);
    output.binormal = mul(input.binormal, (float3x3)worldMatrix);

    return output;
}

//ShadowMapLookups for directional and spot lights here

float PointLightShadowMapLookup(samplerCUBE shadowMap, float3 shadowTexCoord, float3 offset, float ourDepth, float texelSize)
{
    return (texCUBE(shadowMap, shadowTexCoord + offset * texelSize).r < ourDepth) ? 0.1f : 1.0f;
}

float4 BlinnPhongPixelShaderFunction(BlinnPhongPixelShaderInput input) : COLOR0
{
    float4 color = globalAmbient;
    float4 specularColor = float4(0.0F, 0.0F, 0.0F, 0.0F);

    float3 V = normalize(input.viewDirection.xyz);

    float3 L;
    float3 H;
    float NDotL;
    float NDotH;

    float attenuation;
    float power;

    float4 normalMap = tex2D(NormalMapSampler, input.textureCoordinates);
    normalMap = (normalMap * 2.0F) - 1.0F;

    float3 N = normalize((normalMap.x * normalize(input.tangent)) + (normalMap.y * normalize(input.binormal)) + (normalMap.z * normalize(input.normal)));

    float4 specularMap;
    specularMap = tex2D(SecularMapSampler, input.textureCoordinates);

    float4 lightingPosition;
    float2 ShadowTexCoord;
    float3 PointLightShadowTexCoord;
    float ourdepth;
    float shadowOcclusion;

    //Directional lights lightning callculations here

    for (int j = 0; j < currentPointLightsNumber; ++j)
    {
        L = (pointLightPosition[j].xyz - input.worldPosition.xyz) / pointLightRadius[j];
        attenuation = saturate(1.0F - dot(L, L));

        L = normalize(L);
        H = normalize(L + V);

        NDotL = saturate(dot(N, L));
        NDotH = saturate(dot(N, H));

        power = (NDotL == 0.0F) ? 0.0F : saturate(pow(NDotH, materialShininessFactor / specularMap.a));

        ourdepth = length((pointLightPosition[j].xyz - input.worldPosition.xyz) * 0.98);

        PointLightShadowTexCoord = -normalize(pointLightPosition[j].xyz - input.worldPosition.xyz);

        shadowOcclusion = 0.0F;

        if (j == 0)
        {
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(0.0f, 0.0f, 0.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(1.0f, 0.0f, 0.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(2.0f, 0.0f, 0.0f), ourdepth, pointLightTexelSize[j]);

            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(0.0f, 1.0f, 0.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(1.0f, 1.0f, 0.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(2.0f, 1.0f, 0.0f), ourdepth, pointLightTexelSize[j]);

            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(0.0f, 2.0f, 0.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(1.0f, 2.0f, 0.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(2.0f, 2.0f, 0.0f), ourdepth, pointLightTexelSize[j]);

            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(0.0f, 0.0f, 1.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(1.0f, 0.0f, 1.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(2.0f, 0.0f, 1.0f), ourdepth, pointLightTexelSize[j]);

            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(0.0f, 1.0f, 1.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(1.0f, 1.0f, 1.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(2.0f, 1.0f, 1.0f), ourdepth, pointLightTexelSize[j]);

            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(0.0f, 2.0f, 1.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(1.0f, 2.0f, 1.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(2.0f, 2.0f, 1.0f), ourdepth, pointLightTexelSize[j]);

            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(0.0f, 0.0f, 2.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(1.0f, 0.0f, 2.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(2.0f, 0.0f, 2.0f), ourdepth, pointLightTexelSize[j]);

            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(0.0f, 1.0f, 2.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(1.0f, 1.0f, 2.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(2.0f, 1.0f, 2.0f), ourdepth, pointLightTexelSize[j]);

            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(0.0f, 2.0f, 2.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(1.0f, 2.0f, 2.0f), ourdepth, pointLightTexelSize[j]);
            shadowOcclusion += PointLightShadowMapLookup(PointLightShadowMapSampler0, PointLightShadowTexCoord, float3(2.0f, 2.0f, 2.0f), ourdepth, pointLightTexelSize[j]);
        }
        else if (j == 1)
        {
            //Same code for second point light
        }
        else if (j == 2)
        {
            //Same code for third point light
        }
        else
        {
            //Same code for fourth point light
        }

        shadowOcclusion /= 27.0F;

        color += (pointLightAmbientColor[j] * attenuation) + (pointLightDiffuseColor[j] * NDotL * attenuation * shadowOcclusion);
        specularColor += (pointLightSpecularColor[j] * power * attenuation * specularMap * shadowOcclusion);
    }

    //Spot lights lightning callculations here

    color = saturate(color * tex2D(DiffuseMapSampler, input.textureCoordinates) + specularColor);
    color.a = (float1)tex2D(OpacityMapSampler, input.textureCoordinates);
    return color;
}

//technique for directional and spot lights shadow mapping here

technique CreateShadowMapForPointLight
{
    pass Pass1
    {
        VertexShader = compile vs_4_0 CreateShadowMapForPointLightVertexShaderFunction();
        PixelShader = compile ps_4_0 CreateShadowMapPixelShaderFunction();
    }
}

technique BlinnPhong
{
    pass Pass1
    {
        VertexShader = compile vs_4_0 BlinnPhongVertexShaderFunction();
        PixelShader = compile ps_4_0 BlinnPhongPixelShaderFunction();
    }
}

我知道这看起来很糟糕,但让我解释一下。我无法将光 vievProjection 矩阵存储在数组中,因为它们的值在运行时是净更新的,所以我不得不为每个光将它们拆分为单个矩阵。至于纹理,有一个警告说着色器编译器强制循环展开,所以我只是想确保一切都会好起来的,所以我也把它们分开了。不知道Matrices问题是Monogame还是HLSL think.

回到阴影问题,我提供的 hlsl 有什么问题吗?根据可能的需要,我将提供我负责阴影贴图渲染的类的代码。

这是我的立方体贴图和 vievProjection 矩阵的样子:

RenderTargetCube ShadowMapRenderTarget = new RenderTargetCube(GameObject.Scene.SceneManager.GameEngine.GraphicsDevice,
                            1024,
                            false,
                            SurfaceFormat.Single,
                            DepthFormat.Depth24);
ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver2, 1.0F, 0.1F, Radius) * Matrix.CreateScale(-1, 1, 1);

public void CreateViewMatrix(Vector3 targetVector)
{
     Vector3 upVector;
     if (targetVector.Y > 0)
     {
         upVector = Vector3.Forward;
     }
     else if (targetVector.Y < 0)
     {
         upVector = Vector3.Backward;
     }
     else
     {
         upVector = Vector3.Up;
     }
     ViewMatrix = Matrix.CreateLookAt(GameObject.Transform.Position,GameObject.Transform.Position + targetVector, upVector);
}

public override void CreateViewProjectionMatrix()
{
    ViewProjectionFrustum.Matrix = ViewMatrix * ProjectionMatrix;
}

在绘制调用期间:

        foreach (PointLight pointLight in PointLights)
        {
            foreach (CubeMapFace cubeMapFace in Enum.GetValues(typeof(CubeMapFace)))
            {
                pointLight.CreateViewMatrix(cubeMapFace.GetDirection());
                pointLight.CreateViewProjectionMatrix();
                SceneManager.GameEngine.GraphicsDevice.SetRenderTarget(pointLight.ShadowMapRenderTarget, cubeMapFace);
                SceneManager.GameEngine.GraphicsDevice.Clear(Color.White);
                foreach (DrawShadowMapDelegateType DrawComponent in ComponentsDrawShadowMapForPointLightMethods)
                {
                    DrawComponent(pointLight);
                }
            }
        }

有没有简单的方法或简单的解释为什么会出现这种想法?有没有办法修复它,或者我会被迫尝试实现双抛物面阴影贴图?如果有,hlsl中是否有成功连接到monogame或xna的示例实现?

提前感谢您的任何建议和抽出时间。

自己实现shadow mapping遇到了第一个问题。原因是我在渲染阴影贴图时在顶点着色器而不是片段着色器中计算深度。所以如果你有一个垂直于光源的多边形,每个顶点都会有相同的深度。我通过使用不同的向量(世界片段位置)然后在片段着色器中设置深度来解决它。不是最好的解决方案,因为它对性能来说是一种不好的做法。