参加了一个大创项目,要在小游戏上实现AR效果,奈何本人太菜,疯狂跳进坑里,在这里记录一下自己遇到的坑,希望能帮助到刚刚开始尝试的朋友,也希望能有路过的大佬救救还在坑里的我QAQ
1.小游戏调用wx.creatcamera()打开摄像头
let camera =wx.createCamera({
x: 0,
y: 0,
width: 500,
height: 500,
flash: "off",
size: "medium",
//devicePosition:"front",
success: function () {
console.log("摄像头打开成功");
},
fail: function (res) {
console.log(res);
console.log("摄像头打开失败");
}
});
在模拟器上是打不开摄像头的,真机调试可以打开。
2.一定要先开始监听帧数据才能获得帧数据
camera.listenFrameChange();//开始监听帧数据
var listener = camera.onCameraFrame((frame) => {
if (count < 0) {
var data = new Uint8ClampedArray(frame.data)
var picurl = that.uinttourl(data, frame.width, frame.height);
this.bg.loadImage(picurl);
count = 10;//每十帧才渲染一次
}
count -= 1;
});
3.因为微信的相机画面一定是在最顶层,而实现AR功能又必须使模型显示在相机上层,因此需要把相机组件的大小设为1*1并利用帧数据渲染相机画面。从相机拿到的帧数据frame.data是像素数组,我需要texture格式或者图片url格式才能把相机画面渲染到屏幕上,开始时我尝试把uint8array转base64,但是按照网上的方法(类似String.fromCharCode.apply(null, slice);这样的),都没有成功,苦恼了两天,最终尝试的网上一位大佬的方法得到下面这段代码,成功得到图片地址。
uinttourl(data, w, h) {
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
let width = canvas.width = w;
let height = canvas.height = h;
let rowBytes = width * 4;
for (let row = 0; row < height; row++) {
let imageData = ctx.createImageData(width, 1);
let start = row * width * 4;
for (let i = 0; i < rowBytes; i++) {
imageData.data[i] = data[start + i];
}
ctx.putImageData(imageData, 0, row);
}
let dataURL = canvas.toDataURL("image/jpeg",0.1);
return dataURL;
}
第一次渲染出来相机画面的时候我真的很激动,然而事实是我又跳进了一个大坑里,通过uinttourl(data, w, h)这个方法获得url确实能渲染出相机帧,但是出来的画面非!常!卡!如上面第二段代码,我需要设置每十帧渲染一次才能基本保证画面不黑屏(但还是会有闪屏),当我以更高的频率渲染时就会全程黑屏。经过排查,主要问题不是loadimage(),也不是因为图片的url是base64格式的。问题应该出在uinttourl(data, w, h)中,但是我现在也不确定到底是因为for循环太耗时还是canvas.toDataURL太耗时还是因为我不会处理内存。
今天调了一天毫无进度,绝望之余写下这篇记录,如果有好心的大佬路过,希望能够给点建议救救孩子~
2021年3月6日更新
看了网上很多人说webgl渲染比canvas染快很多,所以今天尝试了把微信官方提供的小程序AR示例代码移植到小游戏,主要是用到了他的webgl配置(真的不会自己写着色器之类的ORZ),成功把相机的帧数据通过webgl方式画到了canvas,然后还是通过canvas.toDataURL转成图片url,按照昨天的方法加载到spirit上,结果画面还是很卡,得出结论:昨天的画面卡并不是因为canvas渲染太耗时。
再又尝试了几种方法无效后,我试图从spirit下手,我用的引擎是laya,认真地读了官方文档后发现了spirit的cacheAs这个属性
虽然也不是很看得懂,但是看见了“渲染性能最高”几个字,决定一试,把cacheAs设为了“bitmap”后,奇迹出现了!渲染出来的画面没有闪屏了!虽然还是不能做到每帧都渲染,但是看起来基本上算是流畅了。
最后放一张效果图