【Three.js】官方彩色粒子立方体旋转实例详解

1. 效果图

2. 详细步骤

2.1 创建场景、相机、渲染器、光

  • 这一步都是基础的创建,注意相机位置和配置数据,保证立方体在视景体内就好。
  • 雾可加可不加,添加雾后立方体后方会部分模糊。
  • 环境光和直线光也可以只添加一个,但不能全不添加,添加不同的光会影响立方体的亮度,可以自己尝试不同颜色不同类型的光。
  • 渲染器的一些配置可参考官方文档
// 声明场景、渲染器、相机变量
let scene, renderer, camera;
// 获取窗口宽度高度
let ww = window.innerWidth,
	wh = window.innerHeight;
// 调用初始化函数
init();

/**
 *@method 初始化函数
 */
function init() {
    // 创建场景
	scene = new THREE.Scene();
	// 创建雾 (颜色, 受影响最小距离, 受影响最大距离)
	scene.fog = new THREE.Fog(0x050505, 2500, 3500);

    // 创建相机
	camera = new THREE.PerspectiveCamera(27, ww/wh, 1, 3500);
	// 设置相机位置
	camera.position.z = 3000;

    // 添加环境光
	scene.add(new THREE.AmbientLight(0xFFFFFF));
			
	// 添加直线光
	var light = new THREE.DirectionalLight(0xffffff, 0.5);
	light.position.set(1, 1, 1);
	scene.add(light);

    // 创建渲染器
	renderer = new THREE.WebGLRenderer({ 
	    canvas: document.getElementById('container'),
		antialias : false
	});
	renderer.setClearColor(scene.fog.color);
	renderer.setPixelRatio(window.devicePixelRatio);
	renderer.setSize(ww, wh);
			
	renderer.gammaInput = true;
	renderer.gammaOutput = true;
}

2.2 创建立方体

  每一步代码注释有相应解释,这里做详细讲解:

  • var triangles = 160000; // 这里是声明了构成立方体的点数,这里的立方体是由小三角形构成的,所以这里就是构成立方体的小三角形的总数。
  • var geometry = new THREE.BufferGeometry(); // 创建一个几何体,这里的BufferGeometry可以理解为自己去定义每一个点的 x y z 值,这些值按顺序放在一个数组里,比如 [1, 2, 3] 构成 x=1, y=2, z=3 的点,即数组中三个元素确定一个点。如果要确定一条 线呢,那么需要2个点,数组中需要6个元素。三角形由三个点构成,所以三个点需要9个元素。
  • positions、normals、colors 就理解为长度为三角形数*9的数组,Float32Array和Array的区别看注释。为什么都 * 3 *3 呢,因为这里一个小三角形有3个点,所以是 *3, 每一个点有 x y z 三个值,所以 *3 *3。positions 就按顺序存储了每个点的 x y z 值;normals存法向量x y z 值;colors 存每一个点 r g b 值。
  • 遍历创建三角形的时候,一次循环创建一个三角形,一个三角形9个元素,所以每次 +9
  • 遍历中声明的 x y z 是每个三角形原点(理解为在这个点附近一定范围内创建三个点构成三角形),Math.random * n 即 0~800,-n2 即 -400 范围为 -400 ~ 400,也就是每一个原点的 x y z 值都在 -400 ~ 400 范围内,即以 (0, 0, 0) 为中心,800 为边长的正方体内。
  • pA、pB、pC 为三角形三个点的位置(这里我们称ABC三点),ax ay az 为点 A x y z 值,Math.random()*d-d2 即-6~6,x y z 分别加这个值,表示原点上加或减6之内的值,同理生成点B、点C的x y z 值,由此随机生成三角形三点。
  • 法向量的计算是三角形两边的向量积,Vector3 提供了一系列的方法。为什么要计算每一个点的法向量呢,因为物体表面和光照方向的夹角大小不同,那么强度也不一样,为了计算这个角度,首先要知道物体表面每个位置的法线方向。并且在网格模型 Mesh 中,物体都是由小三角形组成的,所以我们需要计算出小三角形三个顶点的法向量。
  • 计算完成后将位置,法向量,颜色分别赋值给 positions、normals、colors。
  • 通过:geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
                geometry.addAttribute('normal', new THREE.BufferAttribute(normals, 3));
                geometry.addAttribute('color', new THREE.BufferAttribute(colors, 3)); 进行设置。
            // 一共有多少个点
			var triangles = 160000;
			
			// 创建立方体
			var geometry = new THREE.BufferGeometry();
			/**
			 * Float32Array 其实就是数组, 在操作较大的阵列时,访问数据通常比普通数组更快
			 * Float32Array 创建数组必须使用构造函数, 参数为数组长度
			 * 这里的长度为显示的点数 * 9 (因为每一个点需要用9个值描述,后面会将)
			 */
			var positions = new Float32Array(triangles * 3 * 3);
			var normals = new Float32Array(triangles * 3 * 3);
			var colors = new Float32Array(triangles * 3 * 3);
			
			var color = new THREE.Color();
			
			// n 为立方体边长
			var n = 800, n2 = n/2;
			// d 为每个小三角形的每一个点距原点偏移的最大值
			var d = 12, d2 = d/2;
			
			// 三角形三个顶点的向量
			var pA = new THREE.Vector3();
			var pB = new THREE.Vector3();
			var pC = new THREE.Vector3();
			
			var cb = new THREE.Vector3();
			var ab = new THREE.Vector3();
			
			// 遍历所有点, i 每次 +9 保证每次循环操作一个点
			for(var i = 0; i < positions.length; i += 9){
				// 每一个点的原点位置, xyz 都在 -400 ~ 400 之间, 生成的点一定在边长为 800 的立方体内
				var x = Math.random() * n - n2; // 随机 -400 ~ 400 的值
				var y = Math.random() * n - n2; // 随机 -400 ~ 400 的值
				var z = Math.random() * n - n2; // 随机 -400 ~ 400 的值
				
				/**
				 * Math.random() * d 为 0 ~ 12, -d2 即为 -6 ~ 6,即三角形顶点 x 在原点基础上 -6 或 +6
				 */
				// 每一个点(小三角形)的点a 的 xyz, 因为随机, 所有每一个小三角形大小不一样, 只控制了最大值
				var ax = x + Math.random() * d - d2;
				var ay = y + Math.random() * d - d2;
				var az = z + Math.random() * d - d2;
				
				var bx = x + Math.random() * d - d2;
				var by = y + Math.random() * d - d2;
				var bz = z + Math.random() * d - d2;
				
				var cx = x + Math.random() * d - d2;
				var cy = y + Math.random() * d - d2;
				var cz = z + Math.random() * d - d2;
				
				// 小三角形点a坐标占 index 0-2
				positions[i] = ax;
				positions[i+1] = ay;
				positions[i+2] = az;
				// 小三角形点b坐标占 index 3-5
				positions[i+3] = bx;
				positions[i+4] = by;
				positions[i+5] = bz;
				// 小三角形点c坐标占 index 6-8
				positions[i+6] = cx;
				positions[i+7] = cy;
				positions[i+8] = cz;
				// 设置三角形的三个顶点
				pA.set(ax, ay, az);
				pB.set(bx, by, bz);
				pC.set(cx, cy, cz);
				// pC pB向量相减(三角形一条边)
				cb.subVectors(pC, pB);
				// A B向量相减(三角形一条边)
				ab.subVectors(pA, pB);
				// cb ab 向量积, 即同时垂直上面两条边的向量,即法向量
				cb.cross(ab);
				// 将法向量转为单位向量
				cb.normalize();
				//法向量的方向可以这样表示N(nx, ny, nz);
				var nx = cb.x;
				var ny = cb.y;
				var nz = cb.z;
				
				// 0-8 循环法向量 xyz
				normals[i]     = nx;
				normals[i+1] = ny;
				normals[i+2] = nz;
				
				normals[i+3] = nx;
				normals[i+4] = ny;
				normals[i+5] = nz;
				
				normals[i+6] = nx;
				normals[i+7] = ny;
				normals[i+8] = nz;

				//颜色用rgb表示, rgb每一个分量取值范围0-1,vx,vy,vz分别对应rgb值。
				/**
				 * x 点的 x 值, -400-400,立方体边长为 800, 所以 vx 为 -0.5 ~ 0.5,+0.5 后范围为 0 ~ 1
				 * 通过这样的计算, 相同的 xyz 颜色相同, 相近的 xyz 颜色相近
				 */
				var vx = (x/n) + 0.5;
				var vy = (y/n) + 0.5;
				var vz = (z/n) + 0.5;
				
				// color 存该点的颜色
				color.setRGB(vx, vy, vz);

				//将三角形的三个顶点设为同样的颜色
				colors[i] = color.r;
				colors[i+1] = color.g;
				colors[i+2] = color.b;
			
				colors[i+3] = color.r;
				colors[i+4] = color.g;
				colors[i+5] = color.b;
				
				colors[i+6] = color.r;
				colors[i+7] = color.g;
				colors[i+8] = color.b;
			}
			
			// 设置原点, 三角形, 颜色
			geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
			geometry.addAttribute('normal', new THREE.BufferAttribute(normals, 3));
			geometry.addAttribute('color', new THREE.BufferAttribute(colors, 3));
			
			// 计算外边界球
			geometry.computeBoundingSphere();
			
			// 创建立方体材质
			var material = new THREE.MeshPhongMaterial({
				color : 0xaaaaaa, 
				ambient : 0xaaaaaa, 
				specular : 0xffffff, 
				shininess : 250,
				side : THREE.DoubleSide, 
				vertexColors : THREE.VertexColors
			});
			
			// 创建立方体物体并添加到场景
			mesh = new THREE.Mesh(geometry, material);
			scene.add(mesh);

3. 完整代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>BufferGeometry 彩色三角形立方体</title>
		<style type="text/css">
			html, body {
				margin: 0px;
				padding: 0px;
				height: 100%;
			}
			#container {
				width: 100%;
				height: 100%;
			}
		</style>
	</head>
	<body>
		<canvas id="container"></canvas>
	</body>
	<script type="text/javascript" src="../../js/build/three.js"></script>
	<script type="text/javascript">
		// 声明场景、渲染器、相机、立方体变量
		let scene, renderer, camera, mesh;
		// 窗口宽高
		let ww = window.innerWidth,
			wh = window.innerHeight;
		// 初始化
		init();
		// 添加动画
		animate();
		/**
		 * @method 初始化函数
		 */
		function init(){
			// 创建场景
			scene = new THREE.Scene();
			// 创建雾 (颜色, 受影响最小距离, 受影响最大距离)
			scene.fog = new THREE.Fog(0x050505, 2500, 3500);

			// 创建相机
			camera = new THREE.PerspectiveCamera(27, ww/wh, 1, 3500);
			// 设置相机位置
			camera.position.z = 3000;
			
			// 添加环境光
			scene.add(new THREE.AmbientLight(0xFFFFFF));
			
			// 添加直线光
			var light = new THREE.DirectionalLight(0xffffff, 0.5);
			light.position.set(1, 1, 1);
			scene.add(light);

			// 创建渲染器
			renderer = new THREE.WebGLRenderer({ 
				canvas: document.getElementById('container'),
				antialias : false
			});
			renderer.setClearColor( scene.fog.color);
			renderer.setPixelRatio(window.devicePixelRatio);
			renderer.setSize(ww, wh);
			
			renderer.gammaInput = true;
			renderer.gammaOutput = true;
			
			// 一共有多少个点
			var triangles = 160000;
			
			// 创建立方体
			var geometry = new THREE.BufferGeometry();
			/**
			 * Float32Array 其实就是数组, 在操作较大的阵列时,访问数据通常比普通数组更快
			 * Float32Array 创建数组必须使用构造函数, 参数为数组长度
			 * 这里的长度为显示的点数 * 9 (因为每一个点需要用9个值描述,后面会将)
			 */
			var positions = new Float32Array(triangles * 3 * 3);
			var normals = new Float32Array(triangles * 3 * 3);
			var colors = new Float32Array(triangles * 3 * 3);
			
			var color = new THREE.Color();
			
			// n 为立方体边长
			var n = 800, n2 = n/2;
			// d 为每个小三角形的每一个点距原点偏移的最大值
			var d = 12, d2 = d/2;
			
			// 三角形三个顶点的向量
			var pA = new THREE.Vector3();
			var pB = new THREE.Vector3();
			var pC = new THREE.Vector3();
			
			var cb = new THREE.Vector3();
			var ab = new THREE.Vector3();
			
			// 遍历所有点, i 每次 +9 保证每次循环操作一个点
			for(var i = 0; i < positions.length; i += 9){
				// 每一个点的原点位置, xyz 都在 -400 ~ 400 之间, 生成的点一定在边长为 800 的立方体内
				var x = Math.random() * n - n2; // 随机 -400 ~ 400 的值
				var y = Math.random() * n - n2; // 随机 -400 ~ 400 的值
				var z = Math.random() * n - n2; // 随机 -400 ~ 400 的值
				
				/**
				 * Math.random() * d 为 0 ~ 12, -d2 即为 -6 ~ 6,即三角形顶点 x 在原点基础上 -6 或 +6
				 */
				// 每一个点(小三角形)的点a 的 xyz, 因为随机, 所有每一个小三角形大小不一样, 只控制了最大值
				var ax = x + Math.random() * d - d2;
				var ay = y + Math.random() * d - d2;
				var az = z + Math.random() * d - d2;
				
				var bx = x + Math.random() * d - d2;
				var by = y + Math.random() * d - d2;
				var bz = z + Math.random() * d - d2;
				
				var cx = x + Math.random() * d - d2;
				var cy = y + Math.random() * d - d2;
				var cz = z + Math.random() * d - d2;
				
				// 小三角形点a坐标占 index 0-2
				positions[i] = ax;
				positions[i+1] = ay;
				positions[i+2] = az;
				// 小三角形点b坐标占 index 3-5
				positions[i+3] = bx;
				positions[i+4] = by;
				positions[i+5] = bz;
				// 小三角形点c坐标占 index 6-8
				positions[i+6] = cx;
				positions[i+7] = cy;
				positions[i+8] = cz;
				// 设置三角形的三个顶点
				pA.set(ax, ay, az);
				pB.set(bx, by, bz);
				pC.set(cx, cy, cz);
				// pC pB向量相减(三角形一条边)
				cb.subVectors(pC, pB);
				// A B向量相减(三角形一条边)
				ab.subVectors(pA, pB);
				// cb ab 向量积, 即同时垂直上面两条边的向量,即法向量
				cb.cross(ab);
				// 将法向量转为单位向量
				cb.normalize();
				//法向量的方向可以这样表示N(nx, ny, nz);
				var nx = cb.x;
				var ny = cb.y;
				var nz = cb.z;
				
				// 0-8 循环法向量 xyz
				normals[i]     = nx;
				normals[i+1] = ny;
				normals[i+2] = nz;
				
				normals[i+3] = nx;
				normals[i+4] = ny;
				normals[i+5] = nz;
				
				normals[i+6] = nx;
				normals[i+7] = ny;
				normals[i+8] = nz;

				//颜色用rgb表示, rgb每一个分量取值范围0-1,vx,vy,vz分别对应rgb值。
				/**
				 * x 点的 x 值, -400-400,立方体边长为 800, 所以 vx 为 -0.5 ~ 0.5,+0.5 后范围为 0 ~ 1
				 * 通过这样的计算, 相同的 xyz 颜色相同, 相近的 xyz 颜色相近
				 */
				var vx = (x/n) + 0.5;
				var vy = (y/n) + 0.5;
				var vz = (z/n) + 0.5;
				
				// color 存该点的颜色
				color.setRGB(vx, vy, vz);

				//将三角形的三个顶点设为同样的颜色
				colors[i] = color.r;
				colors[i+1] = color.g;
				colors[i+2] = color.b;
			
				colors[i+3] = color.r;
				colors[i+4] = color.g;
				colors[i+5] = color.b;
				
				colors[i+6] = color.r;
				colors[i+7] = color.g;
				colors[i+8] = color.b;
			}
			
			// 设置原点, 三角形, 颜色
			geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
			geometry.addAttribute('normal', new THREE.BufferAttribute(normals, 3));
			geometry.addAttribute('color', new THREE.BufferAttribute(colors, 3));
			
			// 计算外边界球
			geometry.computeBoundingSphere();
			
			// 创建立方体材质
			var material = new THREE.MeshPhongMaterial({
				color : 0xaaaaaa, 
				ambient : 0xaaaaaa, 
				specular : 0xffffff, 
				shininess : 250,
				side : THREE.DoubleSide, 
				vertexColors : THREE.VertexColors
			});
			
			// 创建立方体物体并添加到场景
			mesh = new THREE.Mesh(geometry, material);
			scene.add(mesh);
			
			window.addEventListener('resize', onWindowResize, false);			
		}
		
		function onWindowResize(){
			camera.aspect = window.innerWidth / window.innerHeight;
			camera.updateProjectionMatrix();
			renderer.setSize( window.innerWidth, window.innerHeight);
			
		}
		
		function animate(){
			requestAnimationFrame(animate);
			render();
		}
		
		function render(){
			var time = Date.now() * 0.001;
			mesh.rotation.x = time * 0.25;
			mesh.rotation.y = time * 0.5;
			
			renderer.render(scene, camera);	
		}
	</script>
</html>
 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值