拓扑和2D阴影的360 FOV深度缓冲

时间:2015-09-05 06:25:25

标签: xna monogame

在上一个问题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. 中定义了一个背景矩形的顶点
  3. 灯光位置为(0.5,0.5),可以通过鼠标移动
  4. 可以通过鼠标单击在同一XY平面中添加新的rects作为阴影脚轮,同时添加阴影接收器。 这是视频http://www.youtube.com/watch?v=xn1jHdTpAHU
  5. 因此,要从光线位置围绕圆圈计算深度缓冲,我会这样做:

    1. 对于poligon线的每个顶点,VS通过atan2函数计算从光位置的角度,并且将顶点的输出位置计算为 -1 <= X <= 1 Y = 0.5 0 <= Z <= 1 ,所以我只是根据目标纹理高度中间的角度弧生成水平线(就目前而言)

      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,我计算深度缓冲,深度=((插值PosW) - 光pos)

      float MakeShadowPS(VertexShaderOutputMakeShadow input) : COLOR0
      {
          float2 v = input.PosW - LightPos;
          return length(v);
      }
      
    2. 最后,我通过比较光线和像素之间的距离以及相同角度的深度缓冲距离来渲染阴影,所以如果距离大于存储距离那么它就是阴影:

      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没有阴影蓝色三角形。 有什么想法吗?

      好吧,实现了奇怪的解决方案 - 我只是使用不同的着色器渲染顶点缓冲区两次,因此如果线点之间的角度>然后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;
      }
      

      P.S。 “angle1 = -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行 - 已定义渲染线

1 个答案:

答案 0 :(得分:0)

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

所以这是VS制作阴影(带注释)

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使用阴影仍然相同