0%

视频滤镜

背景

1
2
3
在公司中做webar(three.js+ar.js)的时候提了一个需求希望对场景提亮,使得展示的产品看起来有点感觉,
最开始想的是使用canvas获取像素数据进行处理,但是性能会比较低,如果直接使用webgl也挺麻烦的,
而p5.js 对webgl进行了封装,可以非常简单的上手

所用技术

1
webrtc + p5.js

在线预览

所有代码

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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>视频滤镜</title>
<link rel="stylesheet" href="./css/element.css" />
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
video {
display: none;
opacity: 0;
}
</style>
</head>
<body>
<div id="app">
<el-select @change="setFilter" v-model="curFilter" placeholder="请选择">
<el-option
v-for="item in filters"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
<video ref="myVideo" autoplay playsinline muted></video>
</div>
<script src="./js//qrcode.js"></script>
<script src="./js//vue.js"></script>
<script src="./js//element.js"></script>
<script src="./js/5.3.3/pixi.min.js"></script>
<script>
// filters
const filters = {
def: null,
// 灰度
grey: {
fragmentShader: `
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
const vec3 W = vec3(0.2125, 0.7154, 0.0721);
void main(void) {
vec4 color = texture2D(uSampler, vTextureCoord);
float temp = dot(color.rgb, W);
gl_FragColor = vec4(vec3(temp), 1.0);
}
`,
},
// 高斯模糊
blur: {
fragmentShader: `
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
uniform vec2 uResolution; // 纹理分辨率
uniform float uBlurRadius; // 模糊半径

// 高斯函数
float gaussian(float x, float sigma) {
return exp(-(x * x) / (2.0 * sigma * sigma)) / (2.0 * 3.14159 * sigma * sigma);
}

void main(void) {
// 在此处添加自定义的着色器逻辑
vec2 texelSize = 1.0 / uResolution;
float sigma = uBlurRadius; // 高斯函数的标准差

vec4 colorSum = vec4(0.0);
float weightSum = 0.0;

// 对周围像素进行采样和加权平均
for (int i = -5; i <= 5; i++) {
for (int j = -5; j <= 5; j++) {
vec2 offset = vec2(float(i), float(j)) * texelSize;
vec4 sampleColor = texture2D(uSampler, vTextureCoord + offset);

float weight = gaussian(float(i), sigma) * gaussian(float(j), sigma);
colorSum += sampleColor * weight;
weightSum += weight;
}
}

// 归一化颜色和权重
vec4 blurredColor = colorSum / weightSum;

gl_FragColor = blurredColor;
}
`,
uniforms: {
uResolution: [800, 600],
uBlurRadius: 10,
},
},
// 场景提亮
brighten: {
fragmentShader: `
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
uniform float strength;
void main(void) {
// 在此处添加自定义的着色器逻辑
vec4 color = texture2D(uSampler, vTextureCoord);
color.rgb += strength;
gl_FragColor = color;
}
`,
uniforms: {
strength: 0.1,
},
},
// 反色
inverseColor: {
fragmentShader: `
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
void main(void) {
// 在此处添加自定义的着色器逻辑
vec4 color = texture2D(uSampler, vTextureCoord);
gl_FragColor = vec4(1.-color.rgb,1.);
}
`,
},
};

// webrtc option
const constraints = {
video: {
facingMode: {
ideal: "environment",
},
},
audio: false,
};

const vue = new Vue({
el: "#app",
data: function () {
this.filters = Object.keys(filters).map((it) => {
return {
label: it,
value: it,
};
});
return {
curFilter: "",
};
},
mounted() {
window.ins = this;
this.init();
},
computed: {
videoEl() {
return this.$refs["myVideo"];
},
},
methods: {
setFilter(key) {
// 修改不同的滤镜 或者结合不同的滤镜
if (!filters[key]) {
this.curFilter = "def";
this.videoSprite.filters = [];
return;
}
this.curFilter = key;
const filter = filters[key];
const shader = new PIXI.Filter(
null,
filter.fragmentShader,
filter.uniforms || {}
);
this.videoSprite.filters = [shader];
},
async init() {
// webrtc 从摄像机获取视频流
const localStream = await navigator.mediaDevices
.getUserMedia(constraints)
.catch((err) => console.log(err));
// 本地视频流
this.videoEl.srcObject = localStream;

// 初始化 p5.js
this.app = new PIXI.Application({
// width,height
});
// 将渲染器添加到页面中的一个元素
this.$el.appendChild(this.app.view);
// 创建 video 贴图
const videoTexture = PIXI.Texture.fromVideo(this.videoEl);
const videoSprite = new PIXI.Sprite(videoTexture);
this.videoSprite = videoSprite;
videoSprite.x = 0;
videoSprite.y = 0;
videoSprite.width = this.app.screen.width;
videoSprite.height = this.app.screen.height;
this.setFilter("inverseColor");

// 将视频精灵添加到舞台
this.app.stage.addChild(videoSprite);
},
},
});
</script>
</body>
</html>