在canvas上绘制多边形并获取UV坐标

由于之前开发的视频融合功能允许进行画面裁剪。

而如果在场景中直接裁剪的话需要将裁剪点投影到地球表面上。

这就造成如果融合面是与地球表面垂直的,就无法进行裁剪。

一个比较通用的方式是利用UV坐标进行画面裁剪。

为了方便获取相应的UV坐标范围,所以编写了一个视频裁剪窗口。

通过在裁剪窗口上绘制/编辑多边形来计算相应的uv坐标。

记录一下关键代码和效果。

首先看看视频融合中的效果

绘制测试

关键代码

CanvasEdit 是一个能够在canvas上绘制/编辑多边形的方法。

并且,当多边形形状发生改变时,会自动计算多边形每个节点的UV坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
const parameter = {
fillStyle: '#8f8ffa40', // 面的填充颜色
strokeStyle: '#0063ff', // 面的边界颜色
radius: 4, // 点的大小
};

interface Point {
x: number;
y: number;
}

export class CanvasEdit {
private _points: Point[];

private _canvas: HTMLCanvasElement;

private _ctx: CanvasRenderingContext2D;

private _pickIndex: number | undefined;

private _draw: boolean;

public uvUpdateEvent: Event;

constructor(canvas: HTMLCanvasElement) {
this._points = [];
this._canvas = canvas;
canvas.oncontextmenu = (ev) => {
ev.preventDefault();
return false;
};
this._ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
this._canvas.addEventListener('mousedown', this._mousedownHandler.bind(this));
this._canvas.addEventListener('mouseup', () => {
this._pickIndex = undefined;
});
this._canvas.addEventListener('mousemove', this._mousemoveHandler.bind(this));
this._draw = false;
this._pickIndex = undefined;
}

// 开始绘制模式
draw() {
this.clear();
this._draw = true;
this._pickIndex = undefined;
}

// 开始编辑状态
edit() {
this._draw = false;
}

// 清除点
clear() {
this._points = [];
this._update();
}

// 通过UV坐标集合更新点
setPointsRorUvs(uvs: number[][]) {
this._points = [];
uvs.forEach((uv) => {
this._points.push({
x: uv[0] * this._canvas.width,
y: this._canvas.height - uv[1] * this._canvas.height,
});
});
this._update();
}

private _mousemoveHandler(event) {
if (this._pickIndex !== undefined) {
this._points[this._pickIndex] = {
x: event.offsetX,
y: event.offsetY,
};
this._update();
}
}

private _mousedownHandler(event) {
if (event.button === 0) {
if (this._draw) {
// 如果处于绘制状态则左键按下为绘制点
this._points.push({
x: event.offsetX,
y: event.offsetY,
});
this._update();
} else {
// 如果不处于绘制状态,则左键按下为编辑点
for (let i = 0; i < this._points.length; i++) {
const x = this._points[i].x - event.offsetX;
const y = this._points[i].y - event.offsetY;
const distance = Math.sqrt(x * x + y * y);
if (distance <= parameter.radius) {
this._pickIndex = i;
break;
}
}
}
}
}

private _update() {
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);

if (this._points.length > 0) {
this._ctx.fillStyle = parameter.fillStyle;
this._ctx.strokeStyle = parameter.strokeStyle;

// 绘制面和边界
this._ctx.beginPath();
this._ctx.moveTo(this._points[0].x, this._points[0].y);
for (let i = 1; i < this._points.length; i++) {
this._ctx.lineTo(this._points[i].x, this._points[i].y);
}
this._ctx.closePath();
this._ctx.stroke();
this._ctx.fill();

// 绘制节点
this._points.forEach((point) => {
this._ctx.beginPath();
this._ctx.arc(point.x, point.y, parameter.radius, 0, 2 * Math.PI);
this._ctx.stroke();
});
}
console.log('uv集合:', this._getUvForPoints());
}

private _getUvForPoints() {
return this._points.map((p) => [
p.x / this._canvas.width,
(this._canvas.height - p.y) / this._canvas.height,
]);
}
}

使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const cvs = document.getElementById("cvs");
cvs.width = window.innerWidth;
cvs.height = window.innerHeight;
window.addEventListener("resize", () => {
cvs.width = window.innerWidth;
cvs.height = window.innerHeight;
});
const canvasEdit = new CanvasEdit(cvs);

function draw() {
canvasEdit.draw();
}

function edit() {
canvasEdit.edit();
}

function clear() {
canvasEdit.clear();
}

document.getElementById('drawButton').addEventListener( 'click' , draw);
document.getElementById('editButton').addEventListener( 'click' , edit);
document.getElementById('clearButton').addEventListener( 'click' , clear);

在canvas上绘制多边形并获取UV坐标
https://www.liaomz.top/2022/03/15/zai-canvas-shang-hui-zhi-duo-bian-xing-bing-huo-qu-uv-zuo-biao/
作者
发布于
2022年3月15日
许可协议