最近有人问到我如何系统的学习 Three.js,我给他推荐了知乎上的一篇文章《如何系统的学习three.js?》。从这里我们可以看出,学习 Three.js,最好要有一些 WebGL 基础。基于此,本文希望通过一个最简单的例子来说明他们之间的关系。
关于还不知道 WebGL 和 Three.js 的,建议大家先阅读一下我的这篇文章《Three.js 简介》。
WebGL 是一个底层的标准,在这些标准被定义之后,Chrome、Firefox之类的浏览器实现了这些标准。然后,程序员就能通过 JavaScript 代码,在网页上实现三维图形的渲染了。但是直接使用 WebGL 太过复杂,同样的功能需要更多的代码,于是 Three.js 应用而生,它相当于一个实现框架。只需更少的代码即可实现更强大的功能。
为了比较说明 Three.js 能大大简化 WebGL 的开发,我们使用最简单的例子进行比较:渲染黑色背景下的白色正方形和三角形。效果如图如下:
Three.js 需要30行左右的代码即可实现:
var renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('mainCanvas') }); renderer.setClearColor(0x000000); // black var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(45, 4 / 3, 1, 1000); camera.position.set(0, 0, 5); camera.lookAt(new THREE.Vector3(0, 0, 0)); scene.add(camera); var material = new THREE.MeshBasicMaterial({ color: 0xffffff // white }); // plane :www.xttblog.com var planeGeo = new THREE.PlaneGeometry(1.5, 1.5); var plane = new THREE.Mesh(planeGeo, material); plane.position.x = 1; scene.add(plane); // triangle var triGeo = new THREE.Geometry(); triGeo.vertices = [new THREE.Vector3(0, -0.8, 0), new THREE.Vector3(-2, -0.8, 0), new THREE.Vector3(-1, 0.8, 0)]; triGeo.faces.push(new THREE.Face3(0, 2, 1)); var triangle = new THREE.Mesh(triGeo, material); scene.add(triangle); renderer.render(scene, camera);
我们再来看看 WebGL 实现该功能的代码:
var gl; function initGL(canvas) { try { gl = canvas.getContext("experimental-webgl"); gl.viewportWidth = canvas.width; gl.viewportHeight = canvas.height; } catch (e) {} if (!gl) { alert("Could not initialise WebGL, sorry :-("); } } function getShader(gl, id) { var shaderScript = document.getElementById(id); if (!shaderScript) { return null; } var str = ""; var k = shaderScript.firstChild; while (k) { if (k.nodeType == 3) { str += k.textContent; } k = k.nextSibling; } var shader; if (shaderScript.type == "x-shader/x-fragment") { shader = gl.createShader(gl.FRAGMENT_SHADER); } else if (shaderScript.type == "x-shader/x-vertex") { shader = gl.createShader(gl.VERTEX_SHADER); } else { return null; } gl.shaderSource(shader, str); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert(gl.getShaderInfoLog(shader)); return null; } return shader; } var shaderProgram; function initShaders() { var fragmentShader = getShader(gl, "shader-fs"); var vertexShader = getShader(gl, "shader-vs"); shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram); if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert("Could not initialise shaders"); } gl.useProgram(shaderProgram); shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); } var mvMatrix = mat4.create(); var pMatrix = mat4.create(); function setMatrixUniforms() { gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix); gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix); } var triangleVertexPositionBuffer; var squareVertexPositionBuffer; function initBuffers() { triangleVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); var vertices = [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); triangleVertexPositionBuffer.itemSize = 3; triangleVertexPositionBuffer.numItems = 3; squareVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); vertices = [ 1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0 ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); squareVertexPositionBuffer.itemSize = 3; squareVertexPositionBuffer.numItems = 4; } function drawScene() { gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix); mat4.identity(mvMatrix); mat4.translate(mvMatrix, [-1.5, 0.0, -7.0]); gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); setMatrixUniforms(); gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems); // :www.xttblog.com mat4.translate(mvMatrix, [3.0, 0.0, 0.0]); gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); setMatrixUniforms(); gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems); } function webGLStart() { var canvas = document.getElementById("lesson01-canvas"); initGL(canvas); initShaders(); initBuffers(); // :www.xttblog.com gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.enable(gl.DEPTH_TEST); drawScene(); }
从上面的代码我们不难发现,使用原生 WebGL 接口实现同样功能需要5倍多的代码量,而且很多代码对于没有图形学基础的程序员是很难看懂的。由这个例子我们可以看出,使用 Three.js 开发要比 WebGL 更快更高效。尤其对图形学知识不熟悉的程序员而言,使用 Three.js 能够降低学习成本,提高三维图形程序开发的效率。
: » WebGL vs Three.js
原创文章,作者:254126420,如若转载,请注明出处:https://blog.ytso.com/251355.html