【发布时间】: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 阴影的矩阵解决方案,在该解决方案中,周围的光线可以找到最近的施法者的深度。好吧,似乎不可能像我预期的那样制作这样的矩阵。所以我找到了另一种方法(仍然没有按预期工作,但非常接近),这就是问题所在。首先让我解释一下拓扑和行为:
- 我在 XY 平面 (0,0),(1,0),(1,1),(0,1) 中定义了一个背景矩形
- 灯光位置为(0.5,0.5),可通过鼠标移动
- 新的矩形可以通过鼠标点击添加到相同的 XY 平面,作为阴影投射器和阴影接收器。 这是视频http://www.youtube.com/watch?v=xn1jHdTpAHU
所以,要从光的位置开始围绕一个圆圈计算深度缓冲区:
-
对于poligon线的每个顶点,VS通过atan2函数计算从光照位置的角度,输出顶点的位置为-1 , Y = 0.5 和 0 ,所以我只是根据目标纹理高度中间的角度弧生成水平线(只是现在)
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 行 - 定义了渲染行
【问题讨论】: