3D 图像,技术,打造产品,还有互联网:这些只是我爱好的一小部分。
现在,感谢 webGL 的出现-一个新的 JavaScriptAPI,它可以在不依赖任何插件的情况下渲染浏览器中的 3D 图像-这让 3D 渲染操作变得异常简单。
随着虚拟现实和增强现实应用的发展,大型厂商们开始转向数字化触觉体验,这是令人动心的一项技术。
或者,至少那些已经投资的人这一年还抱有希望- 11 亿美金流入 VR 和 AR 领域 .
从 Abbey Road Studios 的谷歌交互之旅 到 拍摄 Deadliest Catch 用到的舰队 ,通过在非真实世界给观众沉浸式的体验,所有的产品,服务和环境得以实现更好的配合。
由于人们能接触到更多体验性的技术,2D 开始变得有些单调。这是事实。
让我们现实点。目前看来,很多致力于创造体验的应用仍处在技术探索阶段,对大多数商业领域而言前景不算明朗。
或者说他们真的创造了令人激动的体验吗?
走进 WebGL: 一项实用与灵活的技术,可以创造更强沉浸式 3D 内容。无论是 Porsche 展示 一辆新 911 的细节 ,还是 NASA 重点介绍的 火星是什么样子 ,或者是 J Dlla 备受喜爱的甜甜圈专辑庆典 ,WebGL 能应用于很多领域来表现各种类型的故事。
为让你熟悉这项强大的技术,我打算做一个关于它是如何工作的简要概括,还有使用 Three.js(一个基于 WebGL API 的 JavaScript 库) 一步步创造简单 3D 环境的快速教程。
WebGL 是一项在浏览器中展示基于硬件加速的 3D 图像的 web 技术,不需要安装额外插件或者下载多余的软件。
因此,很多受众可以更方便地接触到 WebGL。浏览器支持程度也很不错 ( 目前应用广泛 ),Chrome,Firefox,IE,Opera 和 Safari 等主流移动端和桌面浏览器都提供了很好的支持。
许多计算机和智能手机有先进的图像渲染单元 (GPUS),可直到最近,大多数网页和移动网站都不能使用 GPUS。这导致设备的加载速度缓慢,图像质量低,并且对 3D 内容的支持程度也很低。
为了解决这个问题 WebGL 花了不少时间。基于著名的 OpenGL 3D 图像标准 ,WebGL 赋予 Javascript 插件式的自由接入方式,通过 html5 元素连接一个设备的图像硬件,并在浏览器中直接应用 3D 技术。结果是 360 度的 3D 内容变得更容易创建—排除了使用独立应用或插件的干扰——同时用户能更容易地在网上拥有高清体验。
OpenGL 和 WebGL 的复杂度相差不大。
Three.js 是一个开源语法库,简化了 WebGL 工具和环境的创建工作。它支持大部分基于 GPU 加速的低代码量 3D 动画。
示例用 Three.js 库展示了更复杂的效果。为了练习需要,我会尽量写的简单,用低复杂度的环境来展示仅靠理解的基础知识能实现什么效果。
我打算构建一个我们已使用过的 例子
贡献者 Matt Agar( @agar )
代码发布于 CodePen .
点击并拖动这个例子,做点尝试
CodePen 上的例子相当于入门,现在我们开始使用 Three.js。
Firstly we need a Scene — a group or stage containing all the objects we want to render. Scenes allow you to set up what and where is going to be rendered by Three.js. This is where you place objects, lights, and cameras. 首先我们需要一个场景 — 一个包含了我们要渲染的所有对象的群组。场景允许你设置 Three.js 要渲染的对象和渲染位置,以及如何进行渲染。这个场景指的就是你放置对象,光线和相机的地方。
- `var scene = new THREE.Scene();`
- 用一个好方法创建场景
接下来我们在这个例子中添加一个相机。我添加的是透视相机,但也有其他可用的选项。头两个参数分别指明了相机的视野区域和宽高比。后两个参数代表相机渲染对象的截止距离。
- var camera = new THREE.PerspectiveCamera(75, // Field of view
- window.innerWidth / window.innerHeight, // Aspect ratio
- 0.1, // Near clipping pane
- 1000 // Far clipping pane
- );
- // Reposition the camera
- camera.position.set(5, 5, 0);
- // Point the camera at a given coordinate
- camera.lookAt(new THREE.Vector3(0, 0, 0));
- 添加相机,视场,宽高比和截止距离
最后至关重要的部分是渲染器本身,它掌握着一个来自给定相机视角场景的渲染。Three.js 提供了很多种渲染器以供选择,但我决定在这个练习中使用标准的 WebGL 渲染器。
- var renderer = new THREE.WebGLRenderer({ antialias: true });
- // Size should be the same as the window
- renderer.setSize( window.innerWidth, window.innerHeight );
- // Set a near white clear color (default is black)
- renderer.setClearColor( 0xeeeeee );
- // Append to the document
- document.body.appendChild( renderer.domElement );
- // Render the scene/camera combination
- renderer.render(scene, camera);
- 添加渲染器
这个例子也包括了一些基础的几何结构— 在这里是一个扁平的平面 — 我们可以看到一些特征以深度形式被渲染出来。如果没有它,我们只能看到空空的屏幕。我接下来会简短介绍关于 Geometry(几何结构),Materials(材质) 和 Meshes(网格)。
- // A mesh is created from the geometry and material, then added to the scene
- var plane = new THREE.Mesh(new THREE.PlaneGeometry(5, 5, 5, 5), new THREE.MeshBasicMaterial({
- color: 0x222222,
- wireframe: true
- }));
- plane.rotateX(Math.PI / 2);
- scene.add(plane);
- 添加一个扁平的平面
你可能已经意识到我在这个例子里使用了外部模块。这个是 Three.js 的 Github repo 里能找到的众多可用模块的一个。
在这个例子里是 轨道控制 , 它允许我们捕获 canvas 元素上的鼠标事件以重新定位围绕着场景的相机。
- var controls = new THREE.OrbitControls( camera, renderer.domElement );
- controls.addEventListener( 'change', function() { renderer.render(scene, camera); } );
- 实现轨道控制
在 CodePen 例子中从动作,点击和拖放或者滚动鼠标轮等方面检验轨道控制。在这个示例中,由于我们没有设置动作循环 (一旦我开始装饰我的圣诞树,我就会介绍动作循环),当控制发生更新时我们同样需要重新渲染场景。
好吧,之前的例子现在可能看着有点蠢,但你在没有硬件基础的情况下无法构建一个更好的屋子或圣诞树。
是时候给我们的场景添些东西了,现在有三件事需要我们去探索:Geometries,Materials, 还有 Meshes。
代码链接 第二步
贡献者 Matt Agar( @agar )
- `var geometry = new THREE.OctahedronGeometry(10,1);`
- var material = new THREE.MeshStandardMaterial( {
- color: 0xff0051,
- shading: THREE.FlatShading, // default is THREE.SmoothShading
- metalness: 0,
- roughness: 1
- } );
- var shapeOne = new THREE.Mesh(geometry, material);
- shapeOne.position.y += 10;
- scene.add(shapeOne);
- var ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
- scene.add(ambientLight);
- var pointLight = new THREE.PointLight(0xffffff, 1);
- pointLight.position.set(25, 50, 25);
- scene.add(pointLight);
- renderer.shadowMap.enabled = true;
- renderer.shadowMap.type = THREE.PCFSoftShadowMap;
- pointLight.castShadow = true;
- pointLight.shadow.mapSize.width = 1024;
- pointLight.shadow.mapSize.height = 1024;
- shapeOne.castShadow = true;
- shapeOne.receiveShadow = true;
- var shadowMaterial = new THREE.ShadowMaterial( { color: 0xeeeeee } );
- shadowMaterial.opacity = 0.5;
- var Decoration = function() {
- // Run the Group constructor with the given arguments
- THREE.Group.apply(this, arguments);
- // A random color assignment
- var colors = ['#ff0051', '#f56762','#a53c6c','#f19fa0','#72bdbf','#47689b'];
- // The main bauble is an Octahedron
- var bauble = new THREE.Mesh(
- addNoise(new THREE.OctahedronGeometry(12,1), 2),
- new THREE.MeshStandardMaterial( {
- color: colors[Math.floor(Math.random()*colors.length)],
- shading: THREE.FlatShading ,
- metalness: 0,
- roughness: 1
- } )
- );
- bauble.castShadow = true;
- bauble.receiveShadow = true;
- bauble.rotateZ(Math.random()*Math.PI*2);
- bauble.rotateY(Math.random()*Math.PI*2);
- this.add(bauble);
- // A cylinder to represent the top attachment
- var shapeOne = new THREE.Mesh(
- addNoise(new THREE.CylinderGeometry(4, 6, 10, 6, 1), 0.5),
- new THREE.MeshStandardMaterial( {
- color: 0xf8db08,
- shading: THREE.FlatShading ,
- metalness: 0,
- roughness: 1
- } )
- );
- shapeOne.position.y += 8;
- shapeOne.castShadow = true;
- shapeOne.receiveShadow = true;
- this.add(shapeOne);
- };
- Decoration.prototype = Object.create(THREE.Group.prototype);
- Decoration.prototype.constructor = Decoration;
- var decoration = new Decoration();
- decoration.position.y += 10;
- scene.add(decoration);
- function addNoise(geometry, noiseX, noiseY, noiseZ) {
- var noiseX = noiseX || 2;
- var noiseY = noiseY || noiseX;
- var noiseZ = noiseZ || noiseY;
- for (var i = 0; i & lt; geometry.vertices.length; i++) {
- var v = geometry.vertices[i];
- v.x += -noiseX / 2 + Math.random() * noiseX;
- v.y += -noiseY / 2 + Math.random() * noiseY;
- v.z += -noiseZ / 2 + Math.random() * noiseZ;
- }
- return geometry;
- }
- requestAnimationFrame(render);
- function render() {
- // Update camera position based on the controls
- controls.update();
- // Re-render the scene
- renderer.render(scene, camera);
- // Loop
- requestAnimationFrame(render);
- }
- this.rotationSpeed = Math.random() * 0.02 + 0.005;
- this.rotationPosition = Math.random();
- Decoration.prototype.updatePosition = function() {
- this.rotationPosition += this.rotationSpeed;
- this.rotation.y = (Math.sin(this.rotationPosition));
- };
- function render() {
- // Update camera position based on the controls
- controls.update();
- // Loop through items in the scene and update their position
- for (var d = 0; d & lt; decorations.length; d++) {
- decorations[d].updatePosition();
- }
- // Re-render the scene
- renderer.render(scene, camera);
- // Loop
- requestAnimationFrame(render);
- }
来源: https://sdk.cn/news/7897