ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Three.js Example 注解 —— webgl_voxels_liquid.html

2020-03-14 16:07:38  阅读:319  来源: 互联网

标签:liquid res webgl Three renderer var new buffer1 size


本文搬自我的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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有