Fish eye correction
This commit is contained in:
100
shaders/fisheye-reproject/shader.json
Normal file
100
shaders/fisheye-reproject/shader.json
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
{
|
||||||
|
"id": "fisheye-reproject",
|
||||||
|
"name": "Fisheye Reproject",
|
||||||
|
"description": "Inverse-projects a cropped fisheye source into a virtual rectilinear or cylindrical camera view with pan, tilt, and roll controls.",
|
||||||
|
"category": "Projection",
|
||||||
|
"entryPoint": "shadeVideo",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"id": "lensFovDegrees",
|
||||||
|
"label": "Lens FOV",
|
||||||
|
"type": "float",
|
||||||
|
"default": 190.0,
|
||||||
|
"min": 1.0,
|
||||||
|
"max": 220.0,
|
||||||
|
"step": 0.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "center",
|
||||||
|
"label": "Optical Center",
|
||||||
|
"type": "vec2",
|
||||||
|
"default": [0.5, 0.5],
|
||||||
|
"min": [0.0, 0.0],
|
||||||
|
"max": [1.0, 1.0],
|
||||||
|
"step": [0.001, 0.001]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "radius",
|
||||||
|
"label": "Fisheye Radius",
|
||||||
|
"type": "vec2",
|
||||||
|
"default": [0.5, 0.885],
|
||||||
|
"min": [0.001, 0.001],
|
||||||
|
"max": [2.0, 2.0],
|
||||||
|
"step": [0.001, 0.001]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "virtualFovDegrees",
|
||||||
|
"label": "Virtual FOV",
|
||||||
|
"type": "float",
|
||||||
|
"default": 75.0,
|
||||||
|
"min": 1.0,
|
||||||
|
"max": 175.0,
|
||||||
|
"step": 0.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "panDegrees",
|
||||||
|
"label": "Pan",
|
||||||
|
"type": "float",
|
||||||
|
"default": 0.0,
|
||||||
|
"min": -180.0,
|
||||||
|
"max": 180.0,
|
||||||
|
"step": 0.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "tiltDegrees",
|
||||||
|
"label": "Tilt",
|
||||||
|
"type": "float",
|
||||||
|
"default": 0.0,
|
||||||
|
"min": -120.0,
|
||||||
|
"max": 120.0,
|
||||||
|
"step": 0.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rollDegrees",
|
||||||
|
"label": "Roll",
|
||||||
|
"type": "float",
|
||||||
|
"default": 0.0,
|
||||||
|
"min": -180.0,
|
||||||
|
"max": 180.0,
|
||||||
|
"step": 0.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fisheyeModel",
|
||||||
|
"label": "Fisheye Model",
|
||||||
|
"type": "enum",
|
||||||
|
"default": "equidistant",
|
||||||
|
"options": [
|
||||||
|
{ "value": "equidistant", "label": "Equidistant" },
|
||||||
|
{ "value": "equisolid", "label": "Equisolid" },
|
||||||
|
{ "value": "stereographic", "label": "Stereographic" },
|
||||||
|
{ "value": "orthographic", "label": "Orthographic" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "outputProjection",
|
||||||
|
"label": "Output Projection",
|
||||||
|
"type": "enum",
|
||||||
|
"default": "rectilinear",
|
||||||
|
"options": [
|
||||||
|
{ "value": "rectilinear", "label": "Rectilinear" },
|
||||||
|
{ "value": "cylindrical", "label": "Cylindrical" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "outsideColor",
|
||||||
|
"label": "Outside Color",
|
||||||
|
"type": "color",
|
||||||
|
"default": [0.0, 0.0, 0.0, 1.0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
95
shaders/fisheye-reproject/shader.slang
Normal file
95
shaders/fisheye-reproject/shader.slang
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
static const float PI = 3.14159265358979323846;
|
||||||
|
|
||||||
|
float radiansFromDegrees(float degrees)
|
||||||
|
{
|
||||||
|
return degrees * (PI / 180.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 rotateX(float3 ray, float angle)
|
||||||
|
{
|
||||||
|
float s = sin(angle);
|
||||||
|
float c = cos(angle);
|
||||||
|
return float3(ray.x, c * ray.y - s * ray.z, s * ray.y + c * ray.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 rotateY(float3 ray, float angle)
|
||||||
|
{
|
||||||
|
float s = sin(angle);
|
||||||
|
float c = cos(angle);
|
||||||
|
return float3(c * ray.x + s * ray.z, ray.y, -s * ray.x + c * ray.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 rotateZ(float3 ray, float angle)
|
||||||
|
{
|
||||||
|
float s = sin(angle);
|
||||||
|
float c = cos(angle);
|
||||||
|
return float3(c * ray.x - s * ray.y, s * ray.x + c * ray.y, ray.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 buildRectilinearRay(float2 screen, float outputAspect, float tanHalfFov)
|
||||||
|
{
|
||||||
|
return normalize(float3(screen.x * outputAspect * tanHalfFov, screen.y * tanHalfFov, 1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 buildCylindricalRay(float2 screen, float outputAspect, float tanHalfFov)
|
||||||
|
{
|
||||||
|
float horizontalFov = 2.0 * atan(outputAspect * tanHalfFov);
|
||||||
|
float yaw = screen.x * horizontalFov * 0.5;
|
||||||
|
float vertical = screen.y * tanHalfFov;
|
||||||
|
return normalize(float3(sin(yaw), vertical, cos(yaw)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float normalizedFisheyeRadius(float theta, float halfFov)
|
||||||
|
{
|
||||||
|
float safeHalfFov = max(halfFov, 0.0001);
|
||||||
|
|
||||||
|
if (fisheyeModel == 1)
|
||||||
|
{
|
||||||
|
return sin(theta * 0.5) / max(sin(safeHalfFov * 0.5), 0.0001);
|
||||||
|
}
|
||||||
|
else if (fisheyeModel == 2)
|
||||||
|
{
|
||||||
|
return tan(theta * 0.5) / max(tan(safeHalfFov * 0.5), 0.0001);
|
||||||
|
}
|
||||||
|
else if (fisheyeModel == 3)
|
||||||
|
{
|
||||||
|
return sin(theta) / max(sin(safeHalfFov), 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
return theta / safeHalfFov;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 shadeVideo(ShaderContext context)
|
||||||
|
{
|
||||||
|
float2 screen = float2(context.uv.x * 2.0 - 1.0, 1.0 - context.uv.y * 2.0);
|
||||||
|
float outputAspect = context.outputResolution.x / max(context.outputResolution.y, 1.0);
|
||||||
|
|
||||||
|
float virtualFov = radiansFromDegrees(clamp(virtualFovDegrees, 1.0, 175.0));
|
||||||
|
float tanHalfFov = tan(virtualFov * 0.5);
|
||||||
|
|
||||||
|
float3 ray = outputProjection == 1
|
||||||
|
? buildCylindricalRay(screen, outputAspect, tanHalfFov)
|
||||||
|
: buildRectilinearRay(screen, outputAspect, tanHalfFov);
|
||||||
|
|
||||||
|
ray = rotateZ(ray, radiansFromDegrees(rollDegrees));
|
||||||
|
ray = rotateX(ray, radiansFromDegrees(-tiltDegrees));
|
||||||
|
ray = rotateY(ray, radiansFromDegrees(panDegrees));
|
||||||
|
|
||||||
|
float halfFov = radiansFromDegrees(clamp(lensFovDegrees, 1.0, 220.0) * 0.5);
|
||||||
|
float theta = acos(clamp(ray.z, -1.0, 1.0));
|
||||||
|
if (theta > halfFov)
|
||||||
|
return outsideColor;
|
||||||
|
|
||||||
|
float phi = atan2(ray.y, ray.x);
|
||||||
|
float fisheyeRadius = normalizedFisheyeRadius(theta, halfFov);
|
||||||
|
|
||||||
|
float2 sourceUv = float2(
|
||||||
|
center.x + cos(phi) * fisheyeRadius * radius.x,
|
||||||
|
center.y - sin(phi) * fisheyeRadius * radius.y
|
||||||
|
);
|
||||||
|
|
||||||
|
if (sourceUv.x < 0.0 || sourceUv.x > 1.0 || sourceUv.y < 0.0 || sourceUv.y > 1.0)
|
||||||
|
return outsideColor;
|
||||||
|
|
||||||
|
return sampleVideo(sourceUv);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user