これは明らかに WEBGL-Gurus への呼び出しです。
2 番目の深度カメラを使用して 2x2 ピクセルの解像度でレンダリングしていますが、1 ピクセルしか読み取っていません。
私が試してみました:
renderer.readRenderTargetPixels()
Three.js で (これgl.readPixels
は pureと同じになると思いますWEBGL
)。getImageData()
にレンダリングした後Canvas
。
どちらも非常に遅いようです。私の場合、2x2 ピクセルのレンダー ターゲットの場合でも、最大 25 ミリ秒かかります。特にの不当な低効率gl.readpixels
もここで言及されています: gl.readPixels を使用する正しい方法は何ですか?
だから、私は解決策を探しています-その単一ピクセルを効率的に読み取り、それをJS配列またはオブジェクトに入れる任意の解決策。ありがとう。
アップデート:
- タイトルと本文をより的を得たものに更新しました。
- readRenderTargetPixels() (gl.readpixels() と同等) のレイテンシを示すために JSFiddle を作成しました。私はそれをできるだけシンプルにしようとしましたが、そうではありません...シンプル:)
注意事項・使い方:
このデモは、通常モードと WebXR モードの両方で実行されます。 巨大な遅延を確認するために VR に入る必要はありません。JSFiddle にはバグがあり、モバイルで VRButton が表示されません。モバイルで VR に入るには、コードを index.html にコピーし、WIFI 経由で安全なローカル サーバー (HTTPS) を実行するか、VRButton を非表示にしない JSFiddle 代替を使用する必要があります。多くをテストしましたが、機能するものを見つけることができませんでした(!)
このデモでは、便宜上、画面上の通常モードと WebXR モードの両方で、最後の 10 フレームのレンダリング統計(最小および最大ミリ秒のレンダリング時間) を表示するため、モバイルでも簡単に確認できます。
メインカメラとデプスカメラがあります。深度カメラの解像度は 2x2 ピクセルで、FOV は 0.015 度です。回転する立方体を指し、深さを測定し、立方体の表面に光線と点を描画します。コードを最適化して、JS でのコストのかかるデコード計算を排除しました。
私は主にモバイルに興味があります。モバイルではレイテンシーがはるかに高く、便宜上 QR コードを提供しています。モバイルでテストするには、次の #1 QR コードをスキャンし、安定するまで 1 分待ってから、2 番目の QR コードをスキャンして比較してください。JSFiddle のコードを表示/編集するには、URL から「show」を削除し、ページをリロードします。
Chrome ブラウザでデモをテストしてください。PC でデモを実行する場合は、遅延を判断する前に 1 分間待つことも重要です。PC 上の Firefox は、PC 上の Chrome よりもはるかに待ち時間が短く、はるかに安定していることに気付きましたが、私が興味を持っている機能は FF ではサポートされていないため、Chrome だけに興味があります。私の PC では、Chrome は約 5 ミリ秒のレンダリング時間で始まり (その機能がない場合は 1 ~ 2 ミリ秒であるのに対して、これはまだ巨大です)、しばらくすると 2 倍や 3 倍になります。モバイルでは、ほとんどの場合、15 ~ 30 ミリ秒 (強力なモバイル) と高くなります。
readRenderTargetPixels() オン:
https://jsfiddle.net/dlllb/h3ywjeud/show
readRenderTargetPixels() オフ:
https://jsfiddle.net/dlllb/mbk4Lfy1/show
<!DOCTYPE html>
<html lang="en" >
<head>
<title>READPIXELS LATENCY</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<meta name="mobile-web-app-capable" content="yes">
<meta name="HandheldFriendly" content="true" />
<style>
html{height: 100%}
body {text-align:center; background-color: #000000; height: 100%; margin: 0px; overflow: hidden}
#info { position: absolute; top: 0px; width: 800px; margin: 10px; text-align: left; z-index: 90; display:none}
</style>
</head>
<body>
<div id="container"></div>
<script type="module" defer>
import * as THREE from "https://threejs.org/build/three.module.js";
import { VRButton } from "https://threejs.org/examples/jsm/webxr/VRButton.js";
var win = {
x: window.innerWidth,
y: window.innerHeight
}
// multiply deg with deg2rad to find radians (rad = deg * Pi/180)
const deg2rad = 0.017453292519943295769236907685;
var tmp;
var startms = 0;
var endms = 0;
var deltams = 0;
var fcounter = 0;
var fbuffer = [0,0,0,0,0,0,0,0,0,0];
var maxms = 0;
var minms = 1000;
let avframes = 10;
//_________________________SCENE___________________________________________
var scene = new THREE.Scene();
scene.background = new THREE.Color( "#346189" );
//___________________xrRig __xrCam________________________________________
var xrRig = new THREE.Object3D();
scene.add(xrRig);
var fov = 50;
var aspect = win.x/win.y;
var cnear = 10;
var cfar = 4000;
var xrCam = new THREE.PerspectiveCamera( fov, aspect, cnear, cfar );
xrRig.add(xrCam);
xrRig.position.set(0, 20, 125);
//___________________ depthRig ____ depthCam ____________________________
var depthRig = new THREE.Object3D();
scene.add(depthRig);
var dres = 2;
var lfov = 0.015625;
var laspect = 1;
var lnear = 1;
var lfar = 2000;
var depthCam = new THREE.PerspectiveCamera( lfov, laspect, lnear, lfar );
depthRig.add(depthCam);
depthRig.position.set(40, 0, 50);
depthRig.rotateOnAxis(new THREE.Vector3(0,1,0), 40 * deg2rad)
// show camera cone (depth won't work)
// const helper = new THREE.CameraHelper( depthCam );
// scene.add( helper );
//_________________________________________________________________
var depthTarget = new THREE.WebGLRenderTarget( dres, dres );
depthTarget.texture.format = THREE.RGBAFormat;
// depthTarget.texture.minFilter = THREE.NearestFilter;
// depthTarget.texture.magFilter = THREE.NearestFilter;
// depthTarget.texture.generateMipmaps = false;
// depthTarget.stencilBuffer = false;
// depthTarget.depthBuffer = true;
// depthTarget.depthTexture = new THREE.DepthTexture();
// depthTarget.depthTexture.format = THREE.DepthFormat;
// depthTarget.depthTexture.type = THREE.UnsignedShortType;
var depthMaterial = new THREE.MeshDepthMaterial({depthPacking: THREE.RGBADepthPacking});
var pb = new Uint8Array(4);
var onpos = new THREE.Vector3();
//_________________________________________________________________
const Dlight = new THREE.DirectionalLight( 0xffffff, 1);
Dlight.position.set( 0, 1000, 1000 );
scene.add( Dlight );
//_________________________________________________________________
// *** WebGLRenderer XR ***
var renderer = new THREE.WebGLRenderer({ antialias: true, precision:'highp'});
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( win.x, win.y );
renderer.autoClear = false;
renderer.xr.enabled = true;
var cont = document.getElementById( 'container' );
cont.appendChild( renderer.domElement );
document.body.appendChild( VRButton.createButton( renderer ) );
//____________ LASER RAY VARS _________________________________
var startray = new THREE.Vector3();
var endray = new THREE.Vector3();
var raygeom = new THREE.BufferGeometry();
var points = [startray, endray];
raygeom.setFromPoints( points );
var rayline = new THREE.Line( raygeom, new THREE.MeshBasicMaterial({color: 0xff0000}) );
scene.add(rayline);
var marker = new THREE.Mesh(new THREE.SphereGeometry(0.8), new THREE.MeshBasicMaterial({color: 0xff0000}));
scene.add(marker);
//____________ CUBE _________________________________
var cubeGeometry = new THREE.BoxGeometry(40,40,40);
var material = new THREE.MeshStandardMaterial({color: "#eabf11"});
var cube = new THREE.Mesh(cubeGeometry, material);
scene.add(cube);
cube.position.set(0,0,0);
// ______________________VERTICAL_PLANE____________________________
var ccw = 500;
var cch = 150;
var vplane = new THREE.PlaneBufferGeometry( ccw, cch );
var vmap = new THREE.MeshBasicMaterial();
var m = new THREE.Mesh( vplane, vmap );
m.visible = false;
m.position.set(0, 150, -1000);
scene.add( m );
//_________ CANVAS _______________
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
ctx.canvas.width = ccw;
ctx.canvas.height = cch;
ctx.font = "60px Arial";
ctx.textBaseline = "top";
var img, tex;
function drawtext(t1, t2){
ctx.clearRect(0, 0, ccw, cch);
ctx.fillStyle = "#346189";
ctx.fillRect(0, 0, ccw, cch);
ctx.fillStyle = "#ffffff";
ctx.fillText(t1, 100, 10, ccw);
ctx.fillText(t2, 100, 80, ccw);
img = ctx.getImageData(0, 0, ccw, cch);
tex = new THREE.Texture(img);
tex.needsUpdate = true;
m.material.map = tex;
m.material.needsUpdate = true;
tex.dispose();
m.visible = true;
}
//_________________________________
window.addEventListener('resize', onResize, false);
renderer.setAnimationLoop( render );
//_________handle_window_resizing___________________________
function onResize(){
if (renderer.xr.isPresenting) return;
win.x = window.innerWidth;
win.y = window.innerHeight;
xrCam.aspect = win.x / win.y;
xrCam.updateProjectionMatrix();
renderer.setSize( win.x, win.y );
}
// ____________________render_frame______________________________
function render() {
startms = Date.now();
renderer.clear();
cube.rotation.y += 0.01;
renderer.xr.enabled = false;
//---------------------- RENDER RGBA-Depth to depthTarget--------------------------
renderer.setClearColor("#ffffff", 1);
rayline.visible = false;
marker.visible = false;
renderer.setRenderTarget(depthTarget);
scene.overrideMaterial = depthMaterial;
renderer.render(scene, depthCam);
// ******* COMMENT-OUT THE FOLLOWING LINE TO COMPARE ******
renderer.readRenderTargetPixels(depthTarget, dres/2, dres/2, 1, 1, pb);
var dp = pb[0]*0.0000000002328306436538696
+ pb[1]*0.00000005960464477539063
+ pb[2]*0.0000152587890625
+ pb[3]*0.00390625;
var viewZ = (lnear * lfar) / ((lfar - lnear) * dp - lfar);
var midZ = viewZ;
if (viewZ < -lfar) {
midZ = -lfar;
}
onpos.set(0, 0, 0.5).applyMatrix4(depthCam.projectionMatrixInverse);
onpos.multiplyScalar(midZ / onpos.z);
onpos.applyMatrix4(depthCam.matrixWorld);
startray = new THREE.Vector3();
depthCam.getWorldPosition(startray);
raygeom.attributes.position.setXYZ(0, startray.x, startray.y, startray.z);
raygeom.attributes.position.setXYZ(1, onpos.x, onpos.y, onpos.z);
raygeom.attributes.position.needsUpdate = true;
//-------------------- RENDER NORMAL SCENE ------------------------
renderer.setClearColor("#346189", 1);
renderer.xr.enabled = true;
rayline.visible = true;
marker.visible = true;
marker.position.copy(onpos);
scene.overrideMaterial = null;
renderer.setRenderTarget(null);
renderer.render( scene, xrCam );
//------- delta time statistics for the last 10 frames -----------
endms = Date.now();
deltams = endms - startms;
for (let f=avframes; f>0; f--){
tmp = fbuffer[f];
minms = Math.min(tmp, minms);
maxms = Math.max(tmp, maxms);
fbuffer[f+1]=tmp;
}
fbuffer[1] = deltams;
fcounter++;
if (fcounter === avframes){
fcounter = 0;
drawtext("max-ms:"+maxms, "min-ms:"+minms);
minms = 1000;
maxms = 0;
}
}//end render() _______________________________________________________________________________
</script>
</body>
</html>
更新 #2
Andre van Kammen の mod:モバイル (Xiaomi Redmi Note 9S) で依然として非常に大きな遅延が発生しています。
ウェブカメラで撮影した動画: https://www.bitchute.com/video/5WJxdo649KiF/
Pixel Buffer Objects (PBO) に関する詳細な記事があり ます 。スペースを押すとオフとオンが切り替わるデモを試して、ゼロ(0)の差が得られるまで!PC: http://www.songho.ca/opengl/files/pboUnpack.zip
どうやら、gl.readpixels が導入されてから 30 年経った今でも、テクノロジはいまいましいピクセルを読み取るための信頼できる効率的な方法を提供できていません。これは非常に恥ずべきことです。私はハードウェアを設計していました。何年にもわたって、すべての問題にはエレクトロニクスの解決策があり、同じことがソフトウェアや他のすべてのセクターにも当てはまります。どうやら、業界の一部では、パフォーマンス優先ではなく、ずさんさ優先のようです。私が間違っていることを証明してください。