WeChat 独自のVisionKit
API を使用して AR 機能を提供します。公式マニュアルのアドレス: VisionKit Basics
公式コードが提供されていますがdemo
、まだ多くの隠れた穴があります。概要は次のとおりです。
デモケース
追加予定
論理的な流れ
一般的なプロセスは次のとおりです。ユーザーが AR ページにアクセスし、プログラムが初期化され、カメラVKSession
データが取得され、画像が認識のためにバックエンドに送信され、対象オブジェクトの座標情報が取得され、3D が生成されます。モデルがターゲットの位置に配置されます。
プロセスの詳細は次のとおりです。
- キャンバスサイズを初期化し、デバイスのピクセル密度に対応する幅と高さの情報を設定します。
- threejsを初期化します。
- シーン、カメラ、ライトを初期化します。threejs ライブラリは WeChat によって提供される必要があります
threejs-miniprogram
。使ってみたところ、three-platformize
カメラのデータを画面に表示できないことが分かりました。 - GLTFLoaderを初期化します。WeChat アプレット環境は Blob 関連の API をサポートしていないため、GLTFLoader がテクスチャ モデルをロードするときに問題が発生します。これをサポートしたい場合は、自分で実装する必要があります。
- WebGLを初期化します。
initGL
, このステップでは、WebGL キャンバスにカメラ データをレンダリングするためにフラグメント シェーダーと頂点シェーダーを設定します。(公式に提供されているシェーダー コードにはシーンにバグがあります)
- シーン、カメラ、ライトを初期化します。threejs ライブラリは WeChat によって提供される必要があります
- VKSessionを初期化します。requestAnimationFrameでフレームデータを取得し、画像を動的にレンダリングします。レンダリング(フレーム)を実行します。
- レンダリング関数では次のようになります。
- カメラの位置情報を同期するには、VKSession 行列の逆行列導出が必要です。また、位置情報をthreejsカメラと同期させて、3Dモデルの方向が携帯電話の動きの方向を確実に追跡するようにします。
- 現在のフレーム画像を取得し、認識のために背景にアップロードします。
- WebGL ピクセル情報を読み取り、canvasPutImageData を提供して、非表示のキャンバスに描画します。
- CanvasToTempFilePath と getFileSystemManager を使用して、キャンバス上の画像を Base64 に変換します。
- 通話識別インターフェイス
- 認識成功、3Dモデル読み込み中
- 認識に失敗した場合は、しばらく待ってから再試行してください。
主な問題
- webgl画像取得
- 画像のピクセル情報を取得する
- 画像反転問題
- 画像をbase64に変換します
- VKSession モデルの摩耗の問題
- VKSession カメラ データのレンダリングの問題
- threejs-miniprogram gltf テクスチャ モデルの読み込みの問題
- モデルアニメーションのエクスポートでの異常な問題
解決
webgl画像取得
主な問題:
- 取得したデータを上下逆に描画した後、ピクセル情報を反転する必要があります。
- iOS デバイスがアンチエイリアスをオンにすると、取得されるデータは 100% 純粋な黒になります。
VKSessionを使用した後は、携帯電話のカメラのデータがVKFrameに保存され、カメラ コンポーネントを再度作成することはできません。したがって、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();
...
}
カメラデータのレンダリングの問題
深度検出がオンになっているため、シェーダー コードにバグがあり、モデル上に黒いバーまたは雪の結晶が表示されます。解決策は次のとおりです。シェーダーコードを次のように変更します。
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 公式デモの互換性、左上隅の花瓶エリアは Huawei p30 pro または Xiaomi 11 に表示されますか?
gltf テクスチャ モデルの読み込みの問題
ミニプログラム環境はBlobオブジェクトやURLオブジェクトをサポートしていないため、3プラットフォーム化プロジェクトのローダーを参照し、対応するAPIを実装してください。