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

举报
拿我格子衫来 发表于 2022/03/18 00:41:14 2022/03/18
【摘要】 案例地址 webgl_animation_cloth--草坪上随风漂浮的白布 github源代码: 效果图: 案件描述介绍: 右上角显示帧数,左上角是一个控制盘,控制一些参数 居中是一片草坪,远处的草坪皆有雾化的效果,逐渐模糊不清,草坪中央有一个单杠,挂着一块白布,随风飘摇.   为什么选中这个案例...

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

github源代码:

效果图:

案件描述介绍:

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

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

 

为什么选中这个案例:

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

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

 


  
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>three.js webgl - cloth simulation</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  7. <link type="text/css" rel="stylesheet" href="main.css">
  8. <style>
  9. body {
  10. background-color: #cce0ff;
  11. color: #000;
  12. }
  13. a {
  14. color: #080;
  15. }
  16. </style>
  17. </head>
  18. <body>
  19. <div id="info">Simple Cloth Simulation<br/>
  20. Verlet integration with relaxed constraints<br/>
  21. <a onclick="wind = !wind;">Wind</a> |
  22. <a onclick="sphere.visible = !sphere.visible;">Ball</a> |
  23. <a onclick="togglePins();">Pins</a>
  24. </div>
  25. <!-- 引入three.js库 -->
  26. <script src="../build/three.js"></script>
  27. <!--引入一个检查浏览器是否支持webgl的辅助工具-->
  28. <script src="js/WebGL.js"></script>
  29. <!--引入一个使用鼠标观察物体的库,可动态观察、缩放和平移-->
  30. <script src="js/controls/OrbitControls.js"></script>
  31. <!--引入一个javascript性能检测库-->
  32. <script src="js/libs/stats.min.js"></script>
  33. <!--引入一个使用松弛约束解算器进行布料模拟的库-->
  34. <script src="js/Cloth.js"></script>
  35. <script>
  36. /* testing cloth simulation */
  37. var pinsFormation = [];
  38. var pins = [ 6 ];
  39. pinsFormation.push( pins );
  40. pins = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
  41. pinsFormation.push( pins );
  42. pins = [ 0 ];
  43. pinsFormation.push( pins );
  44. pins = []; // cut the rope ;)
  45. pinsFormation.push( pins );
  46. pins = [ 0, cloth.w ]; // classic 2 pins
  47. pinsFormation.push( pins );
  48. pins = pinsFormation[ 1 ];
  49. function togglePins() {
  50. pins = pinsFormation[ ~~ ( Math.random() * pinsFormation.length ) ];
  51. }
  52. if ( WEBGL.isWebGLAvailable() === false ) {
  53. document.body.appendChild( WEBGL.getWebGLErrorMessage() );
  54. }
  55. var container, stats;
  56. var camera, scene, renderer;
  57. var clothGeometry;
  58. var sphere;
  59. var object;
  60. init();
  61. animate();
  62. function init() {
  63. container = document.createElement( 'div' );
  64. document.body.appendChild( container );
  65. // scene
  66. scene = new THREE.Scene(); // 创建一个场景
  67. scene.background = new THREE.Color( 0xcce0ff ); // 设置场景的背景色
  68. // 设置场景的雾化距离(第一次参数是雾的颜色,第二个数值表示雾从哪个距离开始显示默认1,第三个表示雾的结束位置默认1000)
  69. scene.fog = new THREE.Fog( 0xcce0ff, 500, 10000 );
  70. // camera
  71. // 构造一个视锥体垂直视野角度为30,视锥体长宽比为window.innerWidth / window.innerHeight,视锥体近端面为1,远端面为10000的透视摄像机
  72. camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 1, 10000 );
  73. // 设置摄像机的顶点
  74. camera.position.set( 1000, 50, 1500 );
  75. // lights
  76. // 为场景添加一个环境光,均匀地洒在场景上
  77. scene.add( new THREE.AmbientLight( 0x666666 ) );
  78. // 创建一个平行光线,可以产生投影,第一个参数表示光线的颜色,16进制默认为白色,第二个表示光的强度,默认1
  79. var light = new THREE.DirectionalLight( 0xdfebff, 1 );
  80. // 设置光线从(50,200,100) 到 (0,0,0) 沿着这条线照射
  81. light.position.set( 50, 200, 100 );
  82. // 将光线的向量与所传入的标量1.3进行相乘。 具体用途 TODO?
  83. light.position.multiplyScalar( 1.3 );
  84. //castShadow 如果设置为 true 该平行光会产生动态阴影。 警告: 这样做的代价比较高而且需要一直调整到阴影看起来正确
  85. light.castShadow = true;
  86. // 设置阴影贴图的宽度和高度
  87. light.shadow.mapSize.width = 1024;
  88. light.shadow.mapSize.height = 1024;
  89. var d = 300;
  90. // 在光的世界里。这用于生成场景的深度图;从光的角度来看,其他物体背后的物体将处于阴影中。
  91. light.shadow.camera.left = - d;
  92. light.shadow.camera.right = d;
  93. light.shadow.camera.top = d;
  94. light.shadow.camera.bottom = - d;
  95. light.shadow.camera.far = 1000;
  96. scene.add( light );
  97. // cloth material
  98. var loader = new THREE.TextureLoader(); // 创建一个texture加载器,内部可以加载图片
  99. var clothTexture = loader.load( 'textures/patterns/circuit_pattern.png' ); // 加载衣服图片或者迷宫图
  100. // anisotropy 沿着轴,通过具有最高纹素密度的像素的样本数。 默认情况下,这个值为1。设置一个较高的值将会产生比基本的mipmap更清晰的效果,代价是需要使用更多纹理样本
  101. clothTexture.anisotropy = 16;
  102. // 使用创建的clothTexture创建一种网格材质(一种非光泽表面的材质,没有镜面高光。)
  103. // map: 颜色贴图 类型为Texture
  104. // side: 定义将要渲染哪一面 - 正面,背面或两者
  105. // alpaTest: 透明度 设置运行alphaTest时要使用的alpha值。如果不透明度低于此值,则不会渲染材质。默认值为0。
  106. var clothMaterial = new THREE.MeshLambertMaterial( {
  107. map: clothTexture,
  108. side: THREE.DoubleSide, // 两面都渲染
  109. alphaTest: 0.5
  110. } );
  111. // cloth geometry
  112. clothGeometry = new THREE.ParametricBufferGeometry( clothFunction, cloth.w, cloth.h );
  113. // cloth mesh
  114. object = new THREE.Mesh( clothGeometry, clothMaterial );
  115. object.position.set( 0, 0, 0 );
  116. object.castShadow = true;
  117. scene.add( object );
  118. object.customDepthMaterial = new THREE.MeshDepthMaterial( {
  119. depthPacking: THREE.RGBADepthPacking,
  120. map: clothTexture,
  121. alphaTest: 0.5
  122. } );
  123. // sphere
  124. var ballGeo = new THREE.SphereBufferGeometry( ballSize, 32, 16 );
  125. var ballMaterial = new THREE.MeshLambertMaterial();
  126. sphere = new THREE.Mesh( ballGeo, ballMaterial );
  127. sphere.castShadow = true;
  128. sphere.receiveShadow = true;
  129. scene.add( sphere );
  130. // ground
  131. // 使用草坪图片制作材质覆盖到场景中
  132. var groundTexture = loader.load( 'textures/terrain/grasslight-big.jpg' );
  133. groundTexture.wrapS = groundTexture.wrapT = THREE.RepeatWrapping;
  134. groundTexture.repeat.set( 25, 25 );
  135. groundTexture.anisotropy = 16;
  136. var groundMaterial = new THREE.MeshLambertMaterial( { map: groundTexture } );
  137. var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 20000, 20000 ), groundMaterial );
  138. mesh.position.y = - 250;
  139. mesh.rotation.x = - Math.PI / 2;
  140. mesh.receiveShadow = true;
  141. scene.add( mesh );
  142. // poles
  143. // 使用poleMat与poleGeo渲染三个杆子到场景中
  144. var poleGeo = new THREE.BoxBufferGeometry( 5, 375, 5 );
  145. var poleMat = new THREE.MeshLambertMaterial();
  146. var mesh = new THREE.Mesh( poleGeo, poleMat );
  147. mesh.position.x = - 125;
  148. mesh.position.y = - 62;
  149. mesh.receiveShadow = true;
  150. mesh.castShadow = true;
  151. scene.add( mesh );
  152. var mesh = new THREE.Mesh( poleGeo, poleMat );
  153. mesh.position.x = 125;
  154. mesh.position.y = - 62;
  155. mesh.receiveShadow = true;
  156. mesh.castShadow = true;
  157. scene.add( mesh );
  158. var mesh = new THREE.Mesh( new THREE.BoxBufferGeometry( 255, 5, 5 ), poleMat );
  159. mesh.position.y = - 250 + ( 750 / 2 );
  160. mesh.position.x = 0;
  161. mesh.receiveShadow = true;
  162. mesh.castShadow = true;
  163. scene.add( mesh );
  164. var gg = new THREE.BoxBufferGeometry( 10, 10, 10 );
  165. var mesh = new THREE.Mesh( gg, poleMat );
  166. mesh.position.y = - 250;
  167. mesh.position.x = 125;
  168. mesh.receiveShadow = true;
  169. mesh.castShadow = true;
  170. scene.add( mesh );
  171. var mesh = new THREE.Mesh( gg, poleMat );
  172. mesh.position.y = - 250;
  173. mesh.position.x = - 125;
  174. mesh.receiveShadow = true;
  175. mesh.castShadow = true;
  176. scene.add( mesh );
  177. // renderer
  178. renderer = new THREE.WebGLRenderer( { antialias: true } );
  179. renderer.setPixelRatio( window.devicePixelRatio );
  180. renderer.setSize( window.innerWidth, window.innerHeight );
  181. container.appendChild( renderer.domElement );
  182. renderer.gammaInput = true;
  183. renderer.gammaOutput = true;
  184. renderer.shadowMap.enabled = true;
  185. // controls
  186. // 使用控制器,鼠标转换视角观看3D物体
  187. var controls = new THREE.OrbitControls( camera, renderer.domElement );
  188. controls.maxPolarAngle = Math.PI * 0.5;
  189. controls.minDistance = 1000;
  190. controls.maxDistance = 5000;
  191. // performance monitor
  192. // 在右上角添加帧数显示
  193. stats = new Stats();
  194. container.appendChild( stats.dom );
  195. //
  196. window.addEventListener( 'resize', onWindowResize, false );
  197. sphere.visible = ! true;
  198. }
  199. // 当缩放窗口时触发该函数,重新计算视锥体的宽高,并更新材质
  200. function onWindowResize() {
  201. camera.aspect = window.innerWidth / window.innerHeight;
  202. camera.updateProjectionMatrix();
  203. renderer.setSize( window.innerWidth, window.innerHeight );
  204. }
  205. // 设置每一帧运行的函数,右上角的站,场景的渲染,以及 调用cloth的函数进行衣服动画的模拟
  206. function animate() {
  207. requestAnimationFrame( animate );
  208. var time = Date.now();
  209. var windStrength = Math.cos( time / 7000 ) * 20 + 40;
  210. windForce.set( Math.sin( time / 2000 ), Math.cos( time / 3000 ), Math.sin( time / 1000 ) )
  211. windForce.normalize()
  212. windForce.multiplyScalar( windStrength );
  213. simulate( time );
  214. render();
  215. stats.update();
  216. }
  217. function render() {
  218. var p = cloth.particles;
  219. for ( var i = 0, il = p.length; i < il; i ++ ) {
  220. var v = p[ i ].position;
  221. clothGeometry.attributes.position.setXYZ( i, v.x, v.y, v.z );
  222. }
  223. clothGeometry.attributes.position.needsUpdate = true;
  224. clothGeometry.computeVertexNormals();
  225. sphere.position.copy( ballPosition );
  226. renderer.render( scene, camera );
  227. }
  228. </script>
  229. </body>
  230. </html>

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

代码片段1:


  
  1. function togglePins() {
  2. pins = pinsFormation[ ~~ ( Math.random() * pinsFormation.length ) ];
  3. }

代码片段2:


  
  1. // 设置每一帧运行的函数,右上角的站,场景的渲染,以及 调用cloth的函数进行衣服动画的模拟
  2. function animate() {
  3. requestAnimationFrame( animate );
  4. var time = Date.now();
  5. var windStrength = Math.cos( time / 7000 ) * 20 + 40;
  6. windForce.set( Math.sin( time / 2000 ), Math.cos( time / 3000 ), Math.sin( time / 1000 ) )
  7. windForce.normalize()
  8. windForce.multiplyScalar( windStrength );
  9. simulate( time );
  10. render();
  11. stats.update();
  12. }

代码片段3:


  
  1. function render() {
  2. var p = cloth.particles;
  3. for ( var i = 0, il = p.length; i < il; i ++ ) {
  4. var v = p[ i ].position;
  5. clothGeometry.attributes.position.setXYZ( i, v.x, v.y, v.z );
  6. }
  7. clothGeometry.attributes.position.needsUpdate = true;
  8. clothGeometry.computeVertexNormals();
  9. sphere.position.copy( ballPosition );
  10. renderer.render( scene, camera );
  11. }

代码片段4:


  
  1. /*
  2. * Cloth Simulation using a relaxed constraints solver
  3. */
  4. // Suggested Readings
  5. // Advanced Character Physics by Thomas Jakobsen Character
  6. // http://freespace.virgin.net/hugo.elias/models/m_cloth.htm
  7. // http://en.wikipedia.org/wiki/Cloth_modeling
  8. // http://cg.alexandra.dk/tag/spring-mass-system/
  9. // Real-time Cloth Animation http://www.darwin3d.com/gamedev/articles/col0599.pdf
  10. var DAMPING = 0.03;
  11. var DRAG = 1 - DAMPING;
  12. var MASS = 0.1;
  13. var restDistance = 25;
  14. .......
  15. .......
  16. .......

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

 

 

文章来源: fizzz.blog.csdn.net,作者:拿我格子衫来,版权归原作者所有,如需转载,请联系作者。

原文链接:fizzz.blog.csdn.net/article/details/96477991

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。