Representing Lens Distortion coordinates in 2D I with my custom lens shader - What am I doing wrong?
Hi,
I am experimenting with something lens related and I have a 'strange' problem:
Scenario:
I have a grid/checkerboard (black/blue) in 3D space to visualize the lens distortion
I want to represent the distortion in 2D space (Canvas) by converting the checkerboard from world space to canvas (red grid).
The checkerboard and the red grid should match each other.
I have done this by using the same distortion algorithm with my shader and the C# script that generates the red UI grid.
The 2D representation of the camera is working great without my shader, but when I enable the shader, it works as if it is inverted (as seen in the gif above). I have simplified the shader algorithm to show the problem easier here.
The shader logic:
float2 GetSimpleDistortion(float2 uv) {
float2 center = float2(0.5, 0.5); // Center of the screen
float2 d = uv - center; // Distance vector from center
float r = sqrt(d.x * d.x + d.y * d.y); // Distance from center (hypothenusa)
float2 newuv = float2(0.0, 0.0);
newuv.x = uv.x + d.x * r * r * _simpleK; // move away from the center
newuv.y = uv.y + d.y * r * r * _simpleK;
return newuv;
}
float4 LensEffectFragmentProgram(PostProcessVaryings input) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
float2 uv = UnityStereoTransformScreenSpaceTex(input.texcoord);
uv = GetSimpleDistortion(uv);
float4 color = LOAD_TEXTURE2D_X(_MainTex, uv * _ScreenSize.xy);
return color;
}
And then in my C# script, I am distorting the red UI grid with the same algorithm:
private Vector2 GetSimpleLensDistortion(Vector3 viewPort, MQMLensEffect _lensEffect)
{
Vector2 uv = new Vector2(viewPort.x, viewPort.y);
Vector2 center = new Vector2(0.5f, 0.5f); // Center of the screen
Vector2 d = uv - center; // Distance vector from center
float r = Mathf.Sqrt(d.x * d.x + d.y * d.y); // Distance from center (hypothenusa)
Vector2 newuv = Vector2.zero;
newuv.x = uv.x + d.x * r * r * _lensEffect.simpleK.value; // move away from the center
newuv.y = uv.y + d.y * r * r * _lensEffect.simpleK.value;
return newuv;
}
And this is how I move the UI grid:
for (int x = 0; x < sizex; x++)
{
for (int y = 0; y < sizey; y++)
{
Vector3 viewport = cam.WorldToViewportPoint(grid3d_points[x,y].position);
Vector2 uv = GetSimpleLensDistortion(viewport, _lensEffect);
var newViewport = new Vector3(uv.x, uv.y, viewport.z);
var canvas_pos = Vector3.Scale(newViewport, canvasRect.sizeDelta);
screen_handles[x,y].anchoredPosition = new Vector2(canvas_pos.x, canvas_pos.y);
}
}
I am doing this by converting the 3D grid points to viewport coordinates - which I think should correspond with the shader's UV-coordinates? - and then to canvas coordinates. But somehow they don't match.
The only variable controlling the distortion is a float 'simpleK' - if simpleK == 0, there is no distortion and the grids match perfectly. It feels like it is working almost, but inverted - as in I am watching the effect from 'behind' or something. And actually, if I invert the simpleK (simpleK = -simpleK) variable only in the C# script, it almost works, but with little bit strange scaling.
Here is an gif showing what happens if simpleK = -simpleK in the C# script:
So the final question is:
What am I missing in my 2D calculations? Something related to camera frustum or some simple factor or what? I am lost.
Help, please.