WebXR深床ã»ã³ã·ã³ã°APIã®å æ¬çã¬ã€ãã§ãé«åºŠãªæ¡åŒµçŸå®ãå®çŸããŸãããããªã¢ã«ãªãªã¯ã«ãŒãžã§ã³ãšç©çæŒç®ã®ããã®ããã¹ãããã¡èšå®ãåŠã³ãŸãã
WebXR深床ã»ã³ã·ã³ã°åŸ¹åºè§£èª¬ïŒããã¹ãããã¡èšå®ããã¹ã¿ãŒãã
ãŠã§ãã¯æ å ±ã®äºæ¬¡å å¹³é¢ãããäžæ¬¡å ã®æ²¡å ¥å空éãžãšé²åããŠããŸãããã®å€é©ã®æåç·ã«ããã®ããä»®æ³çŸå®ïŒVRïŒãšæ¡åŒµçŸå®ïŒARïŒããã©ãŠã¶ã«ãããã匷åãªAPIãWebXRã§ãããŠã§ãäžã®åæã®ARäœéšã¯å°è±¡çã§ããããçŸå®äžçããåãé¢ãããŠããããã«æããããããšããããããŸãããä»®æ³ãªããžã§ã¯ãã¯çŸå®äžçã®å®¶å ·ãå£ãéãæããŠããŸããååšæãªãäžèªç¶ã«ç©ºéã«æµ®ããã§ããŸããã
ããã§ç»å Žããã®ãWebXR深床ã»ã³ã·ã³ã°APIã§ãããã®ç»æçãªæ©èœã¯å€§ããªé£èºã§ããããŠã§ãã¢ããªã±ãŒã·ã§ã³ããŠãŒã¶ãŒã®ç°å¢ã®ãžãªã¡ããªãçè§£ã§ããããã«ããŸããããã¯ããžã¿ã«ãšç©ççãªäžçã®éã®ã®ã£ãããåããä»®æ³ã³ã³ãã³ããçŸå®äžçã®æ³åãã¬ã€ã¢ãŠããå°éãããçã«æ²¡å ¥æã®ããã€ã³ã¿ã©ã¯ãã£ããªäœéšãå¯èœã«ããŸãããã®åãè§£ãæŸã€éµã¯ãããã¹ãããã¡ãçè§£ããæ£ããèšå®ããããšã«ãããŸãã
ãã®å æ¬çãªã¬ã€ãã¯ããŠã§ãéçºè ãXRæå¥œå®¶ãã¯ãªãšã€ãã£ããã¯ãããžã¹ããšãã£ãäžçäžã®èªè ã察象ãšããŠããŸããç§ãã¡ã¯æ·±åºŠã»ã³ã·ã³ã°ã®åºç€ãæ¢æ±ããWebXR APIã®èšå®ãªãã·ã§ã³ãåæãããªã¢ã«ãªãªã¯ã«ãŒãžã§ã³ãç©çæŒç®ãšãã£ãé«åºŠãªARæ©èœãå®è£ ããããã®å®è·µçã§ã¹ããããã€ã¹ãããã®ã¬ã€ãã³ã¹ãæäŸããŸããæåŸãŸã§èªãã°ãããã¹ãããã¡èšå®ããã¹ã¿ãŒããæ¬¡äžä»£ã®é åçã§ã³ã³ããã¹ãã¢ãŠã§ã¢ãªWebXRã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããããã®ç¥èã身ã«ã€ããããšãã§ããã§ãããã
ã³ã¢ã³ã³ã»ããã®çè§£
APIã®è©³çްã«å ¥ãåã«ããã£ãããšããåºç€ãç¯ãããšãéèŠã§ããæ·±åºŠèªèåæ¡åŒµçŸå®ãæ¯ããã³ã¢ã³ã³ã»ãããè§£ãæãããŠãããŸãããã
ããã¹ãããïŒæ·±åºŠãããïŒãšã¯ïŒ
éšå±ãèŠãŠãããšæ³åããŠã¿ãŠãã ãããããªãã®è³ã¯ãããŒãã«ãå£ãããè¿ãã«ãããæ€ åãããŒãã«ã®åã«ããããšãç¬æã«åŠçããã·ãŒã³ãçè§£ããŸããããã¹ãããã¯ããã®çè§£ãããžã¿ã«ã§è¡šçŸãããã®ã§ãããã®æ žãšãªãããã¹ãããã¯2Dç»åã§ãããåãã¯ã»ã«ã®å€ã¯è²ã§ã¯ãªããç©çäžçã®ãã®ç¹ãã»ã³ãµãŒïŒããã€ã¹ã®ã«ã¡ã©ïŒããã©ãã ãé¢ããŠããããšããè·é¢ã衚ããŸãã
ã°ã¬ãŒã¹ã±ãŒã«ç»åãæãæµ®ãã¹ãŠãã ãããæããã¯ã»ã«ã¯éåžžã«è¿ããªããžã§ã¯ãã衚ããæãããã¯ã»ã«ã¯é ããªããžã§ã¯ãã衚ããããããŸããïŒãŸãã¯ãæ £äŸã«ãã£ãŠã¯ãã®éããããŸãïŒããã®ããŒã¿ã¯ãéåžžãæ¬¡ã®ãããªç¹æ®ãªããŒããŠã§ã¢ã«ãã£ãŠãã£ããã£ãããŸãïŒ
- Time-of-Flight (ToF) ã»ã³ãµãŒïŒ ãããã®ã»ã³ãµãŒã¯èµ€å€å ã®ãã«ã¹ãçºããå ãç©äœã«åœãã£ãŠè·³ãè¿ã£ãŠãããŸã§ã®æéãæž¬å®ããŸãããã®æéå·®ãçŽæ¥è·é¢ã«å€æãããŸãã
- LiDAR (Light Detection and Ranging)ïŒ ToFãšäŒŒãŠããŸãããããé«ç²ŸåºŠãªå Žåãå€ãLiDARã¯ãã¬ãŒã¶ãŒãã«ã¹ã䜿çšããŠç°å¢ã®é«è§£å床ãªç¹çŸ€ãäœæãããããããã¹ãããã«å€æããŸãã
- ã¹ãã¬ãªã¹ã³ããã¯ã«ã¡ã©ïŒ 2ã€ä»¥äžã®ã«ã¡ã©ã䜿çšããããšã§ãããã€ã¹ã¯äººéã®äž¡çŒèŠãæš¡å£ã§ããŸããåã«ã¡ã©ããã®ç»åã®å·®ç°ïŒèŠå·®ïŒãåæããŠæ·±åºŠãèšç®ããŸãã
WebXR APIã¯ãåºç€ãšãªãããŒããŠã§ã¢ãæœè±¡åããããã€ã¹ã«é¢ä¿ãªããéçºè ãæ±ããæšæºåãããããã¹ããããæäŸããŸãã
ãªã深床ã»ã³ã·ã³ã°ã¯ARã«ãšã£ãŠéèŠãªã®ãïŒ
ã·ã³ãã«ãªããã¹ãããã¯ããŠãŒã¶ãŒã®ARäœéšãæ ¹æ¬çã«å€ããåãªãç®æ°ããããçã«ä¿¡ææ§ã®ããã€ã³ã¿ã©ã¯ã·ã§ã³ãžãšæè¯ããããç¡éã®å¯èœæ§ãè§£ãæŸã¡ãŸãã
- ãªã¯ã«ãŒãžã§ã³ïŒ ããã¯ééããªãæãéèŠãªå©ç¹ã§ãããªã¯ã«ãŒãžã§ã³ãšã¯ãçŸå®äžçã®ç©äœãä»®æ³ãªããžã§ã¯ãã®èŠçãé®ãèœåã®ããšã§ããããã¹ãããã䜿ãã°ãã¢ããªã±ãŒã·ã§ã³ã¯åãã¯ã»ã«ã«ãããçŸå®äžçã®è¡šé¢ã®æ£ç¢ºãªè·é¢ãç¥ãããšãã§ããŸããã¬ã³ããªã³ã°ããŠããä»®æ³ãªããžã§ã¯ãããåããã¯ã»ã«ã«ããçŸå®äžçã®è¡šé¢ãããé ãã«ããå ŽåãåçŽã«ãããæç»ããªãããã«éžæã§ããŸãããã®åçŽãªè¡çºã«ãããä»®æ³ã®ãã£ã©ã¯ã¿ãŒãçŸå®ã®ãœãã¡ã®åŸãã説åŸåãæã£ãŠæ©ããããããžã¿ã«ã®ããŒã«ãçŸå®ã®ããŒãã«ã®äžã転ãã£ããããããšãã§ããæ·±ãäžäœæãçãŸããŸãã
- ç©çæŒç®ãšã€ã³ã¿ã©ã¯ã·ã§ã³ïŒ éçãªä»®æ³ãªããžã§ã¯ãã¯è峿·±ãã§ãããã€ã³ã¿ã©ã¯ãã£ããªãã®ã¯é åçã§ããæ·±åºŠã»ã³ã·ã³ã°ã¯ãªã¢ã«ãªç©çã·ãã¥ã¬ãŒã·ã§ã³ãå¯èœã«ããŸããä»®æ³ã®ããŒã«ãçŸå®ã®åºã§è·³ããããããžã¿ã«ã®ãã£ã©ã¯ã¿ãŒãå®éã®å®¶å ·ã®åšããç§»åããããä»®æ³ã®ãã³ããç©ççãªå£ã«é£ã³æ£ã£ããããããšãã§ããŸããããã«ããããã€ãããã¯ã§å¿çæ§ã®é«ãäœéšãçãŸããŸãã
- ã·ãŒã³ã®åæ§ç¯ïŒ æéã®çµéãšãšãã«ããã¹ããããåæããããšã§ãã¢ããªã±ãŒã·ã§ã³ã¯ç°å¢ã®åçŽåããã3Dã¡ãã·ã¥ãæ§ç¯ã§ããŸãããã®å¹ŸäœåŠçãªçè§£ã¯ããªã¢ã«ãªã©ã€ãã£ã³ã°ïŒçŸå®ã®è¡šé¢ã«åœ±ãèœãšãïŒãã€ã³ããªãžã§ã³ããªãªããžã§ã¯ãé 眮ïŒä»®æ³ã®è±ç¶ãçŸå®ã®ããŒãã«ã«çœ®ãïŒãªã©ã®é«åºŠãªARæ©èœãå®çŸããããã«äžå¯æ¬ ã§ãã
- ãªã¢ãªãºã ã®åäžïŒ æçµçã«ãããããã¹ãŠã®æ©èœãããããªã¢ã«ã§æ²¡å ¥æã®ããäœéšã«è²¢ç®ããŸããããžã¿ã«ã³ã³ãã³ãããŠãŒã¶ãŒã®ç©ç空éãèªèããçžäºäœçšãããšããããã¯äžçã®éã®éå£ãæã¡ç Žããããæ·±ãååšæãè²ã¿ãŸãã
WebXR深床ã»ã³ã·ã³ã°APIïŒæŠèŠ
深床ã»ã³ã·ã³ã°ã¢ãžã¥ãŒã«ã¯ãã³ã¢ãšãªãWebXR Device APIã®æ¡åŒµæ©èœã§ããå€ãã®æå 端ãŠã§ãæè¡ãšåæ§ã«ããã¹ãŠã®ãã©ãŠã¶ã§ããã©ã«ãã§æå¹ã«ãªã£ãŠããããã§ã¯ãªããç¹å®ã®ãã©ã°ãå¿ èŠã§ãã£ããããªãªãžã³ãã©ã€ã¢ã«ã®äžéšã§ãã£ããããå ŽåããããŸããæ©èœã䜿çšããããšããåã«å¿ ããµããŒãç¶æ³ã確èªããã¢ããªã±ãŒã·ã§ã³ãé²åŸ¡çã«æ§ç¯ããããšãäžå¯æ¬ ã§ãã
ãµããŒãç¶æ³ã®ç¢ºèª
ã»ãã·ã§ã³ããªã¯ãšã¹ãããåã«ããŸããã©ãŠã¶ã'depth-sensing'æ©èœãåãã'immersive-ar'ã¢ãŒãããµããŒãããŠãããã©ãããå°ããå¿ èŠããããŸããããã¯`navigator.xr.isSessionSupported()`ã¡ãœããã䜿çšããŠè¡ããŸãã
async function checkDepthSensingSupport() {
if (!navigator.xr) {
console.log("WebXRã¯å©çšã§ããŸããã");
return false;
}
try {
const supported = await navigator.xr.isSessionSupported('immersive-ar');
if (supported) {
// ããã§ç¹å®ã®æ©èœã確èªããŸã
const session = await navigator.xr.requestSession('immersive-ar', {
requiredFeatures: ['depth-sensing']
});
// ãããæåããã°ãæ©èœã¯ãµããŒããããŠããŸãããã¹ãã»ãã·ã§ã³ãçµäºã§ããŸãã
await session.end();
console.log("深床ã»ã³ã·ã³ã°ä»ãã®WebXR ARã¯ãµããŒããããŠããŸãïŒ");
return true;
} else {
console.log("ãã®ããã€ã¹ã§ã¯WebXR ARã¯ãµããŒããããŠããŸããã");
return false;
}
} catch (error) {
console.log("深床ã»ã³ã·ã³ã°ã®ãµããŒãç¶æ³ç¢ºèªäžã«ãšã©ãŒãçºçããŸããïŒ", error);
return false;
}
}
ããçŽæ¥çã§ãå®å šã§ã¯ãããŸããããã»ãã·ã§ã³ãçŽæ¥ãªã¯ãšã¹ãããŠãšã©ãŒããã£ããããæ¹æ³ããããŸãããäžèšã®æ¹æ³ã®æ¹ãäºåã«èœåã確èªããäžã§ããå ç¢ã§ãã
ã»ãã·ã§ã³ã®ãªã¯ãšã¹ã
ãµããŒãã確èªãããã`requiredFeatures`ãŸãã¯`optionalFeatures`é åã«'depth-sensing'ãå«ããŠXRã»ãã·ã§ã³ããªã¯ãšã¹ãããŸããéèŠãªã®ã¯ãæ©èœåãšãšãèšå®ãªããžã§ã¯ããæž¡ãããšã§ãããã§ç§ãã¡ã®åžæãå®çŸ©ããŸãã
async function startXRSession() {
const session = await navigator.xr.requestSession('immersive-ar', {
requiredFeatures: ['local-floor', 'dom-overlay'], // ä»ã®äžè¬çãªæ©èœ
optionalFeatures: [
{
name: 'depth-sensing',
usagePreference: ['cpu-optimized', 'gpu-optimized'],
dataFormatPreference: ['float32', 'luminance-alpha']
}
]
});
// ... ã»ãã·ã§ã³ã®ã»ããã¢ãããç¶è¡
}
'depth-sensing'ããªããžã§ã¯ãã«ãªã£ãŠããããšã«æ³šæããŠãã ãããããã§ãã©ãŠã¶ã«èšå®ã®ãã³ããæäŸããŸãããããã®éèŠãªãªãã·ã§ã³ãåè§£ããŠã¿ãŸãããã
ããã¹ãããã¡ã®èšå®ïŒåé¡ã®æ žå¿
深床ã»ã³ã·ã³ã°APIã®åã¯ãã®æè»æ§ã«ãããŸããæ·±åºŠããŒã¿ãã©ã®ããã«äœ¿çšããã€ãããããã©ãŠã¶ã«äŒããããšã§ããã©ãŠã¶ã¯ãŠãŒã¹ã±ãŒã¹ã«æãå¹ççãªåœ¢åŒã§æ å ±ãæäŸã§ããŸãããã®èšå®ã¯ãäž»ã«`usagePreference`ãš`dataFormatPreference`ã®2ã€ã®ããããã£ãéããŠãæ©èœèšè¿°åãªããžã§ã¯ãå ã§è¡ãããŸãã
`usagePreference`: CPUãGPUãïŒ
`usagePreference`ããããã£ã¯ãããªãã®äž»èŠãªãŠãŒã¹ã±ãŒã¹ããŠãŒã¶ãŒãšãŒãžã§ã³ãïŒUAïŒãã€ãŸããã©ãŠã¶ã«äŒããæååã®é åã§ããããã«ãããã·ã¹ãã ã¯ããã©ãŒãã³ã¹ã粟床ãããã³æ¶è²»é»åãæé©åã§ããŸããè€æ°ã®äœ¿çšæ³ãåªå é ã«ãªã¯ãšã¹ãã§ããŸãã
'gpu-optimized'
- æå³ïŒ ãã©ãŠã¶ã«å¯ŸããŠãäž»ãªç®çãGPUäžã§ãããããã¯ã·ã§ãŒããŒå ã§ã¬ã³ããªã³ã°ç®çã§æ·±åºŠããŒã¿ãçŽæ¥äœ¿çšããããšã ãšäŒããŠããŸãã
- ããŒã¿æäŸæ¹æ³ïŒ ããã¹ãããã¯`WebGLTexture`ãšããŠå ¬éãããŸããããŒã¿ãã¬ã³ããªã³ã°ã«äœ¿çšãããããã«GPUã®ã¡ã¢ãªããåºãå¿ èŠããªããããããã¯éåžžã«å¹ççã§ãã
- äž»ãªãŠãŒã¹ã±ãŒã¹ïŒ ãªã¯ã«ãŒãžã§ã³ããã©ã°ã¡ã³ãã·ã§ãŒããŒã§ãã®ãã¯ã¹ãã£ããµã³ããªã³ã°ããããšã«ãããçŸå®äžçã®æ·±åºŠãšä»®æ³ãªããžã§ã¯ãã®æ·±åºŠãæ¯èŒããé ããã¹ããã©ã°ã¡ã³ããç Žæ£ã§ããŸããããã¯ã深床ãèæ ®ããããŒãã£ã¯ã«ããªã¢ã«ãªåœ±ãªã©ãä»ã®GPUããŒã¹ã®ãšãã§ã¯ãã«ã圹ç«ã¡ãŸãã
- ããã©ãŒãã³ã¹ïŒ ããã¯ã¬ã³ããªã³ã°ã¿ã¹ã¯ã«ãšã£ãŠæé«ã®ããã©ãŒãã³ã¹ãªãã·ã§ã³ã§ãããã¬ãŒã ããšã«å€§éã®ããŒã¿ãGPUããCPUã«è»¢éãããšãã倧ããªããã«ããã¯ãåé¿ããŸãã
'cpu-optimized'
- æå³ïŒ CPUäžã®JavaScriptã³ãŒãã§çã®æ·±åºŠå€ã«çŽæ¥ã¢ã¯ã»ã¹ããå¿ èŠããããŸãã
- ããŒã¿æäŸæ¹æ³ïŒ ããã¹ãããã¯JavaScriptããã¢ã¯ã»ã¹å¯èœãª`ArrayBuffer`ãšããŠå ¬éãããŸãããã¹ãŠã®æ·±åºŠå€ãèªã¿åããè§£æããåæããããšãã§ããŸãã
- äž»ãªãŠãŒã¹ã±ãŒã¹ïŒ ç©çæŒç®ãè¡çªæ€åºãã·ãŒã³åæã äŸãã°ããŠãŒã¶ãŒãã¿ããããç¹ã®3D座æšãèŠã€ããããã«ã¬ã€ãã£ã¹ããå®è¡ããããããŒã¿ãåæããŠããŒãã«ãåºãªã©ã®å¹³ããªé¢ãèŠã€ããŠãªããžã§ã¯ããé 眮ãããã§ããŸãã
- ããã©ãŒãã³ã¹ïŒ ãã®ãªãã·ã§ã³ã«ã¯å€§ããªããã©ãŒãã³ã¹ã³ã¹ããããããŸããæ·±åºŠããŒã¿ã¯ãCPUãã¢ã¯ã»ã¹ããããã«ããã€ã¹ã®ã»ã³ãµãŒ/GPUããã·ã¹ãã ã®ã¡ã€ã³ã¡ã¢ãªã«ã³ããŒããå¿ èŠããããŸããJavaScriptã§ãã¬ãŒã ããšã«ãã®å€§éã®ããŒã¿é åã«å¯ŸããŠè€éãªèšç®ãå®è¡ãããšãããã©ãŒãã³ã¹ã®åé¡ãäœããã¬ãŒã ã¬ãŒãã«ç°¡åã«ã€ãªããå¯èœæ§ããããŸããæå³çã«ããããŠæ§ããã«äœ¿çšãã¹ãã§ãã
æšå¥šïŒ ãªã¯ã«ãŒãžã§ã³ãå®è£ ããäºå®ãããå Žåã¯ãåžžã«'gpu-optimized'ããªã¯ãšã¹ãããŠãã ãããäž¡æ¹ããªã¯ãšã¹ãããããšãå¯èœã§ããäŸïŒ`['gpu-optimized', 'cpu-optimized']`ããã©ãŠã¶ã¯æåã®åžæãå°éããããšããŸããããªãã®ã³ãŒãã¯ãã·ã¹ãã ã«ãã£ãŠå®éã«ã©ã®äœ¿çšã¢ãã«ãä»äžããããã確èªããäž¡æ¹ã®ã±ãŒã¹ãåŠçã§ããã»ã©å ç¢ã§ãªããã°ãªããŸããã
`dataFormatPreference`: 粟床ãäºææ§ã
`dataFormatPreference`ããããã£ã¯ã深床å€ã®æãŸããããŒã¿åœ¢åŒãšç²ŸåºŠã瀺åããæååã®é åã§ãããã®éžæã¯ã粟床ãšããŒããŠã§ã¢ã®äºææ§ã®äž¡æ¹ã«åœ±é¿ããŸãã
'float32'
- æå³ïŒ åæ·±åºŠå€ã¯å®å šãª32ãããæµ®åå°æ°ç¹æ°ã§ãã
- ä»çµã¿ïŒ å€ã¯çŽæ¥ã¡ãŒãã«åäœã®è·é¢ã衚ããŸãããã³ãŒãããå¿ èŠã¯ãªãããã®ãŸãŸäœ¿çšã§ããŸããäŸãã°ããããã¡å ã®å€1.5ã¯ããã®ç¹ã1.5ã¡ãŒãã«é¢ããŠããããšãæå³ããŸãã
- é·æïŒ é«ç²ŸåºŠã§ãã·ã§ãŒããŒãšJavaScriptã®äž¡æ¹ã§éåžžã«äœ¿ããããã§ããããã¯ç²ŸåºŠã«ãšã£ãŠçæ³çãªåœ¢åŒã§ãã
- çæïŒ WebGL 2ãšãæµ®åå°æ°ç¹ãã¯ã¹ãã£ããµããŒãããããŒããŠã§ã¢ïŒ`OES_texture_float`æ¡åŒµæ©èœãªã©ïŒãå¿ èŠã§ãããã®åœ¢åŒã¯ããã¹ãŠã®ãç¹ã«å€ãã¢ãã€ã«ããã€ã¹ã§å©çšã§ãããšã¯éããŸããã
'luminance-alpha'
- æå³ïŒ ããã¯WebGL 1ããã³floatãã¯ã¹ãã£ããµããŒãããªãããŒããŠã§ã¢ãšã®äºææ§ã®ããã«èšèšããã圢åŒã§ãã2ã€ã®8ããããã£ã³ãã«ïŒèŒåºŠãšã¢ã«ãã¡ïŒã䜿çšããŠ16ãããã®æ·±åºŠå€ãæ ŒçŽããŸãã
- ä»çµã¿ïŒ çã®16ãããæ·±åºŠå€ã¯2ã€ã®8ãããéšåã«åå²ãããŸããå®éã®æ·±åºŠãåŸãã«ã¯ãã³ãŒãå ã§ãããã®éšåãåçµåããå¿ èŠããããŸããéåžžãåŒã¯æ¬¡ã®ããã«ãªããŸãïŒ`decodedValue = luminanceValue + alphaValue / 255.0`ãçµæã¯0.0ãã1.0ã®éã®æ£èŠåãããå€ã§ãããã¡ãŒãã«åäœã®è·é¢ãåŸãããã«ã¯ãå¥ã®ä¿æ°ã§ã¹ã±ãŒãªã³ã°ããå¿ èŠããããŸãã
- é·æïŒ ã¯ããã«åºãããŒããŠã§ã¢äºææ§ã'float32'ããµããŒããããŠããªãå Žåã®ä¿¡é Œã§ãããã©ãŒã«ããã¯ã§ãã
- çæïŒ ã·ã§ãŒããŒãŸãã¯JavaScriptã§è¿œå ã®ãã³ãŒãã¹ããããå¿ èŠãšãªããããããªè€éããå ãããŸãããŸãã'float32'ãšæ¯èŒããŠç²ŸåºŠãäœãïŒ16ãããïŒã§ãã
æšå¥šïŒ æãæãŸãã圢åŒãæåã«æå®ããŠãäž¡æ¹ããªã¯ãšã¹ãããŠãã ããïŒ`['float32', 'luminance-alpha']`ãããã¯ãã©ãŠã¶ã«é«ç²ŸåºŠåœ¢åŒã奜ãããå¿ èŠã§ããã°äºææ§ã®é«ã圢åŒãæ±ããããšãäŒããŸããç¹°ãè¿ãã«ãªããŸãããã¢ããªã±ãŒã·ã§ã³ã¯ã©ã®åœ¢åŒãä»äžããããã確èªããããŒã¿ãåŠçããããã®æ£ããããžãã¯ãé©çšããå¿ èŠããããŸãã
å®è·µçãªå®è£ ïŒã¹ããããã€ã¹ãããã¬ã€ã
ããŠããããã®ã³ã³ã»ãããå®è·µçãªå®è£ ã«çµã¿åãããŠã¿ãŸããããæãäžè¬çãªãŠãŒã¹ã±ãŒã¹ã§ãããGPUæé©åãããããã¹ãããã¡ã䜿çšãããªã¢ã«ãªãªã¯ã«ãŒãžã§ã³ã«çŠç¹ãåœãŠãŸãã
ã¹ããã1ïŒå ç¢ãªXRã»ãã·ã§ã³ãªã¯ãšã¹ãã®èšå®
çæ³çãªèšå®ã§ã»ãã·ã§ã³ããªã¯ãšã¹ãããŸãããä»£æ¿æ¡ãåŠçã§ããããã«ã¢ããªã±ãŒã·ã§ã³ãèšèšããŸãã
let xrSession = null;
let xrDepthInfo = null;
async function onXRButtonClick() {
try {
xrSession = await navigator.xr.requestSession('immersive-ar', {
requiredFeatures: ['local-floor'],
domOverlay: { root: document.body }, // ä»ã®æ©èœã®äŸ
depthSensing: {
usagePreference: ['gpu-optimized'],
dataFormatPreference: ['float32', 'luminance-alpha']
}
});
// ... ã»ãã·ã§ã³éå§ããžãã¯ããã£ã³ãã¹ãWebGLã³ã³ããã¹ãã®èšå®ãªã©
// ã»ãã·ã§ã³éå§ããžãã¯ã§ã深床ã»ã³ã·ã³ã°ã®èšå®ãååŸ
const depthSensing = xrSession.depthSensing;
if (depthSensing) {
console.log(`Depth sensing granted with usage: ${depthSensing.usage}`);
console.log(`Depth sensing granted with data format: ${depthSensing.dataFormat}`);
} else {
console.warn("深床ã»ã³ã·ã³ã°ããªã¯ãšã¹ããããŸãããä»äžãããŸããã§ããã");
}
xrSession.requestAnimationFrame(onXRFrame);
} catch (e) {
console.error("XRã»ãã·ã§ã³ã®éå§ã«å€±æããŸããã", e);
}
}
ã¹ããã2ïŒã¬ã³ããªã³ã°ã«ãŒãã§ã®æ·±åºŠæ å ±ãžã®ã¢ã¯ã»ã¹
ãã¬ãŒã ããšã«åŒã³åºããã`onXRFrame`颿°å ã§ãçŸåšã®ãã¥ãŒã®æ·±åºŠæ å ±ãååŸããå¿ èŠããããŸãã
function onXRFrame(time, frame) {
const session = frame.session;
session.requestAnimationFrame(onXRFrame);
const pose = frame.getViewerPose(xrReferenceSpace);
if (!pose) return;
const glLayer = session.renderState.baseLayer;
const gl = webglContext; // ããªãã®WebGLã³ã³ããã¹ã
gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer);
for (const view of pose.views) {
const viewport = glLayer.getViewport(view);
gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
// éèŠãªã¹ãããïŒæ·±åºŠæ
å ±ãååŸ
const depthInfo = frame.getDepthInformation(view);
if (depthInfo) {
// ãã®ãã¬ãŒã ãšãã¥ãŒã®æ·±åºŠããŒã¿ããããŸãïŒ
// ãããã¬ã³ããªã³ã°é¢æ°ã«æž¡ããŸã
renderScene(view, depthInfo);
} else {
// ãã®ãã¬ãŒã ã§å©çšå¯èœãªæ·±åºŠããŒã¿ã¯ãããŸãã
renderScene(view, null);
}
}
}
`depthInfo`ãªããžã§ã¯ãïŒ`XRDepthInformation`ã®ã€ã³ã¹ã¿ã³ã¹ïŒã«ã¯ãå¿ èŠãªãã®ããã¹ãŠå«ãŸããŠããŸãïŒ
- `depthInfo.texture`: ('gpu-optimized'ã䜿çšããŠããå Žå)深床ããããå«ã`WebGLTexture`ã
- `depthInfo.width`, `depthInfo.height`: 深床ãã¯ã¹ãã£ã®å¯žæ³ã
- `depthInfo.normDepthFromNormView`: æ£èŠåããããã¥ãŒåº§æšã深床ãããããµã³ããªã³ã°ããããã®æ£ãããã¯ã¹ãã£åº§æšã«å€æããããã«äœ¿çšããã`XRRigidTransform`ïŒè¡åïŒãããã¯ã深床ããŒã¿ãã«ã©ãŒã«ã¡ã©ç»åãšæ£ããäœçœ®åããããããã«äžå¯æ¬ ã§ãã
- `depthInfo.rawValueToMeters`: ã¹ã±ãŒã«ä¿æ°ããã¯ã¹ãã£ããåŸãçã®å€ã«ãã®æ°å€ãæããããšã§ãã¡ãŒãã«åäœã®è·é¢ãåŸãããŸãã
ã¹ããã3ïŒGPUæé©åããã¹ãããã¡ã«ãããªã¯ã«ãŒãžã§ã³ã®å®è£
ãããéæ³ãèµ·ããå ŽæãGLSLã·ã§ãŒããŒã®å éšã§ããç®æšã¯ãçŸå®äžçã®æ·±åºŠïŒãã¯ã¹ãã£ããïŒããçŸåšæç»ããŠããä»®æ³ãªããžã§ã¯ãã®æ·±åºŠãšæ¯èŒããããšã§ãã
é ç¹ã·ã§ãŒããŒïŒç°¡æçïŒ
é ç¹ã·ã§ãŒããŒã¯ã»ãšãã©æšæºçãªãã®ã§ãããªããžã§ã¯ãã®é ç¹ã倿ããéèŠãªããšãšããŠãã¯ãªãã空é座æšããã©ã°ã¡ã³ãã·ã§ãŒããŒã«æž¡ããŸãã
// GLSL (é ç¹ã·ã§ãŒããŒ)
attribute vec3 a_position;
uniform mat4 u_projectionMatrix;
uniform mat4 u_modelViewMatrix;
varying vec4 v_clipPosition;
void main() {
vec4 position = u_modelViewMatrix * vec4(a_position, 1.0);
gl_Position = u_projectionMatrix * position;
v_clipPosition = gl_Position;
}
ãã©ã°ã¡ã³ãã·ã§ãŒããŒïŒã³ã¢ããžãã¯ïŒ
ãã©ã°ã¡ã³ãã·ã§ãŒããŒãéåŽåãæ ããŸããæ·±åºŠãã¯ã¹ãã£ãšããã«é¢é£ããã¡ã¿ããŒã¿ãuniformãšããŠæž¡ãå¿ èŠããããŸãã
// GLSL (ãã©ã°ã¡ã³ãã·ã§ãŒããŒ)
precision mediump float;
varying vec4 v_clipPosition;
uniform sampler2D u_depthTexture;
uniform mat4 u_normDepthFromNormViewMatrix;
uniform float u_rawValueToMeters;
// float32ãšluminance-alphaã®ã©ã¡ãã䜿çšããŠããããã·ã§ãŒããŒã«äŒããuniform
uniform bool u_isFloatTexture;
// çŸåšã®ãã©ã°ã¡ã³ãã«å¯Ÿå¿ããçŸå®äžçã®æ·±åºŠãã¡ãŒãã«åäœã§ååŸãã颿°
float getDepth(vec2 screenUV) {
// ã¹ã¯ãªãŒã³UVããæ·±åºŠãã¯ã¹ãã£UVã«å€æ
vec2 depthUV = (u_normDepthFromNormViewMatrix * vec4(screenUV, 0.0, 1.0)).xy;
// ãã¯ã¹ãã£ã®å€åŽããµã³ããªã³ã°ããªãããã«ä¿èšŒ
if (depthUV.x < 0.0 || depthUV.x > 1.0 || depthUV.y < 0.0 || depthUV.y > 1.0) {
return 10000.0; // ç¯å²å€ã®å Žåã¯å€§ããªå€ãè¿ã
}
float rawDepth;
if (u_isFloatTexture) {
rawDepth = texture2D(u_depthTexture, depthUV).r;
} else {
// luminance-alpha圢åŒãããã³ãŒã
vec2 encodedDepth = texture2D(u_depthTexture, depthUV).ra; // .raã¯.laãšç䟡
rawDepth = encodedDepth.x + (encodedDepth.y / 255.0);
}
// ç¡å¹ãªæ·±åºŠå€ïŒå€ãã¯0.0ïŒãåŠç
if (rawDepth == 0.0) {
return 10000.0; // éåžžã«é ããã®ãšããŠæ±ã
}
return rawDepth * u_rawValueToMeters;
}
void main() {
// ãã®ãã©ã°ã¡ã³ãã®ã¹ã¯ãªãŒã³ç©ºéUV座æšãèšç®
// v_clipPosition.w ã¯ããŒã¹ãã¯ãã£ãé€ç®ã®ä¿æ°
vec2 screenUV = (v_clipPosition.xy / v_clipPosition.w) * 0.5 + 0.5;
float realWorldDepth = getDepth(screenUV);
// ä»®æ³ãªããžã§ã¯ãã®æ·±åºŠãååŸ
// gl_FragCoord.z ã¯çŸåšã®ãã©ã°ã¡ã³ãã®æ£èŠåãããæ·±åºŠ [0, 1]
// ãããã¡ãŒãã«ã«æ»ãå¿
èŠããããŸãïŒããã¯ãããžã§ã¯ã·ã§ã³è¡åã®near/farå¹³é¢ã«äŸåããŸãïŒ
// ãã¢ã³ã¹ãã¬ãŒã·ã§ã³ã®ããã®åçŽåãããç·åœ¢å€æïŒ
float virtualObjectDepth = v_clipPosition.z / v_clipPosition.w;
// ãªã¯ã«ãŒãžã§ã³ãã§ãã¯
if (virtualObjectDepth > realWorldDepth) {
discard; // ãã®ãã©ã°ã¡ã³ãã¯çŸå®äžçã®ãªããžã§ã¯ãã®åŸãã«ããã®ã§ãæç»ããªã
}
// ããã«æ¥ãå Žåããªããžã§ã¯ãã¯è¡šç€ºãããŸããæç»ããŸãã
gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0); // äŸïŒããŒã³ã¿è²
}
æ·±åºŠå€æã«é¢ããéèŠãªæ³šæïŒ `gl_FragCoord.z`ãã¯ãªãã空éã®Zãã¡ãŒãã«åäœã®ç·åœ¢è·é¢ã«æ»ãããšã¯ããããžã§ã¯ã·ã§ã³è¡åã«äŸåããç°¡åãªäœæ¥ã§ã¯ãããŸããã`float virtualObjectDepth = v_clipPosition.z / v_clipPosition.w;`ãšããè¡ã¯ãã¥ãŒç©ºéã®æ·±åºŠãæäŸããããã¯æ¯èŒã®åºçºç¹ãšããŠé©ããŠããŸããå®ç§ãªç²ŸåºŠãåŸãããã«ã¯ãã«ã¡ã©ã®nearããã³farã¯ãªããã³ã°å¹³é¢ãå«ãåŒã䜿çšããŠãããã¹ãããã¡ã®å€ãç·åœ¢åããå¿ èŠããããŸãã
ãã¹ããã©ã¯ãã£ã¹ãšããã©ãŒãã³ã¹ã«é¢ããèæ ®äºé
å ç¢ã§ããã©ãŒãã³ã¹ã®é«ã深床èªèåäœéšãæ§ç¯ããã«ã¯ã次ã®ç¹ãæ éã«èæ ®ããå¿ èŠããããŸãã
- æè»ãã€é²åŸ¡çã«ïŒ åžæããèšå®ãä»äžããããšæ±ºããŠæã蟌ãŸãªãã§ãã ãããåžžã«ã¢ã¯ãã£ããª`xrSession.depthSensing`ãªããžã§ã¯ããã¯ãšãªããŠãä»äžããã`usage`ãš`dataFormat`ã確èªããŠãã ããããµããŒãããææã®ãããã¹ãŠã®å¯èœãªçµã¿åãããåŠçã§ããããã«ã¬ã³ããªã³ã°ããžãã¯ãèšè¿°ããŠãã ããã
- ã¬ã³ããªã³ã°ã«ã¯GPUãåªå ïŒ ããã©ãŒãã³ã¹ã®å·®ã¯éåžžã«å€§ããã§ããæ·±åºŠã®èŠèŠåããªã¯ã«ãŒãžã§ã³ãå«ãã¿ã¹ã¯ã«ã¯ã'gpu-optimized'ãã¹ãã¹ã ãŒãºãª60/90fpsäœéšã®ããã®å¯äžã®å®è¡å¯èœãªéžæè¢ã§ãã
- CPUäœæ¥ã®æå°åãšé å»¶ïŒ ç©çæŒç®ãã¬ã€ãã£ã¹ãã£ã³ã°ã®ããã«'cpu-optimized'ããŒã¿ã䜿çšããªããã°ãªããªãå Žåã¯ããã¬ãŒã ããšã«ãããã¡å šäœãåŠçããªãã§ãã ããã察象ãçµã£ãèªã¿åããå®è¡ããŠãã ãããäŸãã°ããŠãŒã¶ãŒãç»é¢ãã¿ãããããšãã«ããã®ç¹å®ã®åº§æšã®æ·±åºŠå€ã®ã¿ãèªã¿åããŸããã¡ã€ã³ã¹ã¬ããããéãåæããªãããŒãããããã«Web Workerã®äœ¿çšãæ€èšããŠãã ããã
- æ¬ æããŒã¿ãé©åã«åŠçïŒ æ·±åºŠã»ã³ãµãŒã¯å®ç§ã§ã¯ãããŸãããçµæãšããŠåŸãããããã¹ãããã«ã¯ãç¹ã«åå°é¢ãéæãªè¡šé¢ã§ç©Žããã€ãºã®å€ãããŒã¿ãäžæ£ç¢ºããçããŸãããªã¯ã«ãŒãžã§ã³ã·ã§ãŒããŒãšç©çããžãã¯ã¯ãèŠèŠçãªã¢ãŒãã£ãã¡ã¯ããäžæ£ãªåäœãé¿ããããã«ãç¡å¹ãªæ·±åºŠå€ïŒå€ãã¯0ãšããŠè¡šãããïŒãåŠçããå¿ èŠããããŸãã
- 座æšç³»ããã¹ã¿ãŒããïŒ ããã¯éçºè ã«ãšã£ãŠäžè¬çãªå€±æç¹ã§ãããã¥ãŒãã¯ãªãããæ£èŠåããã€ã¹ããã¯ã¹ãã£ãªã©ãããŸããŸãªåº§æšç³»ã«çްå¿ã®æ³šæãæãã`normDepthFromNormView`ã®ãããªæäŸãããè¡åãæ£ãã䜿çšããŠãã¹ãŠãäœçœ®åããããŠããããšã確èªããŠãã ããã
- æ¶è²»é»åã®ç®¡çïŒ æ·±åºŠã»ã³ã·ã³ã°ããŒããŠã§ã¢ãç¹ã«LiDARã®ãããªã¢ã¯ãã£ãã»ã³ãµãŒã¯ãããªãã®ããããªãŒé»åãæ¶è²»ããå¯èœæ§ããããŸããã¢ããªã±ãŒã·ã§ã³ãæ¬åœã«å¿ èŠãªå Žåã«ã®ã¿'depth-sensing'æ©èœããªã¯ãšã¹ãããŠãã ããããŠãŒã¶ãŒãç©æ¥µçã«é¢äžããŠããªããšãã¯ãé»åãç¯çŽããããã«XRã»ãã·ã§ã³ãé©åã«äžæããã³çµäºãããããã«ããŠãã ããã
WebXR深床ã»ã³ã·ã³ã°ã®æªæ¥
深床ã»ã³ã·ã³ã°ã¯åºç€æè¡ã§ãããWebXR仿§ã¯ãããåãå·»ããŠé²åãç¶ããŠããŸããäžçã®éçºè ã³ãã¥ããã£ã¯ãå°æ¥çã«ã¯ããã«åŒ·åãªæ©èœãæåŸ ã§ããŸãïŒ
- ã·ãŒã³ã®çè§£ãšã¡ãã·ã¥åïŒ æ¬¡ã®è«ççãªã¹ãããã¯XRMeshã¢ãžã¥ãŒã«ã§ãããæ·±åºŠããŒã¿ããæ§ç¯ãããç°å¢ã®å®éã®3Däžè§åœ¢ã¡ãã·ã¥ãæäŸããŸããããã«ãããããã«ãªã¢ã«ãªç©çæŒç®ãããã²ãŒã·ã§ã³ãã©ã€ãã£ã³ã°ãå¯èœã«ãªããŸãã
- ã»ãã³ãã£ãã¯ã©ãã«ïŒ 衚é¢ã®ãžãªã¡ããªãç¥ãã ãã§ãªããããããåºããå£ããããŒãã«ãã§ããããšãç¥ãããšãæ³åããŠã¿ãŠãã ãããå°æ¥ã®APIã¯ããã®ãããªã»ãã³ãã£ãã¯æ å ±ãæäŸããå¯èœæ§ãé«ããä¿¡ããããªãã»ã©ã€ã³ããªãžã§ã³ãã§ã³ã³ããã¹ãã¢ãŠã§ã¢ãªã¢ããªã±ãŒã·ã§ã³ãå¯èœã«ããŸãã
- ããŒããŠã§ã¢çµ±åã®åäžïŒ ARã°ã©ã¹ãã¢ãã€ã«ããã€ã¹ããã匷åã«ãªããããåªããã»ã³ãµãŒãšããã»ããµãŒãæèŒããã«ã€ããŠãWebXRã«æäŸãããæ·±åºŠããŒã¿ã®å質ãè§£å床ã粟床ã¯åçã«åäžããæ°ããªåµé çå¯èœæ§ãåãéãã§ãããã
çµè«
WebXR深床ã»ã³ã·ã³ã°APIã¯ãéçºè ãæ°ããã¯ã©ã¹ã®ãŠã§ãããŒã¹æ¡åŒµçŸå®äœéšãåµé ããããšãå¯èœã«ããå€é©çãªæè¡ã§ããåçŽãªãªããžã§ã¯ãé 眮ãè¶ ããç°å¢çè§£ãåãå ¥ããããšã§ããããªã¢ã«ã§ãã€ã³ã¿ã©ã¯ãã£ãã§ããŠãŒã¶ãŒã®äžçãšçã«çµ±åãããã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ã§ããŸããããã¹ãããã¡ã®èšå®ããã¹ã¿ãŒããããšãã€ãŸã'cpu-optimized'ãš'gpu-optimized'ã®äœ¿çšæ³ãããã³'float32'ãš'luminance-alpha'ããŒã¿åœ¢åŒã®éã®ãã¬ãŒããªããçè§£ããããšãããã®å¯èœæ§ãè§£ãæŸã€ããã«å¿ èŠãªéèŠãªã¹ãã«ã§ãã
ãŠãŒã¶ãŒã®ããã€ã¹èœåã«é©å¿ã§ãããæè»ã§ãããã©ãŒãã³ã¹ãé«ããå ç¢ãªã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããããšã§ãããªãã¯åäžã®äœéšãåµé ããŠããã ãã§ã¯ãããŸãããããªãã¯æ²¡å ¥åã§ç©ºéçãªãŠã§ãã®åºç€ã«è²¢ç®ããŠããã®ã§ããããŒã«ã¯ããªãã®æã®äžã«ãããŸããæ·±ãæãäžããæªæ¥ãæ§ç¯ããæã§ãã