使用微信自带的 VisionKit
API提供 AR 能力。官方手册地址: VisionKit 基础
虽然官方提供了 demo
代码,但是埋藏的暗坑还是不少。特此总结一下。
DEMO案例
待添加
逻辑流程
大致流程如下: 用户访问 AR 页面,程序进行初始化,通过 VKSession
获取到摄像头数据,并将图片传到后端进行识别,获得目标物体坐标信息,随后在目标位置放置 3D 模型。
流程细节如下:
- 初始化 canvas 尺寸,设置对应设备像素密度下的宽高信息。
- 初始化 threejs 。
- 初始化 scene、 camera、 light。threejs 库需要使用微信官方提供的
threejs-miniprogram
。使用了three-platformize
发现摄像头数据无法上屏。 - 初始化 GLTFLoader。 GLTFLoader 加载有贴图模型会有问题,因为微信小程序环境不支持 Blob 相关 API,若要支持需要自己实现。
- 初始化 WebGL。
initGL
,这一步设置好片元着色器和顶点着色器,让摄像头的数据渲染到webgl 画布上。(官方提供的着色器代码在我们场景有bug)
- 初始化 scene、 camera、 light。threejs 库需要使用微信官方提供的
- 初始化 VKSession。在 requestAnimationFrame 中获取帧数据,进行动态渲染画面。执行 render(frame)。
- 在 render 函数中:
- 同步摄像头位置信息,需要对 VKSession 的矩阵进行逆矩阵求导。并把位置信息同步给 threejs 的摄像头,来保证 3D 模型的方向跟踪手机移动的方向。
- 获取当前帧图片,上传到后台进行识别
- 读取 webgl 像素信息,提供 canvasPutImageData 绘制到一个不可见的 canvas。
- 使用 canvasToTempFilePath 和 getFileSystemManager 将 canvas 上的图像转化成base64。
- 调用识别接口
- 识别成功,载入3D模型
- 识别不成功,等待一段时间后继续尝试。
主要问题
- webgl 图片获取
- 图片像素信息获取
- 图片翻转问题
- 图片转化成base64
- VKSession 模型穿模问题
- VKSession 摄像头数据渲染问题
- threejs-miniprogram gltf 贴图模型加载问题
- 模型动画导出异常问题
解决方案
webgl 图片获取
主要问题:
- 获取到的数据绘制出来后上下颠倒需要对像素信息进行翻转
- iOS 设备开启抗锯齿后获取数据是100%是纯黑色
由于使用 VKSession 后,手机摄像头的数据在 VKFrame 中,而且无法再次创建 camera 组件。所以我们必须利用 VKFrame
中的数据来获取摄像头的图像。 摄像头的上屏逻辑在 renderGL
方法中。此时,图像已经被渲染到页面的 webgl
组件上,我们需要把 webgl
的渲染帧读取下来。
threejs 中截图源码为 https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLRenderer.js#L1903
if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) {
// the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604)
if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) {
_gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer );
}
} else {
console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' );
}
核心代码如下
// 这里是核心步骤,获取 webgl 的像素信息
const gl = this.gl;
const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
const pixels = new Uint8Array(width * height * 4);
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
// 翻转Y轴
flip(pixels, width, height, 4);
图片转化成base64
获取到需要的像素数据后, 就可以绘制到 canvas
上( canvasPutImageData
方法),然后将 canvas
导出成 base64 图片( canvasToTempFilePath
+ getFileSystemManager
)了。
const frame = this.canvas;
const gl = this.gl;
const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
const pixels = new Uint8Array(width * height * 4);
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
flip(pixels, width, height, 4);
wx.canvasPutImageData(
{
canvasId: "myCanvas",
data: new Uint8ClampedArray(typedArrayToBuffer(pixels)),
x: 0,
y: 0,
width: frame.width,
height: frame.height,
success: (res) => {
// 图片保存到 canvas
this.save(frame).then((base64) => {
reslove(base64);
});
},
fail(res) {
console.log(res);
}
}
);
save(frame) {
return wx
.canvasToTempFilePath({
x: 0,
y: 0,
width: frame.width,
height: frame.height,
canvasId: "myCanvas",
fileType: "jpg",
destWidth: frame.width,
destHeight: frame.height,
// 精度修改
quality: 0.6
})
.then(
(res) => {
// 临时文件转base64
return new Promise((reslove, reject) => {
wx.getFileSystemManager().readFile({
filePath: res.tempFilePath, //选择图片返回的相对路径
encoding: "base64", //编码格式
success: (res) => {
// 保存base64
reslove(res.data);
},
fail: (error) => {
reject(error);
}
});
});
},
(tempError) => {
console.log(tempError);
wx.showToast({
title: "图片生成失败,重新检测",
icon: "none",
duration: 1000
});
}
);
}
穿模问题
官方提供的 renderGL
默认关闭了深度检测,会导致 3D 模型穿模 ,所以需要注释 gl.disable(gl.DEPTH_TEST);
。
renderGL(frame) {
const gl = this.renderer.getContext();
// gl.disable(gl.DEPTH_TEST);
const { yTexture, uvTexture } = frame.getCameraTexture(gl, "yuv");
const displayTransform = frame.getDisplayTransform();
...
}
摄像头数据渲染问题
由于开启了深度检测,导致着色器代码有bug,会在机型上展示黑条或者雪花。解决方案如下。修改着色器代码如下
const vs = `
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform mat3 displayTransform;
varying vec2 v_texCoord;
void main() {
vec3 p = displayTransform * vec3(a_position, 0);
gl_Position = vec4(p.x, p.y, -1, 1);
v_texCoord = a_texCoord;
}
`;
以下方案未尝试,也可以试试看。
VKSession官方demo兼容性,在华为p30 pro或者小米11 会出现左上角花瓶区域?文章来源:https://www.toymoban.com/news/detail-433118.html
gltf 贴图模型加载问题
由于小程序环境不支持Blob 和 URL 对象,所以参考 three-platformize 项目的 loader,实现对应的api即可。文章来源地址https://www.toymoban.com/news/detail-433118.html
到了这里,关于小程序AR踩坑记录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!