A-Frame + pdf.js:无法更新 canvas 纹理两次,仅在 VR 模式下

A-Frame + pdf.js: unable to update canvas texture twice, in VR mode only

我正在使用 A-Frame 和 pdf.js 创建应用程序以在 VR 中查看 PDF 文件。该应用程序在桌面上按预期工作,包括前进到 PDF 文档的下一页并重新呈现为 canvas.

当 运行 在 VR 中的浏览器中(使用 Quest 2 测试)时,第一次尝试按预期呈现。但是,当将第二个文档页面渲染到 canvas 时,新图像无法出现 ,除非 退出 VR 模式,此时新纹理会按预期出现。

我已经尝试将关联的 material map.needsUpdate 重复设置(在 A-Frame 组件滴答循环中)无效。在试验时,我还注意到,如果您尝试第二次渲染一个新的 PDF 页面,然后第三次渲染另一个页面(在 VR 模式下前进两页),当退出 VR 模式时,只会出现第二次的纹理;第三次渲染的数据似乎丢失了 - 我想知道这是否与主要问题有关。

下面是一个最小示例的代码,http://stemkoski.net/test/quest-pdf-min.html 提供了实时版本。在桌面模式下查看示例时,您可以通过打开浏览器 JavaScript 控制台并输入 testBook.nextPage(); 前进到下一页,并看到图像按预期变化。您可以使用 Quest 耳机在 VR 模式下进行测试;按 A 按钮应前进到下一页。

问题是:如何在 A-Frame 的 VR 模式下多次将 PDF 文档的页面呈现为相同 canvas?

<!DOCTYPE html>
<html>

<head>
    <title>A-Frame + PDF.js</title>
    <script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.js"></script>
</head>

<body>
<script>
AFRAME.registerComponent("interactive-pdf", {

    schema:
    {   
        fileName:   {type: 'string', default:"assets/test10.pdf"},
        pageWidth:  {type: 'float',  default: 1200},
        pageHeight: {type: 'float',  default: 1500},
    },

    init: function () 
    {
        this.displayPlane = document.getElementById("plane1");
        this.displayMaterial = this.displayPlane.getObject3D('mesh').material;
        this.canvas = this.displayPlane.getAttribute("material").src
        this.canvas.setAttribute("width", this.data.pageWidth);
        this.canvas.setAttribute("height", this.data.pageHeight);
        this.context = this.canvas.getContext('2d');
        this.textArea = document.querySelector("#textArea");

        this.pdf = null;
        this.currentPage = 1;

        pdfjsLib.GlobalWorkerOptions.workerSrc = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.worker.js";

        let self = this;
        pdfjsLib.getDocument(this.data.fileName).promise.then( function(pdf) {
            self.pdf = pdf;
            self.render(1);
        });
    },

    render(pageNumber = 1)
    {
        if (!this.pdf)
            return;

        let self = this;

        let text = "rendering page " + pageNumber + " of " + this.pdf.numPages + " on canvas";
        this.textArea.setAttribute("text", "value", text);

        this.pdf.getPage(pageNumber).then( function(page) {

            const pageViewport = page.getViewport({ scale: 2 });
            const context = self.canvas.getContext("2d");
            const renderContext = {
              canvasContext: context,
              viewport: pageViewport
            };

            const renderTask = page.render(renderContext);

            renderTask.promise.then( function() {
                self.displayMaterial.map.needsUpdate = true;
            });    
        });
    },

    nextPage: function()
    {
        this.currentPage++;
        this.render( this.currentPage );
    },

    prevPage: function()
    {
        this.currentPage--;
        this.render( this.currentPage );
    },

    tick: function()
    {
        // this.displayMaterial.map.needsUpdate = true;
    }

});

AFRAME.registerComponent('page-turner', {
  
  init: function () 
  {
      let bookComponent = document.getElementById("interactive-pdf").components["interactive-pdf"];
      this.el.addEventListener('abuttondown', function(event) { bookComponent.nextPage(); } );
  }

});
</script>

<a-scene>
        
    <a-assets>
        <!-- use to display pdf pages -->
        <canvas id="canvas1"></canvas>
    </a-assets>

    <a-camera></a-camera>

    <a-sky color = "#000337"></a-sky>

    <a-plane id="plane1" material="shader: flat; src: #canvas1;" position="0 1 -2"></a-plane>

    <a-entity id="interactive-pdf" position="0 1 -2" 
        interactive-pdf="fileName: assets/test10.pdf; pageWidth: 1224; pageHeight: 1584;">
    </a-entity>
        
    <a-entity id="left-controller" oculus-touch-controls="hand: left"></a-entity>
    <a-entity id="right-controller" oculus-touch-controls="hand: right" page-turner></a-entity>   

    <!-- text area to print debug text -->
    <a-entity id="textArea" position="0 2 -2"
        geometry="primitive: plane;  width: 3; height: auto"
        material="color: #444444;"
        text="anchor: center; color: #8888FF; value: debug text here">
    </a-entity>

</a-scene>

<script>
var testBook = document.getElementById("interactive-pdf").components["interactive-pdf"];
</script>

</body>
</html>

找到答案here:

requestAnimationFrame doesn't fire when WebXR is engaged

它非常相关,因为 pdf.js 在将图像渲染到 canvas 上时默认使用 requestAnimationFrame

虽然可以切换 - pages render function has an option intent, which is later on used to determine whether to use requestAnimationFrame


您使用的来源略有不同(较旧?),但如果您在 ProxyPDFPagerender 函数中搜索创建 InternalRenderTask - 都在那里:

const internalRenderTask = new InternalRenderTask({
  callback: complete,
  params: {
    canvasContext,
    viewport,
    transform,
    imageLayer,
    background
  },
  objs: this.objs,
  commonObjs: this.commonObjs,
  operatorList: intentState.operatorList,
  pageIndex: this._pageIndex,
  canvasFactory: canvasFactoryInstance,
  webGLContext,
  useRequestAnimationFrame: renderingIntent !== "print",
  pdfBug: this._pdfBug
});

您可以在 renderContext 对象中将 intent 设置为 print

// 'interactive-pdf'.render()
const renderContext = {
          canvasContext: context,
          viewport: pageViewport,
          intent: "print"
};
const renderTask = page.render(renderContext);

它似乎工作正常:

我不确定,如果将意图设置为 print 不会造成不良影响(它也会影响其他设置),所以在源中禁用 useRequestAnimationFrame 就可以了嗯。

查看 the version with the 'print' intent, or the one with the modified pdfjs source code