首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Three.js案例分析系列1--webgl_animation_cloth 草坪上漂浮的白布

Three.js案例分析系列1--webgl_animation_cloth 草坪上漂浮的白布

作者头像
拿我格子衫来
发布2022-01-24 09:55:58
发布2022-01-24 09:55:58
1.7K0
举报
文章被收录于专栏:TopFETopFE

案例地址 webgl_animation_cloth--草坪上随风漂浮的白布

github源代码:

效果图:

案件描述介绍:

右上角显示帧数,左上角是一个控制盘,控制一些参数

居中是一片草坪,远处的草坪皆有雾化的效果,逐渐模糊不清,草坪中央有一个单杠,挂着一块白布,随风飘摇.

为什么选中这个案例:

首先这个案例是运用了雾化,动画,材质,而且效果看着也很自然,不管是远处的草坪,还是近处随风飘摇的衣服,都很让人感觉很舒服.

再看代码,在html中只有300行不到. 下面先贴出带有我注释的代码,在来给大家讲一讲其中的精华和重点.

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js webgl - cloth simulation</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<link type="text/css" rel="stylesheet" href="main.css">
		<style>
			body {
				background-color: #cce0ff;
				color: #000;
			}
			a {
				color: #080;
			}
		</style>
	</head>

	<body>
		<div id="info">Simple Cloth Simulation<br/>
			Verlet integration with relaxed constraints<br/>
			<a onclick="wind = !wind;">Wind</a> |
			<a onclick="sphere.visible = !sphere.visible;">Ball</a> |
			<a onclick="togglePins();">Pins</a>
		</div>

		<!-- 引入three.js库 -->
		<script src="../build/three.js"></script>

		<!--引入一个检查浏览器是否支持webgl的辅助工具-->
		<script src="js/WebGL.js"></script>

		<!--引入一个使用鼠标观察物体的库,可动态观察、缩放和平移-->
		<script src="js/controls/OrbitControls.js"></script>

		<!--引入一个javascript性能检测库-->
		<script src="js/libs/stats.min.js"></script>

		<!--引入一个使用松弛约束解算器进行布料模拟的库-->
		<script src="js/Cloth.js"></script>

		<script>

			/* testing cloth simulation */

			var pinsFormation = [];
			var pins = [ 6 ];

			pinsFormation.push( pins );

			pins = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
			pinsFormation.push( pins );

			pins = [ 0 ];
			pinsFormation.push( pins );

			pins = []; // cut the rope ;)
			pinsFormation.push( pins );

			pins = [ 0, cloth.w ]; // classic 2 pins
			pinsFormation.push( pins );

			pins = pinsFormation[ 1 ];


			function togglePins() {

				pins = pinsFormation[ ~~ ( Math.random() * pinsFormation.length ) ];

			}

			if ( WEBGL.isWebGLAvailable() === false ) {

				document.body.appendChild( WEBGL.getWebGLErrorMessage() );

			}

			var container, stats;
			var camera, scene, renderer;

			var clothGeometry;
			var sphere;
			var object;

			init();
			animate();

			function init() {

				container = document.createElement( 'div' );
				document.body.appendChild( container );

				// scene

				scene = new THREE.Scene(); // 创建一个场景
				scene.background = new THREE.Color( 0xcce0ff );  // 设置场景的背景色

				// 设置场景的雾化距离(第一次参数是雾的颜色,第二个数值表示雾从哪个距离开始显示默认1,第三个表示雾的结束位置默认1000)
				scene.fog = new THREE.Fog( 0xcce0ff, 500, 10000 );

				// camera
				// 构造一个视锥体垂直视野角度为30,视锥体长宽比为window.innerWidth / window.innerHeight,视锥体近端面为1,远端面为10000的透视摄像机
				camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 1, 10000 );
				// 设置摄像机的顶点
				camera.position.set( 1000, 50, 1500 );

				// lights

				// 为场景添加一个环境光,均匀地洒在场景上
				scene.add( new THREE.AmbientLight( 0x666666 ) );

				// 创建一个平行光线,可以产生投影,第一个参数表示光线的颜色,16进制默认为白色,第二个表示光的强度,默认1
				var light = new THREE.DirectionalLight( 0xdfebff, 1 );

				// 设置光线从(50,200,100) 到 (0,0,0) 沿着这条线照射
				light.position.set( 50, 200, 100 );

				// 将光线的向量与所传入的标量1.3进行相乘。 具体用途 TODO?
				light.position.multiplyScalar( 1.3 );

				//castShadow 如果设置为 true 该平行光会产生动态阴影。 警告: 这样做的代价比较高而且需要一直调整到阴影看起来正确
				light.castShadow = true;

				// 设置阴影贴图的宽度和高度
				light.shadow.mapSize.width = 1024;
				light.shadow.mapSize.height = 1024;

				var d = 300;

				// 在光的世界里。这用于生成场景的深度图;从光的角度来看,其他物体背后的物体将处于阴影中。
				light.shadow.camera.left = - d;
				light.shadow.camera.right = d;
				light.shadow.camera.top = d;
				light.shadow.camera.bottom = - d;
				light.shadow.camera.far = 1000;
				scene.add( light );

				// cloth material

				var loader = new THREE.TextureLoader(); // 创建一个texture加载器,内部可以加载图片
				var clothTexture = loader.load( 'textures/patterns/circuit_pattern.png' ); // 加载衣服图片或者迷宫图

				// anisotropy 沿着轴,通过具有最高纹素密度的像素的样本数。 默认情况下,这个值为1。设置一个较高的值将会产生比基本的mipmap更清晰的效果,代价是需要使用更多纹理样本
				clothTexture.anisotropy = 16;

				// 使用创建的clothTexture创建一种网格材质(一种非光泽表面的材质,没有镜面高光。)
				// map: 颜色贴图  类型为Texture
				// side: 定义将要渲染哪一面 - 正面,背面或两者
				// alpaTest: 透明度 设置运行alphaTest时要使用的alpha值。如果不透明度低于此值,则不会渲染材质。默认值为0。
				var clothMaterial = new THREE.MeshLambertMaterial( {
					map: clothTexture,
					side: THREE.DoubleSide, // 两面都渲染
					alphaTest: 0.5
				} );

				// cloth geometry

				clothGeometry = new THREE.ParametricBufferGeometry( clothFunction, cloth.w, cloth.h );

				// cloth mesh

				object = new THREE.Mesh( clothGeometry, clothMaterial );
				object.position.set( 0, 0, 0 );
				object.castShadow = true;
				scene.add( object );

				object.customDepthMaterial = new THREE.MeshDepthMaterial( {

					depthPacking: THREE.RGBADepthPacking,
					map: clothTexture,
					alphaTest: 0.5

				} );

				// sphere

				var ballGeo = new THREE.SphereBufferGeometry( ballSize, 32, 16 );
				var ballMaterial = new THREE.MeshLambertMaterial();

				sphere = new THREE.Mesh( ballGeo, ballMaterial );
				sphere.castShadow = true;
				sphere.receiveShadow = true;
				scene.add( sphere );

				// ground

				// 使用草坪图片制作材质覆盖到场景中
				var groundTexture = loader.load( 'textures/terrain/grasslight-big.jpg' );
				groundTexture.wrapS = groundTexture.wrapT = THREE.RepeatWrapping;
				groundTexture.repeat.set( 25, 25 );
				groundTexture.anisotropy = 16;

				var groundMaterial = new THREE.MeshLambertMaterial( { map: groundTexture } );

				var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 20000, 20000 ), groundMaterial );
				mesh.position.y = - 250;
				mesh.rotation.x = - Math.PI / 2;
				mesh.receiveShadow = true;
				scene.add( mesh );

				// poles
				// 使用poleMat与poleGeo渲染三个杆子到场景中
				var poleGeo = new THREE.BoxBufferGeometry( 5, 375, 5 );
				var poleMat = new THREE.MeshLambertMaterial();

				var mesh = new THREE.Mesh( poleGeo, poleMat );
				mesh.position.x = - 125;
				mesh.position.y = - 62;
				mesh.receiveShadow = true;
				mesh.castShadow = true;
				scene.add( mesh );

				var mesh = new THREE.Mesh( poleGeo, poleMat );
				mesh.position.x = 125;
				mesh.position.y = - 62;
				mesh.receiveShadow = true;
				mesh.castShadow = true;
				scene.add( mesh );

				var mesh = new THREE.Mesh( new THREE.BoxBufferGeometry( 255, 5, 5 ), poleMat );
				mesh.position.y = - 250 + ( 750 / 2 );
				mesh.position.x = 0;
				mesh.receiveShadow = true;
				mesh.castShadow = true;
				scene.add( mesh );

				var gg = new THREE.BoxBufferGeometry( 10, 10, 10 );
				var mesh = new THREE.Mesh( gg, poleMat );
				mesh.position.y = - 250;
				mesh.position.x = 125;
				mesh.receiveShadow = true;
				mesh.castShadow = true;
				scene.add( mesh );

				var mesh = new THREE.Mesh( gg, poleMat );
				mesh.position.y = - 250;
				mesh.position.x = - 125;
				mesh.receiveShadow = true;
				mesh.castShadow = true;
				scene.add( mesh );

				// renderer

				renderer = new THREE.WebGLRenderer( { antialias: true } );
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );

				container.appendChild( renderer.domElement );

				renderer.gammaInput = true;
				renderer.gammaOutput = true;

				renderer.shadowMap.enabled = true;

				// controls
				// 使用控制器,鼠标转换视角观看3D物体
				var controls = new THREE.OrbitControls( camera, renderer.domElement );
				controls.maxPolarAngle = Math.PI * 0.5;
				controls.minDistance = 1000;
				controls.maxDistance = 5000;

				// performance monitor

				// 在右上角添加帧数显示
				stats = new Stats();
				container.appendChild( stats.dom );

				//

				window.addEventListener( 'resize', onWindowResize, false );

				sphere.visible = ! true;

			}

			// 当缩放窗口时触发该函数,重新计算视锥体的宽高,并更新材质
			function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );

			}

			// 设置每一帧运行的函数,右上角的站,场景的渲染,以及 调用cloth的函数进行衣服动画的模拟
			function animate() {

				requestAnimationFrame( animate );

				var time = Date.now();

				var windStrength = Math.cos( time / 7000 ) * 20 + 40;

				windForce.set( Math.sin( time / 2000 ), Math.cos( time / 3000 ), Math.sin( time / 1000 ) )
				windForce.normalize()
				windForce.multiplyScalar( windStrength );

				simulate( time );
				render();
				stats.update();

			}

			function render() {

				var p = cloth.particles;

				for ( var i = 0, il = p.length; i < il; i ++ ) {

					var v = p[ i ].position;

					clothGeometry.attributes.position.setXYZ( i, v.x, v.y, v.z );

				}

				clothGeometry.attributes.position.needsUpdate = true;

				clothGeometry.computeVertexNormals();

				sphere.position.copy( ballPosition );

				renderer.render( scene, camera );

			}

		</script>
	</body>
</html>

下面解释一下代码中比较难理解的函数和方法

代码片段1:

代码语言:javascript
复制
function togglePins() {
    pins = pinsFormation[ ~~ ( Math.random() * pinsFormation.length ) ];
}

代码片段2:

代码语言:javascript
复制
// 设置每一帧运行的函数,右上角的站,场景的渲染,以及 调用cloth的函数进行衣服动画的模拟
function animate() {

	requestAnimationFrame( animate );

	var time = Date.now();

	var windStrength = Math.cos( time / 7000 ) * 20 + 40;

	windForce.set( Math.sin( time / 2000 ), Math.cos( time / 3000 ), Math.sin( time / 1000 ) )
	windForce.normalize()
	windForce.multiplyScalar( windStrength );

	simulate( time );
	render();
	stats.update();

}

代码片段3:

代码语言:javascript
复制
function render() {

	var p = cloth.particles;

	for ( var i = 0, il = p.length; i < il; i ++ ) {

		var v = p[ i ].position;

		clothGeometry.attributes.position.setXYZ( i, v.x, v.y, v.z );

	}

	clothGeometry.attributes.position.needsUpdate = true;

	clothGeometry.computeVertexNormals();

	sphere.position.copy( ballPosition );

	renderer.render( scene, camera );

}

代码片段4:

代码语言:javascript
复制
/*
 * Cloth Simulation using a relaxed constraints solver
 */

// Suggested Readings

// Advanced Character Physics by Thomas Jakobsen Character
// http://freespace.virgin.net/hugo.elias/models/m_cloth.htm
// http://en.wikipedia.org/wiki/Cloth_modeling
// http://cg.alexandra.dk/tag/spring-mass-system/
// Real-time Cloth Animation http://www.darwin3d.com/gamedev/articles/col0599.pdf

var DAMPING = 0.03;
var DRAG = 1 - DAMPING;
var MASS = 0.1;
var restDistance = 25;

.......
.......
.......

代码的详细分析,在稍后上传..... 有点急事需要处理,请各位稍等...

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 案例地址 webgl_animation_cloth--草坪上随风漂浮的白布
  • github源代码:
  • 案件描述介绍:
  • 为什么选中这个案例:
    • 代码片段1:
    • 代码片段2:
    • 代码片段3:
    • 代码片段4:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档