【问题标题】:360 FOV depth buffer by topology and 2D shadows通过拓扑和 2D 阴影的 360 FOV 深度缓冲区
【发布时间】:2015-09-26 04:21:01
【问题描述】:

在上一个问题https://stackoverflow.com/questions/32085542/how-to-define-a-matrix-to-transform-2d-plane-into-perspective-360%C2%B0-fov 中,我要求提供用于 2D 阴影的矩阵解决方案,在该解决方案中,周围的光线可以找到最近的施法者的深度。好吧,似乎不可能像我预期的那样制作这样的矩阵。所以我找到了另一种方法(仍然没有按预期工作,但非常接近),这就是问题所在。首先让我解释一下拓扑和行为:

  1. 我在 XY 平面 (0,0),(1,0),(1,1),(0,1) 中定义了一个背景矩形
  2. 灯光位置为(0.5,0.5),可通过鼠标移动
  3. 新的矩形可以通过鼠标点击添加到相同的 XY 平面,作为阴影投射器和阴影接收器。 这是视频http://www.youtube.com/watch?v=xn1jHdTpAHU

所以,要从光的位置开始围绕一个圆圈计算深度缓冲区:

  1. 对于poligon线的每个顶点,VS通过atan2函数计算从光照位置的角度,输出顶点的位置为-1 , Y = 0.50 ,所以我只是根据目标纹理高度中间的角度弧生成水平线(只是现在)

    struct VertexShaderInput
    {
        float4 Position : SV_POSITION;
        float4 Color : COLOR0;
    };
    struct VertexShaderOutputMakeShadow
    {
        float4 Position : SV_POSITION;
        float2 PosW : TEXCOORD0;
    };
    VertexShaderOutputMakeShadow MakeShadowVS(VertexShaderInput input)
    {
        VertexShaderOutputMakeShadow output;
        float2 v = input.Position.xy - LightPos;
        float angle = atan2(-v.y, v.x); // minus to flip y, because y+ goes down
        //output.Position = float4(angle, 0, length(v), 3.1415926535);// same as line bellow, but (-1) HLSL instruction because x is devided by w always in hardware
        output.Position = float4(angle / 3.1415926535, 0, length(v), 1.0);
        output.PosW = input.Position.xy;
        return output;
    }
    

    然后通过PS,我计算深度缓冲区,depth = ((interpolated PosW) - light pos)

    float MakeShadowPS(VertexShaderOutputMakeShadow input) : COLOR0
    {
        float2 v = input.PosW - LightPos;
        return length(v);
    }
    

最后,我通过比较光和像素之间的距离以及相同角度的深度缓冲距离来渲染阴影,所以如果距离大于存储的距离,那么它就在阴影中:

struct VertexShaderOutputUseShadow
{
    float4 Position : SV_POSITION;
    float2 PosW : TEXCOORD0;
    float4 Color : COLOR0;
};
VertexShaderOutputUseShadow UseShadowVS(VertexShaderInput input)
{
    VertexShaderOutputUseShadow output;
    float4 p = float4(input.Position.xy, 0, 1);
    output.Position = mul(p, World);
    output.Color = input.Color;
    output.PosW = input.Position.xy;
    return output;
}
float4 UseShadowPS(VertexShaderOutputUseShadow input) : COLOR0
{
    float2 v = input.PosW - LightPos;
    float angle = atan2(-v.y, v.x);
    float2 UV = float2((angle / 3.1415926535 + 1) / 2, 0.5);
    float shadowD = tex2D(shadowSampler, UV);
    float d = length(v);
    return input.Color * (1 - (d > shadowD ? 1 : d));
}

但有一个奇怪的地方 - 你可以在视频中的 0:19 看到它(左上角太阳附近的黄色区域),有点像鱼眼效果。 第二(不知道如何修复它) - 线开始的地方说 135 度到 -135 它应该呈现到 -0.75PI 到 0.75PI(矩形的左线),所以它直接重写几乎整个缓冲区(0:31) ,但是我希望它分为两部分--1..-0.75 和 0.75-1。好吧,我找到了一个解决方案,但它很奇怪。找不到好的 :( 对于这个视频,我根本不渲染左侧,所以在 0:30 出现了像没有阴影的蓝色三角形这样的伪影。 有什么想法吗?

好吧,实现了奇怪的解决方案 - 我只是用不同的着色器渲染顶点缓冲区两次,所以 VS 找出线点之间的角度是否 > PI 然后调整 X(zw 是线的第二个点的 xy):

VertexShaderOutputMakeShadow MakeShadowVS1(VertexShaderInput input)
{
    VertexShaderOutputMakeShadow output;
    float2 v1 = input.Position.xy - LightPos, v2 = input.Position.zw - LightPos;
    float angle1 = atan2(-v1.y, v1.x), angle2 = atan2(-v2.y, v2.x);
    if (abs(angle1 - angle2) > 3.1415926535)
    {
        if (angle1 < 0)
        {
            angle1 = 2 * 3.1415926535 + angle1;
        }
    }
    output.Position = float4(angle1 / 3.1415926535, 0, length(v1), 1.0);
    output.PosW = input.Position.xy;
    return output;
}

和第二个 VS 唯一不同:

if (abs(angle1 - angle2) > 3.1415926535)
{
    if (angle1 > 0)
    {
        angle1 = angle1 - 2 * 3.1415926535;
    }
}
else
{
    angle1 = -100;
}

附: “角度1 = -100;”在这里意味着禁用由第一个着色器提供的光栅化线,所以发生了什么 youtu.be/BWmBnF1eTho

但第一个问题仍然存在。 VS Graphics Debugger 的调试显示了第一个问题 - 通过传递给 TEXTCOORD 将 xy 从 x1y1 插值到 x2y2 不会像 strigth 行一样,我不知道为什么:( 试图在点之间插值并找到深度作为点和光/sin(插值角)之间的距离,它适用于水平线 -

float MakeShadowPS(VertexShaderOutputMakeShadow input) : COLOR0
{
    return (LightPos.y - input.PosW.y) / sin(input.PosW.z);
}

youtu.be/HgAiYRmSRSk 垂直线相同,但余弦 - abs(LightPos.x - input.PosW.x) / cos(input.PosW.z); 但是我怎样才能合并这两种方法呢? 位于https://yadi.sk/d/pgri0j_IjBamD 的项目使用VS2013 和最后一个MonoGame。如果您要尝试,请注意 QuadsBoard.cs 第 111 和 185 行 - 定义了渲染行

【问题讨论】:

    标签: xna monogame


    【解决方案1】:

    我终于找到了解决方案 :) 这是视频https://youtu.be/TbMJs1zGY6g

    所以这里是 VS 制作阴影(使用 cmets)

    VertexShaderOutputMakeShadow MakeShadowVS1(VertexShaderInput input)
    {
        // lenght of the line (x1y1-x2y2)
        float d1 = distance(input.Position.xy, input.Position.zw);
        // lenght of perpendicular from LightPos to the line
        float d2 = abs(((LightPos.y - input.Position.y) * (input.Position.z - input.Position.x) - (LightPos.x - input.Position.x) * (input.Position.w - input.Position.y)) / d1);
        float2 v = input.Position.xy - input.Position.zw; // vector of the line
        float2 v1 = input.Position.xy - LightPos; // vector from light to x1y1
        float2 v2 = input.Position.zw - LightPos; // vector from light to x2y2
    
        float sa = v.x * v1.y - v1.x * v.y; // classify position of light and vector to x1y1 which we calculate to current vertex (left < 0 > right)
    
        float2 perpendicular = normalize(float2(-v.y, v.x)) * sign(sa); // sign is to flip perpendicular vector if light is not at right
        float perpendicularAngle = atan2(-perpendicular.y, perpendicular.x); // angle of perpendecular
    
        float angle1 = atan2(-v1.y, v1.x), angle2 = atan2(-v2.y, v2.x); // angles for both line points
        /*
            Here is the tricky part
            Since rasterizer will render left-half circle points as shortest line, like 3/4PI to (-3/4PI) it will overwrite whole depth buffer
            i render only part of it which and to oposite direction, so during the second pass the another part of it will be rendered with different condition,
            but all other lines will be clipped:
            if (abs(angle1 - angle2) > 3.1415926535)
            {
                if (angle1 > 0)
                {
                angle1 = angle1 - 2 * 3.1415926535;
                }
            }
            else
            {
                angle1 = -100;
            }
        */
        if (abs(angle1 - angle2) > 3.1415926535)
        {
            if (angle1 < 0)
            {
                angle1 = 2 * 3.1415926535 + angle1;
            }
        }
        float angleBetweenPerpendicularAndPoint = angle1 - perpendicularAngle; // angle to be interpolated
        VertexShaderOutputMakeShadow output;
        output.PosW = float4(angleBetweenPerpendicularAndPoint, d2, 0, 0); // feed to interpolator
        output.Position = float4(angle1 / 3.1415926535, 0, length(v1), 1.0); // generate X and Z of the transformed vertex, so its between -1 <= X <= 1, and 0 <= Z <= 1
        return output;
    }
    

    而且PS很简单

    float MakeShadowPS(VertexShaderOutputMakeShadow input) : COLOR0
    {
        // input.PosW.y is always the distance from light to the rendered line
        // but input.PosW.x interpolated from angle1 to angle2
        return input.PosW.y / cos(input.PosW.x);
    }
    

    VS/PS 使用阴影还是一样

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-12
      • 1970-01-01
      • 1970-01-01
      • 2016-01-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多