久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

X3D:現(xiàn)代Web的聲明式3D技術(shù)

 西北望msm66g9f 2019-10-07
作者|Adrian Sureshkumar
譯者|夏夜
編輯|王文婧
現(xiàn)代 Web 技術(shù)使開(kāi)發(fā)人員能夠創(chuàng)建干凈而視覺(jué)豐富的用戶(hù)體驗(yàn),這些體驗(yàn)被所有主流瀏覽器作為標(biāo)準(zhǔn)進(jìn)行廣泛支持。那么,,如何為 Web 編寫(xiě)基于標(biāo)準(zhǔn)的可視化程序呢,?對(duì) 3D 圖形的支持到底又有哪些呢,?讓我們首先回顧 HTML 標(biāo)準(zhǔn)中支持的兩種主要方法:SVG 和 Canvas,。
SVG:可伸縮的矢量圖形 

SVG 本身是基于 XML 的一種獨(dú)立的數(shù)據(jù)格式,,用于聲明式的 2D 矢量圖形,。但是,它也可以嵌入到 HTML 文檔中,,這是所有主流瀏覽器都支持的,。

讓我們考慮一個(gè)例子,如何使用 SVG 繪制一個(gè)可調(diào)整大小的圓:
<html style='height: 100%; width: 100%'>
  <body style='height: 100%; width: 100%; margin: 0px'>
    <svg style='height: 100%; width: 100%; display: block' viewBox='0 0 100 100'>
      <circle cx='50' cy='50' r='25' fill='red' stroke='black'
              vector-effect='non-scaling-stroke' />
    </svg>
  </body>
</html>

想要理解這段代碼很容易,!我們只是向?yàn)g覽器描述了要繪制什么(與傳統(tǒng) HTML 文檔非常相似),。它保留了這個(gè)描述,并負(fù)責(zé)如何在屏幕上繪制它,。

當(dāng)瀏覽器窗口調(diào)整大小或縮放時(shí),,它將重新縮放圖像,而不會(huì)丟失圖像的任何質(zhì)量(因?yàn)閳D像是根據(jù)形狀定義的,,而不是根據(jù)像素定義的),。當(dāng) SVG 元素被 JavaScript 代碼修改時(shí),它還會(huì)自動(dòng)重新繪制圖像,,這使得 SVG 特別適合與 JavaScript 庫(kù)(如 D3)一起使用,,D3 將數(shù)據(jù)綁定到 DOM 中的元素,,從而能夠創(chuàng)建從簡(jiǎn)單圖表到更奇特的交互式數(shù)據(jù)可視化的任何內(nèi)容。

這種聲明性方法也稱(chēng)為保留模式圖形繪制(retained-mode graphics rendering),。

 畫(huà)  布 
canvas 元素只是在網(wǎng)頁(yè)上提供了一個(gè)可以繪圖的區(qū)域,。使用 JavaScript 代碼,首先從畫(huà)布獲取上下文,,然后使用提供的 API,,定義繪制圖像的函數(shù),。
const canvas = document.getElementById(id);
const context = canvas.getContext(contextType);

// call some methods on context to draw onto the canvas

當(dāng)腳本執(zhí)行時(shí),,圖像立即繪制成了底層位圖的像素,瀏覽器不保留繪制方式的任何信息,。為了更新繪圖,,需要再次執(zhí)行腳本。重新縮放圖像時(shí),,也會(huì)觸發(fā)更新繪圖,,否則,瀏覽器只會(huì)拉伸原始位圖,,導(dǎo)致圖像明顯模糊或像素化,。

這種函數(shù)式方法也稱(chēng)為即時(shí)模式圖形繪制(immediate-mode graphics rendering)。

上下文:2D

首先讓我們考慮 2D 繪制的上下文,,它提供了一個(gè)用于在畫(huà)布上繪制 2D 圖形的高級(jí) API,。

讓我們來(lái)看一個(gè)例子,看看如何使用它來(lái)繪制我們可調(diào)整大小的圓:
<html style='height: 100%; width: 100%'>
  <body style='height: 100%; width: 100%; margin: 0px'>
    <canvas id='my-canvas' style='height: 100%; width: 100%; display: block'></canvas>
    <script>
      const canvas = document.getElementById('my-canvas');
      const context = canvas.getContext('2d');

      function render() {
        // Size the drawing surface to match the actual element (no stretch).
        canvas.height = canvas.clientHeight;
        canvas.width = canvas.clientWidth;

        context.beginPath();

        // Calculate relative size and position of circle in pixels.
        const x = 0.5 * canvas.width;
        const y = 0.5 * canvas.height;
        const radius = 0.25 * Math.min(canvas.height, canvas.width);

        context.arc(x, y, radius, 0, 2 * Math.PI);

        context.fillStyle = 'red';
        context.fill();

        context.strokeStyle = 'black';
        context.stroke();
      }

      render();
      addEventListener('resize', render);
    </script>
  </body>
</html>

同樣,,這非常簡(jiǎn)單,,但肯定比前面的示例更冗長(zhǎng)!我們必須自己根據(jù)畫(huà)布的當(dāng)前大小,,以像素為單位計(jì)算圓的半徑和中心位置,。這也意味著我們必須監(jiān)聽(tīng)縮放的事件并相應(yīng)地重新繪制。

那么,,既然更加復(fù)雜,,為什么還要使用這種方法而不是 SVG 呢?在大多數(shù)情況下,,你可能不會(huì)使用該方法,。然而,這給了你對(duì)渲染的內(nèi)容更多的控制,。對(duì)于要繪制更多對(duì)象的,、更復(fù)雜的動(dòng)態(tài)可視化,它可能比更新 DOM 中的大量元素,,并讓瀏覽器來(lái)決定何時(shí)呈現(xiàn)和呈現(xiàn)什么,,帶來(lái)更好的性能,。

上下文:WebGL 

大多數(shù)現(xiàn)代瀏覽器也支持 webgl 上下文。這為您提供了使用 WebGL 標(biāo)準(zhǔn)繪制硬件加速圖形的底層 API,,盡管這需要 GPU 支持,。它可以用來(lái)渲染 2D,更重要的是,,也可以用來(lái)渲染本篇博客所說(shuō)的 3D 圖形,。

現(xiàn)在讓我們來(lái)看一個(gè)例子,看看如何使用 WebGL 渲染我們的圓圈:
<html style='height: 100%; width: 100%'>
  <body style='height: 100%; width: 100%; margin: 0px'>
    <canvas id='my-canvas' style='height: 100%; width: 100%; display: block'></canvas>
    <script>
      const canvas = document.getElementById('my-canvas');
      const context = canvas.getContext('webgl');

      const redColor = new Float32Array([1.0, 0.0, 0.0, 1.0]);
      const blackColor = new Float32Array([0.0, 0.0, 0.0, 1.0]);

      // Use an orthogonal projection matrix as we're rendering in 2D.
      const projectionMatrix = new Float32Array([
        1.0, 0.0, 0.0, 0.0,
        0.0, 1.0, 0.0, 0.0,
        0.0, 0.0, 0.0, 0.0,
        0.0, 0.0, 0.0, 1.0,
      ]);

      // Define positions of the vertices of the circle (in clip space).
      const radius = 0.5;
      const segmentCount = 360;
      const positions = [0.0, 0.0];
      for (let i = 0; i < segmentCount + 1; i++) {
          positions.push(radius * Math.sin(2 * Math.PI * i / segmentCount));
        positions.push(radius * Math.cos(2 * Math.PI * i / segmentCount));
      }

      const positionBuffer = context.createBuffer();
      context.bindBuffer(context.ARRAY_BUFFER, positionBuffer);
      context.bufferData(context.ARRAY_BUFFER, new Float32Array(positions), context.STATIC_DRAW);

      // Create shaders and program.
      const vertexShader = context.createShader(context.VERTEX_SHADER);
      context.shaderSource(vertexShader, `
        attribute vec4 position;
        uniform mat4 projection;

        void main() {
          gl_Position = projection * position;
        }
      `);
      context.compileShader(vertexShader);

      const fragmentShader = context.createShader(context.FRAGMENT_SHADER);
      context.shaderSource(fragmentShader, `
        uniform lowp vec4 color;

        void main() {
          gl_FragColor = color;
        }
      `);
      context.compileShader(fragmentShader);

      const program = context.createProgram();
      context.attachShader(program, vertexShader);
      context.attachShader(program, fragmentShader);
      context.linkProgram(program);

      const positionAttribute = context.getAttribLocation(program, 'position');

      const colorUniform = context.getUniformLocation(program, 'color');
      const projectionUniform = context.getUniformLocation(program, 'projection');

      function render() {
        // Size the drawing surface to match the actual element (no stretch).
        canvas.height = canvas.clientHeight;
        canvas.width = canvas.clientWidth;

        context.viewport(0, 0, canvas.width, canvas.height);

        context.useProgram(program);

        // Scale projection to maintain 1:1 ratio between height and width on canvas.
        projectionMatrix[0] = canvas.width > canvas.height ? canvas.height / canvas.width : 1.0;
        projectionMatrix[5] = canvas.height > canvas.width ? canvas.width / canvas.height : 1.0;
        context.uniformMatrix4fv(projectionUniform, false, projectionMatrix);

        const vertexSize = 2;
        const vertexCount = positions.length / vertexSize;

        context.bindBuffer(context.ARRAY_BUFFER, positionBuffer);
        context.vertexAttribPointer(positionAttribute, vertexSize, context.FLOAT, false, 0, 0);
        context.enableVertexAttribArray(positionAttribute);

        context.uniform4fv(colorUniform, redColor);
        context.drawArrays(context.TRIANGLE_FAN, 0, vertexCount);

        context.uniform4fv(colorUniform, blackColor);
        context.drawArrays(context.LINE_STRIP, 1, vertexCount - 1);
      }

      render();
      addEventListener('resize', render);
    </script>
  </body>
</html>

復(fù)雜度升級(jí)得相當(dāng)快,!在我們渲染任何東西之前,,要做很多設(shè)置。我們必須使用頂點(diǎn)列表,,將圓定義為由小三角形組成的一個(gè)序列,。我們還必須定義一個(gè)投影矩陣,將我們的 3D 模型(一個(gè)平面圓)投影到 2D 畫(huà)布上,。然后,,我們必須編寫(xiě)“著色器”(用一種稱(chēng)為 GLSL 的語(yǔ)言),在 GPU 上編譯并運(yùn)行,,以確定頂點(diǎn)的位置和顏色,。

但是,額外的復(fù)雜性和較底層的 API,,確實(shí)能夠讓我們更好地控制 2D 圖形繪制的性能(如果我們真的需要的話),。它還為我們提供了渲染 3D 可視化的能力,即使我們還沒(méi)有考慮過(guò)這樣的例子,。

面向聲明式的 3D 圖形 

現(xiàn)在我們已經(jīng)了解了 WebGL,,并了解了如何使用它來(lái)繪制一個(gè)圓。隨著我們進(jìn)入 3D 圖形的世界,,下一個(gè)步驟就是使用它來(lái)繪制一個(gè)球體,。然而,這增加了另一層次的復(fù)雜性,,因?yàn)槲覀儗⒁伎?,如何使用一組頂點(diǎn)來(lái)表示球面。我們還需要添加一些燈光效果,,這樣我們就可以看到一個(gè)球體的輪廓,,而不是從任何角度都只能看到一個(gè)平坦的紅色圓圈。

我們還看到,,對(duì)于絕對(duì)性能并不重要的場(chǎng)景,,SVG 等簡(jiǎn)單而簡(jiǎn)潔的聲明式方法可以發(fā)揮多大的作用。它們還可以讓我們使用 D3 這樣的庫(kù),輕松地生成與數(shù)據(jù)連接起來(lái)的可視化,。所以,,如果我們能以類(lèi)似的方式表示基于 Web 的 3D 圖形,那不是更好嗎?

遺憾的是,,目前 HTML 中的標(biāo)準(zhǔn)還不支持這個(gè)操作,。但也許還有另一種方法……

正如 Mike Bostock(D3 的創(chuàng)建者)在 POC(Proof of Concept)中所演示的,在 DOM 中定義 2D“素描”的定制化 XML 表示,,并將其與一些 JavaScript 代碼結(jié)合,,使用 2D 上下文將其繪制到畫(huà)布上,這相對(duì)來(lái)說(shuō)會(huì)更加簡(jiǎn)單,。

這意味著在所有主流瀏覽器上運(yùn)行的聲明式 3D 真正需要的是:
  • 基于 XML 格式的 3D 模型聲明,;
  • 使用 webgl 上下文將它們繪制到畫(huà)布上的 JavaScript 代碼。

 X3D ——拼圖中缺失的這塊,? 

X3D 是表示 3D 模型的 ISO 標(biāo)準(zhǔn),,是虛擬現(xiàn)實(shí)建模語(yǔ)言(VRML)的后續(xù)標(biāo)準(zhǔn),。它可以表示為各種編碼,,包括 JSON 和 XML。后者特別適合嵌入到 HTML 文檔中,。它由 Web3D 聯(lián)盟維護(hù),,他們希望它能像 SVG 一樣在 HTML5 中得到原生支持。

目前有兩種被 Web3D 聯(lián)盟認(rèn)可的 JavaScript 開(kāi)源 X3D 實(shí)現(xiàn):X3DOM 和 X_ite,。

X3DOM 是由弗勞恩霍夫計(jì)算機(jī)圖形研究所 IGD(The Fraunhofer Institute for Computer Graphics Research IGD)開(kāi)發(fā)的,,IGD 本身也是 Web3D 聯(lián)盟的成員。為了使用它,,您只需要在 HTML 頁(yè)面中包含 X3DOM JavaScript 代碼和樣式表,。

讓我們來(lái)看看用 X3D 和 X3DOM 繪制圓圈的例子:
<html style='height: 100%; width: 100%'>
  <head>
    <script type='text/javascript' src='http://www./release/x3dom-full.js'></script>
    <link rel='stylesheet' type='text/css' href='http://www./release/x3dom.css'>
    <style>x3d > canvas { display: block; }</style>
  </head>
  <body style='height: 100%; width: 100%; margin: 0px'>
    <x3d style='height: 100%; width: 100%'>
      <scene>
        <orthoviewpoint></orthoviewpoint>
        <shape>
          <appearance>
            <material diffuseColor='1 0 0'></material>
          </appearance>
          <disk2d outerRadius='0.5'></disk2d>
        </shape>
        <shape>
          <appearance>
            <material emissiveColor='0 0 0'></material>
          </appearance>
          <circle2d radius='0.5'></circle2d>
        </shape>
      </scene>
    </x3d>
  </body>
</html>

這比 WebGL 示例更容易接受一些!但是,,如果您將 X3DOM 圓與我們的 WebGL 版本進(jìn)行比較,,您會(huì)注意到圓周看起來(lái)不那么光滑。這是因?yàn)?X3DOM 庫(kù)對(duì)形狀的近似只使用了 32 條線段,。而我們的 WebGL 繪制中選擇了 360 條線段,。我們對(duì)要渲染什么有一個(gè)更簡(jiǎn)單的描述,但同時(shí)也會(huì)放棄對(duì)如何渲染的一些控制,。

現(xiàn)在是時(shí)候走出我們的“平面”世界,,渲染一些 3D 的東西了!如前所述,,讓我們來(lái)看看一個(gè)球體的繪制:
<html style='height: 100%; width: 100%'>
  <head>
    <script type='text/javascript' src='http://www./release/x3dom-full.js'></script>
    <link rel='stylesheet' type='text/css' href='http://www./release/x3dom.css'>
    <style>x3d > canvas { display: block; }</style>
  </head>
  <body style='height: 100%; width: 100%; margin: 0px'>
    <x3d style='height: 100%; width: 100%'>
      <scene>
        <orthoviewpoint></orthoviewpoint>
        <navigationinfo headlight='false'></navigationinfo>
        <directionallight direction='1 -1 -1' on='true' intensity='1.0'></directionallight>
        <shape>
          <appearance>
            <material diffuseColor='1 0 0'></material>
          </appearance>
          <sphere radius='0.5'></sphere>
        </shape>
      </scene>
    </x3d>
  </body>
</html>

這又是很直接的,。我們使用一個(gè) XML 元素定義了一個(gè)球體,該元素具有單一屬性:半徑。為了看到球體的輪廓,,我們還調(diào)整了光線,,移除了與觀察者頭部對(duì)齊的默認(rèn)光源,并用與我們視角成一定角度的定向光替換它,。這不需要為球體的表面定義一個(gè)復(fù)雜的網(wǎng)格或者編寫(xiě)一個(gè)著色器來(lái)控制光照效果,。

X3DOM 還提供了開(kāi)箱即用的導(dǎo)航功能,允許您旋轉(zhuǎn),、平移和縮放模型,。根據(jù)您正在編寫(xiě)的應(yīng)用程序的類(lèi)型,還可以使用各種不同的控制方案和導(dǎo)航模式,。

 結(jié)  論 

就是這樣,!我們已經(jīng)看到可以使用 X3D 和 X3DOM 庫(kù)來(lái)編寫(xiě)聲明式的 3D 圖形應(yīng)用,這些圖形將在大多數(shù)現(xiàn)代 Web 瀏覽器中運(yùn)行,。這是一種比直接深鉆 WebGL 更簡(jiǎn)單的 Web 3D 圖形入門(mén)方法,,代價(jià)是增加對(duì)底層繪制的控制。如果您有興趣了解這個(gè)庫(kù)的更多信息,,官方 X3DOM 文檔中有一些教程,。

在我的下一篇博客文章中,我將演示如何將 X3DOM 與 D3 結(jié)合起來(lái)生成動(dòng)態(tài) 3D 圖表,。

英文原文:

https://blog./2019/08/27/declarative-3d-for-the-modern-web.html

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多