in prev question https://stackoverflow.com/questions/32085542/how-to-define-a-matrix-to-transform-2d-plane-into-perspective-360%C2%B0-fov i've asked for a matrix solution for 2D shadows, where depth to the nearest caster is found by all around light. Well, seems its impossible to make such a matrix as i expected. So i've found another method (still does'nt work as expected but very near), and this is the question. First let me explain topology and behaviour:
- I've defined a background rect with vertices in XY plane (0,0),(1,0),(1,1),(0,1)
- Light position is (0.5,0.5) and can be moved by mouse
- New rects can be added by mouse click in the same XY plane, as shadow casters and in the same time as shadow receivers. Here is the video http://www.youtube.com/watch?v=xn1jHdTpAHU
So, to calc depth buffer all around a circle from light position i do:
For each vertex of the poligon line, VS calculates angle from light position by atan2 function, and output position of the vertex to be -1 <= X <= 1, Y = 0.5 and 0 <= Z <= 1, so i just generate horizontal lines according to the arc of angle in the middle of height of a target texture (just for now)
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; }
then by PS, i calculate the depth buffer, depth = ((interpolated PosW) - light pos)
float MakeShadowPS(VertexShaderOutputMakeShadow input) : COLOR0 { float2 v = input.PosW - LightPos; return length(v); }
and finally, i render shadows by comparing distance between light and a pixel with depth buffer distance at the same angle, so if the distance is more than stored then it is in shadow:
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));
}
But there is strange thing - you can see it at the 0:19 in the video (yellow areas nearby sun in the left top coner), something like a fish-eye effect. And second (not sure how to fix it yet) - where line begins say 135 degrees to -135 it should rendered to -0.75PI to 0.75PI (left line of a rect) so it rewrites almost whole buffer directly (0:31), however i want it in 2 parts - -1..-0.75 and 0.75-1. Well, i've found a solution, but its weird. Can't find fine one :( For this video, i just don't render left side at all, so there such artifacts like not shadowed blue triangles as at the 0:30. Any ideas ?
Well, implemented weird solution - i just render vertex buffer twice with a different shader, so VS findouts if an angle between line points > PI then adjust X (zw are xy of the second point of the line):
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;
}
and second VS only differs:
if (abs(angle1 - angle2) > 3.1415926535)
{
if (angle1 > 0)
{
angle1 = angle1 - 2 * 3.1415926535;
}
}
else
{
angle1 = -100;
}
P.S. "angle1 = -100;" means here to disable rasterizing lines which were feeded by the first shader, so what is happenning youtu.be/BWmBnF1eTho
But first problem is still there. Debugging by VS Graphics Debugger shown the first problem - interpolation of xy from x1y1 to x2y2 by passing to the TEXTCOORD is not going as strigth line, and i'm not sure why :( Tried to interpolate angle between points and find depth as distance between point and light/sin(interpolated angle), and it worked for horizontal lines -
float MakeShadowPS(VertexShaderOutputMakeShadow input) : COLOR0
{
return (LightPos.y - input.PosW.y) / sin(input.PosW.z);
}
youtu.be/HgAiYRmSRSk same for vertical lines but cosine - abs(LightPos.x - input.PosW.x) / cos(input.PosW.z); But how can i merge these two methods ? Project located at https://yadi.sk/d/pgri0j_IjBamD it uses VS2013 and last MonoGame. If you are going to try it, please note QuadsBoard.cs lines 111 and 185 - there are rendered lines defined