mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2026-10-28 10:04:37 +08:00
avfilter/v360_vulkan: support GoPro Max inputs
Add GoPro Max 360 projection for input. Add tentative Equi-Angular Cubemap (EAC) projection for output (for testing). Signed-off-by: TADANO Tokumei <aimingoff@pc.nifty.jp>
This commit is contained in:
1 parent
cc6003d5f5
commit
dce56e75eb
4 files changed
+322
-42
No files matched your search
@@ -560,7 +560,7 @@ OBJS-$(CONFIG_UNSHARP_OPENCL_FILTER) += vf_unsharp_opencl.o opencl.o \
|
||||
OBJS-$(CONFIG_UNTILE_FILTER) += vf_untile.o
|
||||
OBJS-$(CONFIG_USPP_FILTER) += vf_uspp.o qp_table.o
|
||||
OBJS-$(CONFIG_V360_FILTER) += vf_v360.o
|
||||
OBJS-$(CONFIG_V360_VULKAN_FILTER) += vf_v360_vulkan.o
|
||||
OBJS-$(CONFIG_V360_VULKAN_FILTER) += vf_v360_vulkan.o framesync.o
|
||||
OBJS-$(CONFIG_VAGUEDENOISER_FILTER) += vf_vaguedenoiser.o
|
||||
OBJS-$(CONFIG_VARBLUR_FILTER) += vf_varblur.o framesync.o
|
||||
OBJS-$(CONFIG_VECTORSCOPE_FILTER) += vf_vectorscope.o
|
||||
|
||||
@@ -55,6 +55,7 @@ enum Projections {
|
||||
ORTHOGRAPHIC,
|
||||
OCTAHEDRON,
|
||||
CYLINDRICALEA,
|
||||
GOPROMAX,
|
||||
NB_PROJECTIONS,
|
||||
};
|
||||
|
||||
|
||||
+155
-39
@@ -22,6 +22,7 @@
|
||||
#include "v360.h"
|
||||
#include "filters.h"
|
||||
#include "video.h"
|
||||
#include "framesync.h"
|
||||
|
||||
extern const unsigned char ff_v360_comp_spv_data[];
|
||||
extern const unsigned int ff_v360_comp_spv_len;
|
||||
@@ -35,6 +36,7 @@ struct PushData {
|
||||
|
||||
typedef struct V360ulkanContext {
|
||||
FFVulkanContext vkctx;
|
||||
FFFrameSync fs;
|
||||
|
||||
int initialized;
|
||||
FFVkExecPool e;
|
||||
@@ -51,6 +53,7 @@ typedef struct V360ulkanContext {
|
||||
float yaw, pitch, roll;
|
||||
char *rorder;
|
||||
int rotation_order[3];
|
||||
int overlap;
|
||||
} V360VulkanContext;
|
||||
|
||||
static int get_rorder(char c)
|
||||
@@ -198,6 +201,7 @@ static void config_params(AVFilterContext *ctx, AVFilterLink *inlink)
|
||||
s->iv_fov = 180.f;
|
||||
break;
|
||||
case EQUIRECTANGULAR: /* unchangeable */
|
||||
case GOPROMAX:
|
||||
s->ih_fov = 360.f;
|
||||
s->iv_fov = 180.f;
|
||||
break;
|
||||
@@ -335,6 +339,9 @@ static av_cold int calculate_output_size(AVFilterContext *ctx)
|
||||
case DUAL_FISHEYE:
|
||||
hf = wf * sar / 2.f;
|
||||
break;
|
||||
case EQUIANGULAR:
|
||||
hf = wf * sar / 3.f * 2.f;
|
||||
break;
|
||||
case STEREOGRAPHIC:
|
||||
case FISHEYE:
|
||||
hf = wf * sar;
|
||||
@@ -353,6 +360,9 @@ static av_cold int calculate_output_size(AVFilterContext *ctx)
|
||||
case FLAT:
|
||||
hf = (float)inlink->h / s->pd.iflat_range[1] / 2.f;
|
||||
break;
|
||||
case GOPROMAX:
|
||||
hf = (float)inlink->h * 2.f;
|
||||
break;
|
||||
default:
|
||||
hf = (float)inlink->h;
|
||||
break;
|
||||
@@ -376,6 +386,9 @@ static av_cold int calculate_output_size(AVFilterContext *ctx)
|
||||
case DUAL_FISHEYE:
|
||||
wf = hf * 2.f;
|
||||
break;
|
||||
case EQUIANGULAR:
|
||||
wf = hf * 3.f / 2.f;
|
||||
break;
|
||||
case STEREOGRAPHIC:
|
||||
case FISHEYE:
|
||||
wf = hf;
|
||||
@@ -404,6 +417,10 @@ static av_cold int calculate_output_size(AVFilterContext *ctx)
|
||||
min_w = 2;
|
||||
min_h = 1;
|
||||
break;
|
||||
case EQUIANGULAR:
|
||||
min_w = 3;
|
||||
min_h = 2;
|
||||
break;
|
||||
default:
|
||||
min_w = 1;
|
||||
min_h = 1;
|
||||
@@ -438,7 +455,7 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in)
|
||||
|
||||
RET(ff_vk_exec_pool_init(vkctx, s->qf, &s->e, FF_VK_DEFAULT_EXEC_CONTEXTS, 0, 0, 0, NULL));
|
||||
|
||||
SPEC_LIST_CREATE(sl, 10, 7*sizeof(int) + 3*sizeof(float))
|
||||
SPEC_LIST_CREATE(sl, 17, 14*sizeof(int) + 3*sizeof(float))
|
||||
SPEC_LIST_ADD(sl, 0, 32, s->out);
|
||||
SPEC_LIST_ADD(sl, 1, 32, s->in);
|
||||
|
||||
@@ -453,6 +470,19 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in)
|
||||
SPEC_LIST_ADD(sl, 8, 32, FF_CEIL_RSHIFT(in->width, desc->log2_chroma_w));
|
||||
SPEC_LIST_ADD(sl, 9, 32, FF_CEIL_RSHIFT(in->height, desc->log2_chroma_h));
|
||||
|
||||
if (s->in == GOPROMAX) {
|
||||
int cube_size = in->height;
|
||||
int gopro_cube_width = (in->width - cube_size) / 2;
|
||||
|
||||
SPEC_LIST_ADD(sl, 10, 32, cube_size);
|
||||
SPEC_LIST_ADD(sl, 11, 32, gopro_cube_width);
|
||||
SPEC_LIST_ADD(sl, 12, 32, s->overlap);
|
||||
SPEC_LIST_ADD(sl, 13, 32, gopro_cube_width + cube_size);
|
||||
SPEC_LIST_ADD(sl, 14, 32, gopro_cube_width / 2 - s->overlap);
|
||||
SPEC_LIST_ADD(sl, 15, 32, gopro_cube_width / 2);
|
||||
SPEC_LIST_ADD(sl, 16, 32, gopro_cube_width / 2 + s->overlap);
|
||||
}
|
||||
|
||||
ff_vk_shader_load(&s->shd, VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
sl, (uint32_t []) { 16, 16, 1 }, 0);
|
||||
|
||||
@@ -460,19 +490,25 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in)
|
||||
VK_SHADER_STAGE_COMPUTE_BIT);
|
||||
|
||||
const FFVulkanDescriptorSetBinding desc_set[] = {
|
||||
{ /* output_img */
|
||||
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.stages = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.elems = planes,
|
||||
},
|
||||
{ /* input_img */
|
||||
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.stages = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.elems = planes,
|
||||
.samplers = DUP_SAMPLER(s->sampler),
|
||||
},
|
||||
{ /* output_img */
|
||||
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.stages = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.elems = planes,
|
||||
{ /* input2_img */
|
||||
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.stages = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.elems = planes,
|
||||
.samplers = DUP_SAMPLER(s->sampler),
|
||||
},
|
||||
};
|
||||
ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc_set, 2, 0);
|
||||
ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc_set, 3, 0);
|
||||
|
||||
RET(ff_vk_shader_link(vkctx, &s->shd,
|
||||
ff_v360_comp_spv_data,
|
||||
@@ -486,13 +522,20 @@ fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int v360_vulkan_filter_frame(AVFilterLink *link, AVFrame *in)
|
||||
static int v360_vulkan_filter_frame(FFFrameSync *fs)
|
||||
{
|
||||
int err;
|
||||
AVFrame *out = NULL;
|
||||
AVFilterContext *ctx = link->dst;
|
||||
V360VulkanContext *s = ctx->priv;
|
||||
AVFilterContext *ctx = fs->parent;
|
||||
V360VulkanContext *s = ctx->priv;
|
||||
AVFilterLink *outlink = ctx->outputs[0];
|
||||
AVFrame *in[2] = { NULL };
|
||||
AVFrame *out = NULL;
|
||||
|
||||
for (int i = 0; i < ctx->nb_inputs; i++) {
|
||||
RET(ff_framesync_get_frame(fs, i, &in[i], 0));
|
||||
if (!in[i])
|
||||
return 0;
|
||||
}
|
||||
|
||||
out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
|
||||
if (!out) {
|
||||
@@ -500,27 +543,37 @@ static int v360_vulkan_filter_frame(AVFilterLink *link, AVFrame *in)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!s->initialized)
|
||||
RET(init_filter(ctx, in));
|
||||
if (!s->initialized) {
|
||||
AVHWFramesContext *fc0 = (AVHWFramesContext*)in[0]->hw_frames_ctx->data;
|
||||
for (int i = 1; i < ctx->nb_inputs; i++) {
|
||||
AVHWFramesContext *fci = (AVHWFramesContext*)in[i]->hw_frames_ctx->data;
|
||||
if (fc0->sw_format != fci->sw_format) {
|
||||
av_log(ctx, AV_LOG_ERROR, "Input %d has a different format "
|
||||
"from input 0\n", i);
|
||||
err = AVERROR(EINVAL);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
RET(init_filter(ctx, in[0]));
|
||||
}
|
||||
|
||||
RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd,
|
||||
out, in, s->sampler, 1,
|
||||
&s->pd, sizeof(s->pd)));
|
||||
|
||||
err = av_frame_copy_props(out, in);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
av_frame_free(&in);
|
||||
RET(ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->shd, out,
|
||||
in, ctx->nb_inputs, s->sampler, 1,
|
||||
&s->pd, sizeof(s->pd)));
|
||||
RET(av_frame_copy_props(out, in[0]));
|
||||
|
||||
return ff_filter_frame(outlink, out);
|
||||
|
||||
fail:
|
||||
av_frame_free(&in);
|
||||
av_frame_free(&out);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int v360_vulkan_activate(AVFilterContext *ctx)
|
||||
{
|
||||
V360VulkanContext *s = ctx->priv;
|
||||
return ff_framesync_activate(&s->fs);
|
||||
}
|
||||
|
||||
static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
|
||||
char *res, int res_len, int flags)
|
||||
@@ -539,13 +592,80 @@ static av_cold int v360_vulkan_config_output(AVFilterLink *outlink)
|
||||
{
|
||||
int err;
|
||||
AVFilterContext *ctx = outlink->src;
|
||||
V360VulkanContext *s = ctx->priv;
|
||||
AVFilterLink *inlink = ctx->inputs[0];
|
||||
|
||||
if (s->in == GOPROMAX) {
|
||||
if (s->overlap == 0) {
|
||||
if (inlink->h <= 960) {
|
||||
s->overlap = 32;
|
||||
}
|
||||
else if (inlink->h < 1920) {
|
||||
s->overlap = 64;
|
||||
}
|
||||
else
|
||||
s->overlap = 96;
|
||||
}
|
||||
if (ctx->nb_inputs != 2) {
|
||||
av_log(ctx, AV_LOG_ERROR, "GoPro Max. requires 2 input streams.\n");
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
if ((inlink->w != ctx->inputs[1]->w) ||
|
||||
(inlink->h != ctx->inputs[1]->h) ||
|
||||
(inlink->w < inlink->h * 3) ||
|
||||
(inlink->w > inlink->h * 3 + s->overlap * 2)) {
|
||||
av_log(ctx, AV_LOG_ERROR, "Incompatible inputs for GoPro Max. (%dx%d + %dx%d)\n",
|
||||
inlink->w, inlink->h, ctx->inputs[1]->w, ctx->inputs[1]->h);
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
}
|
||||
else if (ctx->nb_inputs != 1) {
|
||||
av_log(ctx, AV_LOG_ERROR, "Too many inputs (%d)\n", ctx->nb_inputs);
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
config_params(ctx, inlink);
|
||||
RET(calculate_output_size(ctx));
|
||||
|
||||
RET(ff_vk_filter_config_output(outlink));
|
||||
|
||||
RET(ff_framesync_init(&s->fs, ctx, ctx->nb_inputs));
|
||||
for (int i = 0; i < ctx->nb_inputs; i++) {
|
||||
s->fs.in[i].time_base = ctx->inputs[i]->time_base;
|
||||
s->fs.in[i].sync = 1;
|
||||
s->fs.in[i].before = EXT_STOP;
|
||||
s->fs.in[i].after = EXT_STOP;
|
||||
}
|
||||
s->fs.on_event = &v360_vulkan_filter_frame;
|
||||
|
||||
RET(ff_framesync_configure(&s->fs));
|
||||
outlink->time_base = s->fs.time_base;
|
||||
|
||||
fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static av_cold int v360_vulkan_init(AVFilterContext *avctx)
|
||||
{
|
||||
int err;
|
||||
V360VulkanContext *s = avctx->priv;
|
||||
AVFilterPad pad = {
|
||||
.type = AVMEDIA_TYPE_VIDEO,
|
||||
.config_props = ff_vk_filter_config_input,
|
||||
};
|
||||
|
||||
if (s->in == GOPROMAX) {
|
||||
pad.name = "front";
|
||||
RET(ff_append_inpad(avctx, &pad));
|
||||
pad.name = "rear";
|
||||
RET(ff_append_inpad(avctx, &pad));
|
||||
}
|
||||
else {
|
||||
pad.name = "default";
|
||||
RET(ff_append_inpad(avctx, &pad));
|
||||
}
|
||||
|
||||
return ff_vk_filter_init(avctx);
|
||||
|
||||
fail:
|
||||
return err;
|
||||
}
|
||||
@@ -556,6 +676,7 @@ static void v360_vulkan_uninit(AVFilterContext *avctx)
|
||||
FFVulkanContext *vkctx = &s->vkctx;
|
||||
FFVulkanFunctions *vk = &vkctx->vkfn;
|
||||
|
||||
ff_framesync_uninit(&s->fs);
|
||||
ff_vk_exec_pool_free(vkctx, &s->e);
|
||||
ff_vk_shader_free(vkctx, &s->shd);
|
||||
|
||||
@@ -579,10 +700,12 @@ static const AVOption v360_vulkan_options[] = {
|
||||
{ "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "in" },
|
||||
{ "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "in" },
|
||||
{ "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, "in" },
|
||||
{ "gopromax", "GoPro Max (two inputs)", 0, AV_OPT_TYPE_CONST, {.i64=GOPROMAX}, 0, 0, FLAGS, "in" },
|
||||
|
||||
{ "output", "set output projection", OFFSET(out), AV_OPT_TYPE_INT, {.i64=FLAT}, 0, NB_PROJECTIONS-1, FLAGS, "out" },
|
||||
{ "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" },
|
||||
{ "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" },
|
||||
{ "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "out" },
|
||||
{ "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
|
||||
{ "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "out" },
|
||||
{ "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "out" },
|
||||
@@ -598,26 +721,18 @@ static const AVOption v360_vulkan_options[] = {
|
||||
{ "v_fov", "set output vertical FOV angle", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, 0.0f, 360.0f, DYNAMIC, "v_fov" },
|
||||
{ "ih_fov", "set input horizontal FOV angle", OFFSET(ih_fov), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, 0.0f, 360.0f, DYNAMIC, "ih_fov" },
|
||||
{ "iv_fov", "set input vertical FOV angle", OFFSET(iv_fov), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, 0.0f, 360.0f, DYNAMIC, "iv_fov" },
|
||||
{ "overlap", "overlapped pixels for GoPro Max", OFFSET(overlap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1024, FLAGS, "overlap" },
|
||||
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
AVFILTER_DEFINE_CLASS(v360_vulkan);
|
||||
|
||||
static const AVFilterPad v360_vulkan_inputs[] = {
|
||||
{
|
||||
.name = "default",
|
||||
.type = AVMEDIA_TYPE_VIDEO,
|
||||
.filter_frame = &v360_vulkan_filter_frame,
|
||||
.config_props = &ff_vk_filter_config_input,
|
||||
},
|
||||
};
|
||||
FRAMESYNC_DEFINE_CLASS(v360_vulkan, V360VulkanContext, fs);
|
||||
|
||||
static const AVFilterPad v360_vulkan_outputs[] = {
|
||||
{
|
||||
.name = "default",
|
||||
.type = AVMEDIA_TYPE_VIDEO,
|
||||
.config_props = &v360_vulkan_config_output,
|
||||
.config_props = v360_vulkan_config_output,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -625,13 +740,14 @@ const FFFilter ff_vf_v360_vulkan = {
|
||||
.p.name = "v360_vulkan",
|
||||
.p.description = NULL_IF_CONFIG_SMALL("Convert 360 projection of video."),
|
||||
.p.priv_class = &v360_vulkan_class,
|
||||
.p.flags = AVFILTER_FLAG_HWDEVICE,
|
||||
.p.flags = AVFILTER_FLAG_HWDEVICE | AVFILTER_FLAG_DYNAMIC_INPUTS,
|
||||
.priv_size = sizeof(V360VulkanContext),
|
||||
.init = &ff_vk_filter_init,
|
||||
.uninit = &v360_vulkan_uninit,
|
||||
FILTER_INPUTS(v360_vulkan_inputs),
|
||||
.preinit = v360_vulkan_framesync_preinit,
|
||||
.init = v360_vulkan_init,
|
||||
.uninit = v360_vulkan_uninit,
|
||||
.activate = v360_vulkan_activate,
|
||||
FILTER_OUTPUTS(v360_vulkan_outputs),
|
||||
FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN),
|
||||
.flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
|
||||
.process_command = &process_command,
|
||||
.process_command = process_command,
|
||||
};
|
||||
@@ -25,10 +25,12 @@
|
||||
layout (local_size_x_id = 253, local_size_y_id = 254, local_size_z_id = 255) in;
|
||||
|
||||
#define EQUIRECTANGULAR 0
|
||||
#define EQUIANGULAR 3
|
||||
#define FLAT 4
|
||||
#define DUAL_FISHEYE 5
|
||||
#define STEREOGRAPHIC 8
|
||||
#define FISHEYE 13
|
||||
#define GOPROMAX 25
|
||||
layout (constant_id = 0) const int out_transform = 0;
|
||||
layout (constant_id = 1) const int in_transform = 0;
|
||||
|
||||
@@ -42,8 +44,17 @@ layout (constant_id = 7) const int in_h = 0;
|
||||
layout (constant_id = 8) const int in_chroma_w = 0;
|
||||
layout (constant_id = 9) const int in_chroma_h = 0;
|
||||
|
||||
layout (set = 0, binding = 0) uniform sampler2D input_img[];
|
||||
layout (set = 0, binding = 1) uniform writeonly image2D output_img[];
|
||||
layout (constant_id = 10) const int in_cube_size = 0;
|
||||
layout (constant_id = 11) const int gopro_cube_width = 0;
|
||||
layout (constant_id = 12) const int gopro_overlap = 0;
|
||||
layout (constant_id = 13) const int gopro_face_offset_2 = 0;
|
||||
layout (constant_id = 14) const int gopro_blend_x_0 = 0;
|
||||
layout (constant_id = 15) const int gopro_blend_x_1 = 0;
|
||||
layout (constant_id = 16) const int gopro_blend_x_2 = 0;
|
||||
|
||||
layout (set = 0, binding = 0) uniform writeonly image2D output_img[];
|
||||
layout (set = 0, binding = 1) uniform sampler2D input_img[];
|
||||
layout (set = 0, binding = 2) uniform sampler2D input2_img[];
|
||||
|
||||
layout (push_constant, scalar) uniform pushConstants {
|
||||
mat4 rot_mat;
|
||||
@@ -58,8 +69,27 @@ ivec2 in_img_size[4] = {
|
||||
ivec2(in_w, in_h)
|
||||
};
|
||||
|
||||
ivec3 gopro_face_offset_x = ivec3(0,
|
||||
gopro_cube_width,
|
||||
gopro_face_offset_2);
|
||||
ivec3 gopro_face_size_x = ivec3(gopro_cube_width,
|
||||
in_cube_size,
|
||||
gopro_cube_width);
|
||||
ivec3 gopro_blend_x = ivec3(gopro_blend_x_0,
|
||||
gopro_blend_x_1,
|
||||
gopro_blend_x_2);
|
||||
|
||||
#define IS_WITHIN(v1, v2) any(lessThan(v1, v2))
|
||||
|
||||
float atan2(in float y, in float x)
|
||||
{
|
||||
return abs(x) < 1e-6 ? sign(y) * m_pi2 : atan(y, x);
|
||||
}
|
||||
vec2 atan2(in vec2 v, in float z)
|
||||
{
|
||||
return abs(z) < 1e-6 ? sign(v) * m_pi2 : atan(v, vec2(z));
|
||||
}
|
||||
|
||||
void xyz_to_flat(uint idx, in vec3 v, in ivec2 pos, in ivec2 in_size)
|
||||
{
|
||||
const float r = tan(acos(v[2]));
|
||||
@@ -166,6 +196,133 @@ void xyz_to_dfisheye(uint idx, in vec3 v, in ivec2 pos, in ivec2 in_size)
|
||||
imageStore(output_img[idx], pos, res);
|
||||
}
|
||||
|
||||
// FIXME: no padding
|
||||
vec3 eac_to_xyz(in ivec2 out_size, in ivec2 pos)
|
||||
{
|
||||
const ivec2 cube_size = out_size / ivec2(3, 2);
|
||||
const ivec2 face = pos / cube_size;
|
||||
const vec2 e_uv = vec2(pos % cube_size) / vec2(cube_size);
|
||||
const int f = face.y * 3 + face.x;
|
||||
vec3 v;
|
||||
vec2 uv;
|
||||
|
||||
uv.x = (e_uv.x >= 1.0) ? 1.0 : tan((e_uv.x - 0.5) * m_pi2);
|
||||
uv.y = (e_uv.y >= 1.0) ? 1.0 : tan((e_uv.y - 0.5) * m_pi2);
|
||||
|
||||
// limited cube_to_xyz
|
||||
switch (f) {
|
||||
case 0:
|
||||
v = vec3(-1.0, uv.y, uv.x);
|
||||
break;
|
||||
case 1:
|
||||
v = vec3(uv.x, uv.y, 1.0);
|
||||
break;
|
||||
case 2:
|
||||
v = vec3(1.0, uv.y, -uv.x);
|
||||
break;
|
||||
case 3:
|
||||
v = vec3(-uv.y, 1.0, -uv.x);
|
||||
break;
|
||||
case 4:
|
||||
v = vec3(-uv.y, -uv.x, -1.0);
|
||||
break;
|
||||
case 5:
|
||||
v = vec3(-uv.y, -1.0, uv.x);
|
||||
break;
|
||||
}
|
||||
|
||||
return normalize(v);
|
||||
}
|
||||
|
||||
// face order : LEFT, FRONT, RIGHT, DOWN, BACK, UP
|
||||
vec3 xzy_to_eac_cube(in vec3 v)
|
||||
{
|
||||
vec3 a = abs(v);
|
||||
int face;
|
||||
vec3 view;
|
||||
vec2 uv;
|
||||
|
||||
if (a.x >= a.y && a.x >= a.z) {
|
||||
if (v.x >= 0.0) { // +X
|
||||
face = 2;
|
||||
view = vec3(-v.z, v.y, a.x);
|
||||
}
|
||||
else { // -X
|
||||
face = 0;
|
||||
view = vec3( v.z, v.y, a.x);
|
||||
}
|
||||
}
|
||||
else if (a.y >= a.z) {
|
||||
if (v.y >= 0.0) { // +Y
|
||||
face = 3;
|
||||
view = vec3(-v.z, -v.x, a.y);
|
||||
}
|
||||
else { // -Y
|
||||
face = 5;
|
||||
view = vec3( v.z, -v.x, a.y);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (v.z >= 0.0) { // +Z
|
||||
face = 1;
|
||||
view = vec3( v.x, v.y, a.z);
|
||||
}
|
||||
else { // -Z
|
||||
face = 4;
|
||||
view = vec3(-v.y, -v.x, a.z);
|
||||
}
|
||||
}
|
||||
uv = atan2(view.xy, view.z) / m_pi2 + 0.5;
|
||||
|
||||
return vec3(uv.x, uv.y, float(face));
|
||||
}
|
||||
|
||||
void xyz_to_gopro(uint idx, in vec3 v, in ivec2 pos, in ivec2 in_size)
|
||||
{
|
||||
vec3 uvf = xzy_to_eac_cube(v);
|
||||
vec2 uv = vec2(uvf.xy);
|
||||
ivec2 grid = ivec2(int(uvf.z) % 3, int(uvf.z) / 3);
|
||||
vec3 face_offset = vec3(gopro_face_offset_x) / float(in_img_size[0].x);
|
||||
vec2 face_size = vec2(float(gopro_face_size_x[grid.x]) / float(in_img_size[0].x), 1.0);
|
||||
vec2 uv_start = vec2(face_offset[grid.x], 0.0);
|
||||
vec2 uv_end = uv_start + face_size;
|
||||
vec4 res;
|
||||
|
||||
if (grid.x == 1) {
|
||||
res = (grid.y == 0) ? texture(input_img[idx], mix(uv_start, uv_end, uv))
|
||||
: texture(input2_img[idx], mix(uv_start, uv_end, uv));
|
||||
} else {
|
||||
float cube_width = float(gopro_cube_width);
|
||||
float overlap = float(gopro_overlap);
|
||||
float gratio = (cube_width - overlap) / cube_width;
|
||||
float blend_width = overlap / (cube_width - overlap);
|
||||
vec3 blend_u = vec3(gopro_blend_x) / cube_width;
|
||||
vec2 uv1 = vec2(uv.x * gratio, uv.y);
|
||||
vec2 uv2 = vec2((uv.x + blend_width) * gratio, uv.y);
|
||||
if (uv1.x < blend_u[0]) {
|
||||
res = (grid.y == 0) ? texture(input_img[idx], mix(uv_start, uv_end, uv1))
|
||||
: texture(input2_img[idx], mix(uv_start, uv_end, uv1));
|
||||
} else if (uv2.x > blend_u[2]) {
|
||||
res = (grid.y == 0) ? texture(input_img[idx], mix(uv_start, uv_end, uv2))
|
||||
: texture(input2_img[idx], mix(uv_start, uv_end, uv2));
|
||||
} else {
|
||||
vec4 res2;
|
||||
if (grid.y == 0) {
|
||||
res = texture(input_img[idx], mix(uv_start, uv_end, uv1));
|
||||
res2 = texture(input_img[idx], mix(uv_start, uv_end, uv2));
|
||||
}
|
||||
else {
|
||||
res = texture(input2_img[idx], mix(uv_start, uv_end, uv1));
|
||||
res2 = texture(input2_img[idx], mix(uv_start, uv_end, uv2));
|
||||
}
|
||||
float a = (uv1.x - blend_u[0]) / (blend_u[1] - blend_u[0]);
|
||||
res = mix(res, res2, a);
|
||||
}
|
||||
}
|
||||
|
||||
imageStore(output_img[idx], pos, res);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
|
||||
@@ -179,6 +336,9 @@ void main()
|
||||
case EQUIRECTANGULAR:
|
||||
v = equirect_to_xyz(out_size, pos);
|
||||
break;
|
||||
case EQUIANGULAR:
|
||||
v = eac_to_xyz(out_size, pos);
|
||||
break;
|
||||
case FLAT:
|
||||
v = flat_to_xyz(out_size, pos);
|
||||
break;
|
||||
@@ -211,6 +371,9 @@ void main()
|
||||
case FISHEYE:
|
||||
xyz_to_fisheye(i, v, pos, in_size);
|
||||
break;
|
||||
case GOPROMAX:
|
||||
xyz_to_gopro(i, v, pos, in_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user