其他分享
首页 > 其他分享> > Three.js Example 注解 —— webgl_voxels_liquid.html

Three.js Example 注解 —— webgl_voxels_liquid.html

作者:互联网

本文搬自我的Github,https://github.com/555chy/three.js-example-comment,有兴趣的可以一起来完善,这个为Three.js的Example进行注解,方便初学者阅读

three.js 官网 Example 地址:https://threejs.org/examples/

<!doctype html>
<html lang="en">
	<head>
		<title>Voxels liquid</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<style>
			body {
				font-family: Monospace;
				background-color: #f0f0f0;
				margin: 0px;
				overflow: hidden;
			}
			#info {
				position: absolute;
				top: 0px; width: 100%;
				padding: 5px;
				text-align:center;
			}
		</style>
	</head>
	<body>
		<script src="../build/three.js"></script>
		<!--
		检测支持(canvas,webgl,workers,fileApi)
		-->
		<script src="js/Detector.js"></script>
		<!--
		在不支持RequestAnimationFrame的旧版浏览器上会使用setTimeout进行降级
		-->
		<script src="js/RequestAnimationFrame.js"></script>
		<!--
		统计插件(FPS,渲染时间,chrome内存使用率),min表示js代码经过压缩
		-->
		<script src="js/libs/stats.min.js"></script>
		
		<script>
			//size是小正方形边长,res表示平面的每条边上有多少个小正方形
			var size = 10, res = 32;
			//sizeres是平面的边长
			var sizeres = size * res, halfsizeres = sizeres / 2;
			var buffer1 = [], buffer2 = [], temp;
			var grid = [], plane;
			var scene, camera, light, renderer;
			var geometry, material;
			var mouse, raycaster, intersects = [];
			var stats;

			//检查是否支持webgl
			if ( Detector.webgl ) {
				init();
				animate();
			} else {
				//如果不支持webgl,则会在body内添加一个不支持的提示信息DIV
				document.body.appendChild( Detector.getWebGLErrorMessage() );
			}

			function init() {
				var container = document.createElement( 'div' );
				document.body.appendChild( container );

				//统计插件(FPS,渲染时间,chrome内存使用率)
				stats = new Stats();
				//这里注意,统计插件的dom元素是"dom",而不是domElement
				container.appendChild( stats.dom );

				/*
				初始化平面上的所有小正方形数组
				如果只是buffer1 = new Array(l),那么虽然生成的数组总长度相同,但是数组的值全为undefined,它不会像高级语言一样自动赋0。
				*/
				for ( var i = 0, l = res * res; i < l; i ++ ) {
					buffer1[ i ] = 0;
					buffer2[ i ] = 0;
				}
				
				scene = new THREE.Scene();

				/*
				透视相机
                PerspectiveCamera(fov, aspect, near, far)
                    fov(视场):从相机位置能够看到的部分场景。推荐默认值45
                    aspect(长宽比):渲染结果输出区域的横向长度和纵向长度的比值。推荐默认值window.innerWidth/window.innerHeight
                    near(近面):定义从距离相机多近的地方开始渲染场景。推荐默认值0.1
                    far(远面):定义相机可以从它所处的位置看多远。默认值1000
                */
				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 2000 );
				//定义相机的位置,有如下两种方式。如果不设置的话,相机位置为默认的Vector3{x:0,y:0,z:0}
				//camera.position.x = 100 + sizeres;
				//camera.position.y = 200;
				//camera.position.z = 100 + sizeres;
				camera.position.set(100 + sizeres, 200, 100 + sizeres);
				//相机的lookAt必须设置,不然注视位置便是undefined,也就不会有图像显示出来
				camera.lookAt( new THREE.Vector3( halfsizeres, - 50, halfsizeres ) );
				scene.add( camera );

				/*
				环境光,这是一种基础光源,它的颜色会添加到整个场景和所有对象的当前颜色上
				AmbientLight( color, intensity)
				color		光源的颜色
				intensity	光照强度,默认为1
				*/
				scene.add( new THREE.AmbientLight( 0x808080 ) );//灰色

				/*
				聚光灯光源,这种光源有聚光效果,类似台灯、天花板上的吊灯、或者手电筒
				SpotLight( color, intensity, distance, angle, penumbra, decay );
				color		光源的颜色
				intensity	光照强度,默认为1
				distance	光照射的距离
				angle		光的张角,单位是弧度,默认值是Math.PI/3
				penumbra	半影区模糊偏移量
				decay		衰减系数
				*/
				light = new THREE.SpotLight( 0xffffff, 1.25 );
				light.position.set( - 500, 900, 600 );
				//聚光灯照射的目标位置
				light.target.position.set( halfsizeres, 0, halfsizeres );
				//该光源是否生成阴影
				light.castShadow = true;
				scene.add( light );

				//小立方体
				geometry = new THREE.CubeGeometry( size, size, size );
				//改变几何体的原始矩阵,将其上移一般的高度,此后对该几何体的所有操作均将基于该矩阵
				geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, size / 2, 0 ) );
				//这种材质会考虑光照的影响,可以用来创建颜色暗淡的、不光亮的物体
				material = new THREE.MeshLambertMaterial( { color: 0xd0d0d0 } );

				for ( var i = 0, l = res * res; i < l; i ++ ) {
					cube = new THREE.Mesh( geometry, material );
					cube.position.x = size + ( ( i % res ) * size );
					cube.position.z = size + ( Math.floor( i / res ) * size );
					//设置模型生成投影
					cube.castShadow = true;
					//设置模型接收阴影
					cube.receiveShadow = true;
					scene.add( cube );

					grid.push( cube );
				}

				/*
				geometry = new THREE.PlaneGeometry( sizeres, sizeres );
				plane = new THREE.Mesh( geometry, material );
				plane.position.x = halfsizeres;
				plane.position.z = halfsizeres;
				plane.rotation.x = - 90 * Math.PI / 180;
				plane.visible = false;
				scene.add( plane );
				*/
				
				renderer = new THREE.WebGLRenderer();
				//打开渲染器的地图阴影
				renderer.shadowMap.enabled = true;
				//softShadow边缘有经过Percentage Closer Filtering过滤处理,不容易锯齿
				renderer.shadowMapSoft = true;
				//投影近点,距离光源多近能产生阴影
				renderer.shadowCameraNear = 3;
				//投影远点,到哪一点为止不再产生阴影
				renderer.shadowCameraFar = camera.far;
				//投影视场,聚光的角度大小
				renderer.shadowCameraFov = 50;

				//阴影偏移
				renderer.shadowMapBias = 0.0039;
				//阴影暗度,默认0.5 定义阴影有多黑
				renderer.shadowMapDarkness = 0.5;
				//阴影映射宽度
				renderer.shadowMapWidth = 512;
				//阴影映射高度
				renderer.shadowMapHeight = 512;
				//如果为true,表示只在场景中添加阴影,并不会添加光照
				//renderer.onlyShadow = false;
				
				//devicePixelRatio是设备上物理像素和设备独立像素(device-independent pixels (dips))的比例,与Android上的DIP相仿,作用是在所有设备上的显示效果都相近
				renderer.setPixelRatio( window.devicePixelRatio );
				//设置待渲染场景的大小
				renderer.setSize( window.innerWidth, window.innerHeight );
				//将渲染器的DOM元素(即Canvas)添加到HTML中
				container.appendChild(renderer.domElement);

				//射线
				raycaster = new THREE.Raycaster();
				mouse = new THREE.Vector2();

				/*
                element.addEventListener(event, function, useCapture)
                useCapture,可选。true:事件句柄在捕获阶段执行;false:默认,事件句柄在冒泡阶段执行
                */
				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
				
				window.addEventListener( 'resize', onWindowResize, false );
			}
			
			function onWindowResize() {
				//重新设置相机的宽高比。如果宽高比不对,那么正方形可能就不是正方形了
                camera.aspect = window.innerWidth / window.innerHeight;
                //更新透视相机的投影矩阵
                camera.updateProjectionMatrix();
				//更新待渲染场景的大小
                renderer.setSize( window.innerWidth, window.innerHeight );

				render();
			}

			function onDocumentMouseMove( event ) {
				//通知 Web 浏览器不要执行与事件关联的默认动作(如果存在这样的动作)
				event.preventDefault();
				
				/*
				html的坐标轴是以左上角为(0,0),右下方向为正方向
                event.clientX=event.pageX返回当事件被触发时鼠标指针向对于浏览器可见区域的X坐标
                event.offsetX返回当前事件相对于事件源元素(srcElement)的X坐标
                event.screenX鼠标相对于用户显示器屏幕左上角的X坐标
				
				mouse.x = (2 * event.clientX - renderer.domElement.clientWidth) / renderer.domElement.clientWidth
				mouse.y = (renderer.domElement.clientHeight - 2 * event.clientY) / renderer.domElement.clientHeight
				鼠标位置在一个边长为2的正方形内部,正方形中心为(0,0)点
				因此,mouse.x和mouse.y的取值范围是[-1,1]
				*/
				mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
				mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;

				//设置该射线从相机位置发出,射向视场的鼠标位置
				raycaster.setFromCamera(mouse, camera);
				//判断射线是否穿过这些物体,参数是数组。返回的是与射线相交的结果数组,按距离从近到远有序排列
				intersects = raycaster.intersectObjects(grid);
			}

			function animate() {
				requestAnimationFrame( animate );

				render();
				//这里可以在render前后使用stats.begin和stats.end,也可以在每次渲染的时候调用一次stats.update
				stats.update();
			}

			function render() {
				if ( intersects.length ) {
					/*
					intersects[ 0 ] {
						distance: double
						face: Face3
						faceIndex: int
						object: Mesh
						point: Vector3
						uv: Vector2
						__proto__: Object
					}
					*/
					var point = intersects[ 0 ].point;
					var x = Math.floor( point.x / size );
					var y = Math.floor( point.z / size );

					//这个大小决定了起伏的高度,值越大越高
					buffer1[ x + y * res ] = size;
				}

				// update buffers
				for ( var i = 0, l = res * res; i < l; i ++ ) {
					//存储该点左右上下的值
					var x1, x2, y1, y2;
					if ( i % res == 0 ) {
						// left edge
						x1 = 0;
						x2 = buffer1[ i + 1 ];
					} else if ( i % res == res - 1 ) {
						// right edge
						x1 = buffer1[ i - 1 ];
						x2 = 0;
					} else {
						x1 = buffer1[ i - 1 ];
						x2 = buffer1[ i + 1 ];
					}
					if ( i < res ) {
						// top edge
						y1 = 0;
						y2 = buffer1[ i + res ];
					} else if ( i > l - res - 1 ) {
						// bottom edge
						y1 = buffer1[ i - res ];
						y2 = 0;
					} else {
						y1 = buffer1[ i - res ];
						y2 = buffer1[ i + res ];
					}
					//除数的大小决定了起伏的高度,值越低越高,但必须大于1.9
					//这里就相当于该块周围新的均值的2倍减去原始值
					buffer2[ i ] = ( x1 + x2 + y1 + y2 ) / 2 - buffer2[ i ];
					//由于mousemove时刻都在触发,为了不至于让波动太明显,这里要减掉一个值,让其小于某个阈值
					buffer2[ i ] -= buffer2[ i ] / 10;
				}

				//交换两数组,交换使得它们不断的平均
				//交换后:buffer1是中间低周围高,buffer2是中间高周围低
				temp = buffer1;
				buffer1 = buffer2;
				buffer2 = temp;

				// update grid
				for ( var i = 0, l = res * res; i < l; i ++ ) {
					//grid[ i ].scale.y = grid[ i ].scale.y * 0.9 +  Math.max( 0.1, 0.1 + buffer2[ i ] ) * 0.1
					grid[ i ].scale.y += ( Math.max( 0.1, 0.1 + buffer2[ i ] ) - grid[ i ].scale.y ) * 0.1;
				}
				renderer.render( scene, camera );
			}
		</script>
	</body>
</html>

标签:liquid,res,webgl,Three,renderer,var,new,buffer1,size
来源: https://blog.csdn.net/chy555chy/article/details/104861512