深度マップをFPS視点で立体的に表示した
距離感を掴むために、あえて高解像度で押します。1024×1024推論と高密度ジオメトリで、滑らかさと現実的なfpsのバランスを見る。端の破綻はエッジフェードでケア。
WEBカメラの映像から深度推定して、3D空間に立体表示するシステムを作った。
midori271は平面的な考えだったけど、今回は3次元の立体的なイメージ。FPS(First Person Shooter)ゲームのような視点で、物体の位置を把握できるようにした。
立体だ。
やりたかったこと
拡張現実(AR)のインターフェースを考えてた。
平面的な表示だと、物体の距離感が分かりにくい。深度情報があるなら、それを3D空間で立体的に表現したい。
FPSゲームみたいに。物体がどこにあるか、一目で分かるように。
画像の3D化には限界がある。でも、深度情報なら、物体の位置はかなり正確に分かる。
高解像度の深度推論
最初に試したのが、高解像度での推論。
class GPUInferenceManager {
constructor(modelPath) {
this.inferenceConfig = {
width: 1024, // 高解像度
height: 1024
};
}
}
1024×1024で推論してる。
midori273は512×512だった。今回は2倍。
細かい物体まで検出したかった。高解像度なら、精度が上がるはず。
最初は2048×2048を試した。推論時間が長すぎた。1回の推論に200ms以上かかった。リアルタイムで動かない。
512×512だと、細かい物体が検出できなかった。
1024×1024なら、推論時間は約50ms。精度も十分。ちょうどいい。
高密度ジオメトリ
PlaneGeometryの分割数を上げた。
const geometry = new THREE.PlaneGeometry(
160, // 幅
90, // 高さ
1024, // 横方向の分割数
288 // 縦方向の分割数
);
1024×288で分割してる。midori273は128×72だった。横方向は8倍。
1024×1024にしたかったけど、ポリゴン数が100万を超えて、描画が重くなった。
1024×288なら、横方向だけ高密度で、縦方向は抑えめ。バランスが良かった。
ShaderMaterialで立体表現
深度値を法線方向の変位に変換する。
const depthDisplacementMaterial = new THREE.ShaderMaterial({
uniforms: {
depthTexture: { value: depthTexture },
displacementScale: { value: 20.0 },
edgeFadeWidth: { value: 0.1 }
},
vertexShader: `
uniform sampler2D depthTexture;
uniform float displacementScale;
uniform float edgeFadeWidth;
void main() {
// 深度値を取得
vec4 depthColor = texture2D(depthTexture, uv);
float depth = (depthColor.r + depthColor.g + depthColor.b) / 3.0;
// エッジでのフェード効果
float fadeX = min(uv.x, 1.0 - uv.x) / edgeFadeWidth;
float fadeY = min(uv.y, 1.0 - uv.y) / edgeFadeWidth;
float edgeFactor = min(min(1.0, fadeX), min(1.0, fadeY));
// エッジに近づくほど変位を減少
float displacement = depth * displacementScale * edgeFactor;
// 法線方向に移動
vec3 newPosition = position + normal * displacement;
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
`,
fragmentShader: `
uniform sampler2D videoTexture;
void main() {
vec4 videoColor = texture2D(videoTexture, vUv);
gl_FragColor = videoColor;
}
`
});
displacementScale は立体感の強さ。20.0に設定してる。
最初は10.0で試した。立体感が弱かった。
50.0にしたら、凹凸が激しすぎた。ポリゴンの歪みが目立った。
20.0がちょうどいい。
エッジフェード効果
画像の端がちぎれて見えるのを防ぐため、エッジでフェードさせる。
// エッジでのフェード効果を計算
float fadeX = min(uv.x, 1.0 - uv.x) / edgeFadeWidth;
float fadeY = min(uv.y, 1.0 - uv.y) / edgeFadeWidth;
float edgeFactor = min(min(1.0, fadeX), min(1.0, fadeY));
// エッジに近づくほど変位を減少
float displacement = depth * displacementScale * edgeFactor;
edgeFadeWidth は0.1に設定してる。画像の端10%をフェード領域にする。
最初はフェードなしで試した。画像の端がギザギザ。
0.05にしたら、フェード領域が狭すぎて、まだギザギザが残った。
0.2にしたら、フェード領域が広すぎて、画像の中央まで影響が出た。
0.1がちょうどいい。
推論間隔の調整
リアルタイム性を重視して、推論間隔を10msに設定した。
let depthInferenceInterval = 10; // ミリ秒単位
midori273は200msだったけど、今回は10ms。約20倍速い更新。
10msだと、推論が追いつかない時がある。でも、待機処理を入れてあるので、画面は壊れない。
if (updateDepthMap.isProcessing) return;
updateDepthMap.isProcessing = true;
// 推論処理...
updateDepthMap.isProcessing = false;
処理中フラグで、同時アクセスを防いでる。
50msにしたら、安定したけど、動きがカクカクになった。
10msなら、滑らかに動く。推論が間に合わない時は、前のフレームをそのまま使う。
ハマったところ
ポリゴン数の限界
最初、1024×1024で分割しようとした。
// NG: ポリゴン数が多すぎ
const geometry = new THREE.PlaneGeometry(160, 90, 1024, 1024);
// → 1024×1024 = 1,048,576ポリゴン
描画が重すぎた。fpsが10以下に落ちた。
1024×288に変えた。
// OK: 横方向だけ高密度
const geometry = new THREE.PlaneGeometry(160, 90, 1024, 288);
// → 1024×288 = 294,912ポリゴン
ポリゴン数が約30万。60fps達成。
深度値の正規化
深度値の範囲が、フレームごとに変わる。正規化しないと、立体感がちらつく。
// 深度値を正規化
float normalizedDepth = (depth - depthMin) / (depthMax - depthMin);
depthMin と depthMax は、推論結果から計算してる。
最初は固定値(0.0〜1.0)で試した。暗いシーンだと、全体が平坦に見えた。
動的に範囲を計算したら、どんなシーンでも立体感が出るようになった。
WebGPUの推論速度
WebGPUで1024×1024の推論は、約50ms。
最初は、もっと速いと思ってた。実際に測定したら、意外と時間がかかった。
WebGLだと約80msかかる。WebGPUの方が速いけど、劇的な差ではない。
1024×1024は、現状のWebGPUでも限界に近い。2048×2048はまだ無理。
パフォーマンス
PCでの動作を測定した。
| 処理 | 時間 |
|------|------|
| 深度推論(WebGPU) | 約50ms |
| ジオメトリ更新 | 約8ms |
| シェーダー描画 | 約10ms |
| 合計(1回の推論) | 約68ms |
推論間隔を10msにしてるけど、実際の推論は約15fpsで実行される。
アニメーション自体は60fpsで動くので、見た目は滑らか。深度データは15fpsで更新。
スマホだと、深度推論が200ms以上かかった。実用的じゃない。PCでの利用を推奨。
結果
FPS視点で深度マップを立体表示できた。
- 高解像度(1024×1024)で深度推論
- 高密度ジオメトリ(1024×288分割)
- エッジフェード効果で自然な表示
- 推論間隔10msで高速更新
1024×1024の推論は重いけど、WebGPUなら実用的な速度で動く。
ポリゴン数を抑えて(1024×288)、パフォーマンスを確保した。横方向だけ高密度にすれば、見た目は十分綺麗。
3D空間での表示を体験:
全体の処理フロー:
これだ。
使ってみて
実際に体験してみてください:
デモを起動する(全画面表示)
ポイントは以下の3つ:
- 1024×1024推論は約50ms(PC推奨)、見た目は十分滑らか
- ジオメトリは1024×288でバランス良好(横方向を高密度)
- エッジフェード0.1で端の破綻を抑制
手応え。
まとめ
今回は、深度マップをFPS視点で立体的に表示する実験を行いました。
ポイントは以下の4つ:
- 高解像度(1024×1024)で深度推論、約50ms
- 高密度ジオメトリ(1024×288分割)で細かい表現
- エッジフェード効果(フェード幅0.1)で自然な表示
- 推論間隔10msで高速更新、実際は約15fps
高解像度の推論は重いけど、WebGPUなら実用的な速度で動かせることが分かった。
3D空間でのAR表現を試している方の参考になれば嬉しいです。
さらに深く学ぶには
この記事で興味を持った方におすすめのリンク:
自分の関連記事:
最後まで読んでくださり、ありがとうございました。