前端与图形学(上)

  1. 引言-计算机图形的应用和研究
    图形 :图形是人类传达知识、表达情感的重要手段,它通常指能在人在视觉系统中产生视觉印象的客观对象。抽象=》具体
    计算机图形( Computer graphics(CG): ):计算机图形学中所研究的图形是指能够通过计算机加以表示;
    WIkpedia: How to represent graphics in computer, compute, processing and display of grafic (如何去表示图形,图元)

图形学工程师的范畴
0.1 计算机图形学研究内容
引子: https://dragonir.github.io/3d/#/olympic
● 图形数据结构及点,线,圆,多边形等基本图元的生成,进一步地,如何生成曲线和曲面 =》三维世界
● 基本图元的几何变换,投影变换,窗口裁剪 => 投影
● 图形的实时显示和并行计算 =>动画
● 插值算法、光影变换和粒子系统 【光照】=》材质
0.2 相关应用

  1. 图形用户界面
  2. 计算机辅助设计与制造 (CAD)
  3. 科学数据可视化 (echarts, icharts, bizcharts)
  4. 计算机动画 (flash)https://threejs.org/examples/css3d_sprites.html
  5. 地理信息系统(GIS)
  6. 虚拟现实、增强现实 (AR, VR)
  7. 计算机图形学基础
    1.1 图元装配 primitive assembly
    什么是图元?
    描述各种图形元素的函数叫做图元,描述几何元素的称为几何图元(点,线段或多边形)。
    点和线是最简单的几何图元 经过顶点着色器计算之后的坐标会被组装成组合图元。
    通俗解释:图元就是一个点、一条线段、或者是一个多边形。

什么是图元装配呢?

简单理解就是说将我们设置的顶点、颜色、纹理等内容组装称为一个可渲染的多边形的过程。

组装的类型取决于: 你最后绘制选择的图形类型
gl.drawArrays(gl.TRIANGLES, 0, 3)
1.2 光栅化 Rasterization
光栅化:把顶点数据转换为片元的过程(简单理解就是找到图形并转换所覆盖的像素)
前端与图形学(上)_第1张图片
图形的绘制过程

  1. CPU 计算节点基础信息(数量)
  2. GPU Vertex Shader 进行顶点绘制
  3. primitives Generation 负责结构生成(link/三角形)
  4. Rasterization 光栅化
  5. Fragment Shader 片元 (着色)
  6. Testing Blending 混合测试 (Alpha透明度)
  7. final render 渲染(图像呈现)

1.3 片元着色器 Fragment Shader
光珊化后,每一个像素点都包含了 颜色 、深度 、纹理数据, 这个我们叫做片元 接收光栅化阶段生成的片元,在光栅化阶段中,已经计算出每个片元的颜色信息,这一阶段会将片元做逐片元挑选的操作,处理过的片元会继续向后面的阶段传递。 片元着色器运行的次数由图形有多少个片元决定的。
2. 前端与图形学的结合
2.1 webgl是什么:
webgl 是在网页上绘制和渲染三维图形的技术,可以让用户与其进行交互。
前端与图形学(上)_第2张图片
前端与图形学(上)_第3张图片

                                                            图1 webGL及其起源

我们首先来看一个简单的webgl 着色器的例子:[用背景色来清空canvas标签的绘图区域] 更多例子详见gitub:
https://github.com/buglas/webgl-lesson

2.2 WebGL的坐标系
webgl画布的建立和获取,和canvas 2d是一样的。一旦我们使用canvas.getContext()方法获取了webgl 类型的上下文对象,那这张画布就不再是以前的canvas 2d 画布。当然,它也不会变成三维的,因为我们的电脑屏幕始终是平的。那这张画布有什么不一样了呢?它的坐标系变了。canvas 2d 画布和webgl 画布使用的坐标系都是二维直角坐标系,只不过它们坐标原点、y 轴的坐标方向,坐标基底都不一样了。
2.2.1 canvas 2d画布的坐标系
canvas 2d 坐标系的原点在左上角。canvas 2d 坐标系的y 轴方向是朝下的。canvas 2d 坐标系的坐标基底有两个分量,分别是一个像素的宽和一个像素的高,即1个单位的宽便是1个像素的宽,1个单位的高便是一个像素的高。如下图,下图两个方块表示两个像素:
前端与图形学(上)_第4张图片
2.2.2 webgl的坐标系
webgl坐标系的坐标原点在画布中心。webgl坐标系的y 轴方向是朝上的。webgl坐标基底中的两个分量分别是半个canvas的宽和canvas的高,即1个单位的宽便是半个个canvas的宽,1个单位的高便是半个canvas的高。如下图:
前端与图形学(上)_第5张图片
前端与图形学(上)_第6张图片

参考文献: https://github1s.com/buglas/webgl-lesson https://juejin.cn/post/6981444627270205453
2.2.3 Canvas和webGL绘图的差异
在讲解canvas和webGL绘图的差异之前,我们要搞清楚着色器这个概念:着色器是为了帮助我们描述顶点,变换,材质,光源和与我们相机之间关系的重要的存在,在代码中是以字符串的形式嵌在js文件中的。

分为顶点着色器和片元着色器。
● 顶点着色器(Vertex shader):描述顶点的特征,如位置等,即网格点。所有的顶点信息是放在着色器当中的,基于着色器语言 OPENGL ES,vec4 【-1.0 - 1.0】
● 片元着色器(Fragment shader):进行逐片元处理,如颜色,纹理,即网格表面的特性。

前端与图形学(上)_第7张图片

前端与图形学(上)_第8张图片

                                                                 表1 canvas和webGL绘图的差异

canvas webGL
2D上下文
js 3D上下文
ELSL ES语言

1. 获取canvas元素    
2.  获取渲染上下文   
3. 绘制样式  红色         
1.  浏览器里内置的webgl 渲染引擎,负责渲染webgl 图形,只认GLSL ES语言。
  1. 程序对象,承载GLSL ES语言,翻译GLSL ES语言和js语言,使两者可以相互通信。
  2. 通过canvas 获取的webgl 类型的上下文对象,可以向手绘板传递绘图命令,并接收手绘板的状态信息。
  3. 通过webgl 类型的上下文对象,用js 画画。
    GLSL ES着色器语言基础:

2.3 WEBGL高级变换与动画基础-矩阵复合变换
3D网格的形状是由顶点位置确定的,变换包含对渲染模型的缩放、旋转和位移等操作,而变换通常是由矩阵来进行操作的。
2.3.1 位移-平移矩阵

2.3.2 缩放-缩放矩阵

旋转-旋转矩阵

2.4 矩阵复合变换

2.4.1 动画基础
机制一:在每一个时间t时反复调用同一个函数绘制;
机制二:在每次绘制之前,清楚上次绘制的内容,并做出相应的变换;
requestAnimationFrame & cancelAnimationFrame

  1. 泛读three.js源码
    ● Three.js
    Three.js 是 WebGL 的综合库,其应用范围比较广泛,美中不足的一点是,Three.js 库没有比较全面详细的官方文档,对于使用者而言不是特别友好
    ● Cesium.js
    Cesium.js 是专用于 3D 地图开发的 WebGL 库,其拥有较为全面的 3D 地图开发 API,对于需要开发 3D 地图的开发者而言是一个不错的选择,但针对其他场景的应用开发覆盖的就不是很全面了
    ● Babylon.js
    Babylon.js 是一款国外应用较广泛的 WebGL 库

以上开发框架就是对webGL进行封装的一个渲染器,类似于原生js和jquery的区别;Three.js库可简化WebGL的开发,它封装了底层的图形接口,能够在无需掌握繁冗的图形学知识的情况下,也能用简单的代码实现三维场景的渲染。https://github.com/mrdoob/three.js
我们来看几个例子:three.js 实现元素周期表:https://threejs.org/examples/css3d_periodictable.html
甜甜圈:https://stemkoski.github.io/Three.js/graphulus-Surface.html
function init() {

			camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
			camera.position.z = 3000;

			scene = new THREE.Scene();

			// table

			for ( let i = 0; i < table.length; i += 5 ) {

				const element = document.createElement( 'div' );
				element.className = 'element';
				element.style.backgroundColor = 'rgba(0,127,127,' + ( Math.random() * 0.5 + 0.25 ) + ')';

				const number = document.createElement( 'div' );
				number.className = 'number';
				number.textContent = ( i / 5 ) + 1;
				element.appendChild( number );

				const symbol = document.createElement( 'div' );
				symbol.className = 'symbol';
				symbol.textContent = table[ i ];
				element.appendChild( symbol );

				const details = document.createElement( 'div' );
				details.className = 'details';
				details.innerHTML = table[ i + 1 ] + '
' + table[ i + 2 ]; element.appendChild( details ); const objectCSS = new CSS3DObject( element ); objectCSS.position.x = Math.random() * 4000 - 2000; objectCSS.position.y = Math.random() * 4000 - 2000; objectCSS.position.z = Math.random() * 4000 - 2000; scene.add( objectCSS ); objects.push( objectCSS ); // const object = new THREE.Object3D(); object.position.x = ( table[ i + 3 ] * 140 ) - 1330; object.position.y = - ( table[ i + 4 ] * 180 ) + 990; targets.table.push( object ); } // sphere const vector = new THREE.Vector3(); for ( let i = 0, l = objects.length; i < l; i ++ ) { const phi = Math.acos( - 1 + ( 2 * i ) / l ); const theta = Math.sqrt( l * Math.PI ) * phi; const object = new THREE.Object3D(); object.position.setFromSphericalCoords( 800, phi, theta ); vector.copy( object.position ).multiplyScalar( 2 ); object.lookAt( vector ); targets.sphere.push( object ); } // helix for ( let i = 0, l = objects.length; i < l; i ++ ) { const theta = i * 0.175 + Math.PI; const y = - ( i * 8 ) + 450; const object = new THREE.Object3D(); object.position.setFromCylindricalCoords( 900, theta, y ); vector.x = object.position.x * 2; vector.y = object.position.y; vector.z = object.position.z * 2; object.lookAt( vector ); targets.helix.push( object ); } // grid for ( let i = 0; i < objects.length; i ++ ) { const object = new THREE.Object3D(); object.position.x = ( ( i % 5 ) * 400 ) - 800; object.position.y = ( - ( Math.floor( i / 5 ) % 5 ) * 400 ) + 800; object.position.z = ( Math.floor( i / 25 ) ) * 1000 - 2000; targets.grid.push( object ); } // renderer = new CSS3DRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.getElementById( 'container' ).appendChild( renderer.domElement ); // controls = new TrackballControls( camera, renderer.domElement ); controls.minDistance = 500; controls.maxDistance = 6000; controls.addEventListener( 'change', render ); const buttonTable = document.getElementById( 'table' ); buttonTable.addEventListener( 'click', function () { transform( targets.table, 2000 ); } ); const buttonSphere = document.getElementById( 'sphere' ); buttonSphere.addEventListener( 'click', function () { transform( targets.sphere, 2000 ); } ); const buttonHelix = document.getElementById( 'helix' ); buttonHelix.addEventListener( 'click', function () { transform( targets.helix, 2000 ); } ); const buttonGrid = document.getElementById( 'grid' ); buttonGrid.addEventListener( 'click', function () { transform( targets.grid, 2000 ); } ); transform( targets.table, 2000 ); // window.addEventListener( 'resize', onWindowResize ); } function transform( targets, duration ) { TWEEN.removeAll(); for ( let i = 0; i < objects.length; i ++ ) { const object = objects[ i ]; const target = targets[ i ]; new TWEEN.Tween( object.position ) .to( { x: target.position.x, y: target.position.y, z: target.position.z }, Math.random() * duration + duration ) .easing( TWEEN.Easing.Exponential.InOut ) .start(); new TWEEN.Tween( object.rotation ) .to( { x: target.rotation.x, y: target.rotation.y, z: target.rotation.z }, Math.random() * duration + duration ) .easing( TWEEN.Easing.Exponential.InOut ) .start(); } new TWEEN.Tween( this ) .to( {}, duration * 2 ) .onUpdate( render ) .start(); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); render(); } function animate() { requestAnimationFrame( animate ); TWEEN.update(); controls.update(); } function render() { renderer.render( scene, camera ); }

如何学习Three.js 源码:

参考文献
webGL入门:https://zhuanlan.zhihu.com/p/470401759 学习博客:http://yxyy.name/blog/
链接:https://juejin.cn/post/6979624309870460935
链接:https://juejin.cn/post/6979624309870460935
webGL的渲染管线:https://dev.opera.com/articles/introduction-to-webgl-part-1/
各种好玩的3d项目:dragonir.github.io/3d/ git地址:https://github.com/dragonir/3d
webgl之着色器:https://zhuanlan.zhihu.com/p/360310507
前端可视化从0-1: https://zhuanlan.zhihu.com/p/407120451
WebGL着色器基础和说明:https://zhuanlan.zhihu.com/p/157340686
webgl性能优化:https://zhuanlan.zhihu.com/p/154425898
webGL光照:https://zhuanlan.zhihu.com/p/357443394 https://zhuanlan.zhihu.com/p/380850482

你可能感兴趣的:(笔记,前端,react.js,面试,js)