vlambda博客
学习文章列表

threejs之波浪球效果(初级实现思路)

今天看到了一个官网上的一个波纹球的效果,感觉很好看,于是便想用自己已经学的threejs的知识写一个



threejs管网的示例是这样的:


threejs之波浪球效果(初级实现思路)


我的最后写完是这样式的(emmm,能跑就行):


threejs之波浪球效果(初级实现思路)


虽然感觉看起来差点,但是大同小异哈哈哈哈

首先我们要思考,如何来生成这些圆,生成一个肯定很点单对吧(如果你认真看过我上一期的文章《初识threejs》的话)

好的,我们来先放个圆

     //生成一个球      var geometry = new THREE.SphereGeometry(101010);      //材质 var material = new THREE.MeshNormalMaterial({ color: 0xff0000, });      //创建对象     let mesh = new THREE.Mesh(geometry, material);     //球的坐标     mesh.position.x=100;     mesh.position.y=200;     //渲染 scene.add(mesh); 

然后,它就这样了:

threejs之波浪球效果(初级实现思路)

因为我们要做动画,肯定有很多个圆,所以,如何实现呢?

基于我现在还不知道有BufferAttribute这个东西,所以呢我们肯定要用循环啦!(大佬勿喷,我是新手threejs之波浪球效果(初级实现思路)

var geometry = new THREE.SphereGeometry(10, 10, 10); var material = new THREE.MeshNormalMaterial({ color: 0xff0000, }); for(let i=-20;i<=20;i++){ for(let j=-20;j<=20;j++){ let mesh = new THREE.Mesh(geometry, material); mesh.position.x=i*100; mesh.position.y=j*200; scene.add(mesh); } }

然后界面上就有很多圆了,成功得到了一个圆的矩阵!

threejs之波浪球效果(初级实现思路)

然后呢,重点来了,我们怎么让它动起来呢???

在animate里改变它的Z坐标吗?

好的我们先加个试试,但是要怎么改变呢,我也获取不到每个圆,所以,我神操作把圆存进了一个数组里!没毛病吧!哈哈哈哈!(能跑就行!)

我只需要循环数组改变它的Z值就可以了吧!

threejs之波浪球效果(初级实现思路)

由于想让他们上下看起来像波浪,所以要用到Math.cos函数,emmm,先随便试试!

var animate = function () { requestAnimationFrame( animate ); var time = performance.now(); for(let i=0;i<arr.length;i++){ arr[i].position.z = 200*Math.cos(time*Math.PI/180) } renderer.render( scene, camera ); };

它这样了!

threejs之波浪球效果(初级实现思路)

动是可以了,但是你不能一起动啊!!!我要的是波浪啊!!

思考:波浪怎么实现

要让每个圆的Z变化量不能一样,emm,对了,我们不是循环吗,可以根据循环的值来改变Z值啊!

说干就干,我改成了这样!

arr[i].position.z = 200*Math.cos((time-i*0.2)*Math.PI/180)

效果:

threejs之波浪球效果(初级实现思路)

横着能动了,但是竖着应该也在动只不过差的有点小看不出来吧应该

所以竖着怎么动啊!

在我掉了一缕头发后,我想到了,我是一个正方形啊,我横着能动了,我再把竖着的取个余数剪了不就是变量了吗???

所以在我剪了个余数乘某个值后,乘的这个值主要是为了改变它动的速度。

arr[i].position.z = 200*Math.cos((time-i%41*10-i*0.2)*Math.PI/180)

他看着有内味了!剩下的就是微调的玩意了!

然后我看了一眼官方的demo,我写的是个什么 * * 东西啊???


最终代码直接贴下边:

<!DOCTYPE html><html lang="en"><head> <title>three.js webgl - instancing test (single triangle)</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> a { color: #08f; } #notSupported { width: 50%; margin: auto; background-color: #f00; margin-top: 20px; padding: 10px; }</style>  <script src="three.min.js"></script> <script src="OrbitControls.js"></script></head><body> <div id="container"></div>  <script type="module"> var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 0.1, 1000000 );
var renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); //允许阴20 renderer.shadowMapEnabled = true; camera.position.x = 0; camera.position.y = -6000; camera.position.z = 500; var arr = []; var geometry = new THREE.SphereGeometry(10, 10, 10); var material = new THREE.MeshNormalMaterial({ color: 0xff0000, }); for(let i=-20;i<=20;i++){ for(let j=-20;j<=20;j++){ let mesh = new THREE.Mesh(geometry, material); mesh.position.x=i*100; mesh.position.y=j*200; scene.add(mesh); arr.push(mesh); } } var spotLight = new THREE.AmbientLight(0xffffff); spotLight.castShadow = true; scene.add(spotLight); // 相机允许鼠标变化 var controls = new THREE.OrbitControls(camera,renderer.domElement);//创建控件对象 controls.addEventListener('change', renderer);//监听鼠标、键盘事件 controls.autoRotate=true; var animate = function () { requestAnimationFrame( animate ); var time = performance.now()/5; for(let i=0;i<arr.length;i++){ arr[i].position.z = 200*Math.cos((time-i%41*10-i*0.2)*Math.PI/180) } renderer.render( scene, camera ); };
animate();</script></body>


官方代码:

<!DOCTYPE html><html lang="en"> <head> <title>three.js webgl - particles - waves</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"> </head> <body> <div id="info"> <a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - webgl particles waves example </div> <script type="x-shader/x-vertex" id="vertexshader"> attribute float scale; void main() { vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); gl_PointSize = scale * ( 300.0 / - mvPosition.z ); gl_Position = projectionMatrix * mvPosition; }</script> <script type="x-shader/x-fragment" id="fragmentshader"> uniform vec3 color; void main() { if ( length( gl_PointCoord - vec2( 0.5, 0.5 ) ) > 0.475 ) discard; gl_FragColor = vec4( color, 1.0 ); }</script> <script type="module"> import * as THREE from '../build/three.module.js'; import Stats from './jsm/libs/stats.module.js'; var SEPARATION = 100, AMOUNTX = 50, AMOUNTY = 50; var container, stats; var camera, scene, renderer; var particles, count = 0; var mouseX = 0, mouseY = 0; var windowHalfX = window.innerWidth / 2; var windowHalfY = window.innerHeight / 2; init(); animate(); function init() { container = document.createElement( 'div' ); document.body.appendChild( container ); camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 ); camera.position.z = 1000; scene = new THREE.Scene(); // var numParticles = AMOUNTX * AMOUNTY; var positions = new Float32Array( numParticles * 3 ); var scales = new Float32Array( numParticles ); var i = 0, j = 0; for ( var ix = 0; ix < AMOUNTX; ix ++ ) { for ( var iy = 0; iy < AMOUNTY; iy ++ ) { positions[ i ] = ix * SEPARATION - ( ( AMOUNTX * SEPARATION ) / 2 ); // x positions[ i + 1 ] = 0; // y positions[ i + 2 ] = iy * SEPARATION - ( ( AMOUNTY * SEPARATION ) / 2 ); // z scales[ j ] = 1; i += 3; j ++; } } var geometry = new THREE.BufferGeometry(); geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); geometry.setAttribute( 'scale', new THREE.BufferAttribute( scales, 1 ) ); var material = new THREE.ShaderMaterial( { uniforms: { color: { value: new THREE.Color( 0xffffff ) }, }, vertexShader: document.getElementById( 'vertexshader' ).textContent, fragmentShader: document.getElementById( 'fragmentshader' ).textContent } ); // particles = new THREE.Points( geometry, material ); scene.add( particles ); // renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); container.appendChild( renderer.domElement ); stats = new Stats(); container.appendChild( stats.dom ); document.addEventListener( 'mousemove', onDocumentMouseMove, false ); document.addEventListener( 'touchstart', onDocumentTouchStart, false ); document.addEventListener( 'touchmove', onDocumentTouchMove, false ); // window.addEventListener( 'resize', onWindowResize, false ); } function onWindowResize() { windowHalfX = window.innerWidth / 2; windowHalfY = window.innerHeight / 2; camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); } // function onDocumentMouseMove( event ) { mouseX = event.clientX - windowHalfX; mouseY = event.clientY - windowHalfY; } function onDocumentTouchStart( event ) { if ( event.touches.length === 1 ) { event.preventDefault(); mouseX = event.touches[ 0 ].pageX - windowHalfX; mouseY = event.touches[ 0 ].pageY - windowHalfY; } } function onDocumentTouchMove( event ) { if ( event.touches.length === 1 ) { event.preventDefault(); mouseX = event.touches[ 0 ].pageX - windowHalfX; mouseY = event.touches[ 0 ].pageY - windowHalfY; } } // function animate() { requestAnimationFrame( animate ); render(); stats.update(); } function render() { camera.position.x += ( mouseX - camera.position.x ) * .05; camera.position.y += ( - mouseY - camera.position.y ) * .05; camera.lookAt( scene.position ); var positions = particles.geometry.attributes.position.array; var scales = particles.geometry.attributes.scale.array; var i = 0, j = 0; for ( var ix = 0; ix < AMOUNTX; ix ++ ) { for ( var iy = 0; iy < AMOUNTY; iy ++ ) { positions[ i + 1 ] = ( Math.sin( ( ix + count ) * 0.3 ) * 50 ) + ( Math.sin( ( iy + count ) * 0.5 ) * 50 ); scales[ j ] = ( Math.sin( ( ix + count ) * 0.3 ) + 1 ) * 8 + ( Math.sin( ( iy + count ) * 0.5 ) + 1 ) * 8; i += 3; j ++; } } particles.geometry.attributes.position.needsUpdate = true; particles.geometry.attributes.scale.needsUpdate = true; renderer.render( scene, camera ); count += 0.1; }</script> </body></html>

好了,今天就到这里了,我要继续研究官方的demo了,蚌埠住了!

结尾福利: