three.js可以生成几种已经设定好的几何体,比如锥体,球体,圆柱体等,three.js也提供了工具可以自己通过顶点来创建自定义的几何体
创建自定义顶点
var vertices = [
new THREE.Vector3(0,2,0),
new THREE.Vector3(-1,0,0),
new THREE.Vector3(0,0,1),
new THREE.Vector3(1,0,0),
new THREE.Vector3(0,0,-1),
new THREE.Vector3(0,-2,0)
];
上面的代码创建了6个顶点,就顶点来说就是类似于两个椎体拼接起来的形状;
创建面
var faces = [
new THREE.Face3(0,1,2),
new THREE.Face3(0,2,3),
new THREE.Face3(0,3,4),
new THREE.Face3(0,4,1),
new THREE.Face3(5,2,1),
new THREE.Face3(5,3,2),
new THREE.Face3(5,4,3),
new THREE.Face3(5,1,4),
];
将分别的三个顶点形成一个三角形的面,顶点相连的逆时针所面向的方向为渲染的方向,如果是背向这个方向的话这个面是不会对光源做出反应的。
创建几何体架构
var geom = new THREE.Geometry();
geom.vertices = vertices;
geom.faces = faces;
geom.computeFaceNormals();//计算法向量 这决定了对光做出的反应
添加材质
var materials = [
new THREE.MeshLambertMaterial({opacity: 0.8,color:0x44e144,transparent:true}),//几何体面的材质
new THREE.MeshBasicMaterial({color:0x008800,wireframe:true}),//一个几何体框架的材质
];
创建几何体
mesh = THREE.SceneUtils.createMultiMaterialObject(geom,materials);//创建有多个材质的对象
mesh.name = '源';
mesh.children.forEach(function (e) {
e.castShadow = true; //可以生成影子
});
在我使用的版本中(97)SceneUtils 被移动到了 js/utils/SceneUtils.js需要另外引用,之前的版本是直接在three.js就有的
之后将这个对象添加进场景中就能看到了:
scene.add(mesh);
mesh.position.set(0,4,0);
完成的:
完整代码1
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="https://cdn.bootcss.com/three.js/92/three.min.js"></script>
<script src="https://threejs.org/examples/js/utils/SceneUtils.js"></script>
<script src="https://threejs.org/examples/js/libs/stats.min.js"></script>
<script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>
<script src="https://threejs.org/examples/js/libs/dat.gui.min.js"></script>
<title>场景</title>
<style>
body{
margin:0;padding:0;
overflow:hidden;
}
</style>
</head>
<body>
<div id="WebGL-output"></div>
<script>
var scene,camera,renderer,axes,mesh;
var planeGeometry;
var guiControl = new function(){
this.rotateSpeed = 0.02;
}
function init(){
scene = new THREE.Scene();
//
camera = new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,1000);
camera.position.set(-15,15,15);
camera.lookAt(0,4,0);
//渲染器
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0Xeeeeee));
renderer.shadowMapEnabled;
renderer.shadowMapEnabled = true;
renderer.setSize(window.innerWidth,window.innerHeight);
document.getElementById("WebGL-output").appendChild(renderer.domElement);
axes = new THREE.AxesHelper(20);//辅助线
scene.add(axes);
//下面的平面
planeGeometry = new THREE.PlaneGeometry(60,30);
var planeMaterial = new THREE.MeshPhongMaterial({color:0xeeeeee});
var plane = new THREE.Mesh(planeGeometry,planeMaterial);
plane.position.set(0,0,0);
plane.rotation.x = -0.5*Math.PI;
plane.receiveShadow = true;
scene.add(plane);
//
var stats = new Stats();
document.body.appendChild(stats.dom);
//球形轨道控制器
var controls = new THREE.TrackballControls(camera,renderer.domElement);
controls.maxDistance = 400.0;
controls.minDistance = 10.0;
controls.target = new THREE.Vector3(0,3,0);
//添加光线
var spotlight = new THREE.SpotLight(0xffffff);
var spotlightHelper = new THREE.SpotLightHelper(spotlight);
spotlight.position.set(-50,100,2);
spotlight.castShadow = true;
spotlight.shadow.mapSize.width = 2000; // default 默认512越大越细致消耗越多资源
spotlight.shadow.mapSize.height = 2000; // default 默认512
scene.add(spotlight);
scene.add(spotlightHelper);
var vertices = [
new THREE.Vector3(0,2,0),
new THREE.Vector3(-1,0,0),
new THREE.Vector3(0,0,1),
new THREE.Vector3(1,0,0),
new THREE.Vector3(0,0,-1),
new THREE.Vector3(0,-2,0)
];
var faces = [
new THREE.Face3(0,1,2),
new THREE.Face3(0,2,3),
new THREE.Face3(0,3,4),
new THREE.Face3(0,4,1),
new THREE.Face3(5,2,1),
new THREE.Face3(5,3,2),
new THREE.Face3(5,4,3),
new THREE.Face3(5,1,4),
];
var geom = new THREE.Geometry();
geom.vertices = vertices;
geom.faces = faces;
geom.computeFaceNormals();
var materials = [
new THREE.MeshLambertMaterial({opacity: 0.8,color:0x44e144,transparent:true}),
new THREE.MeshBasicMaterial({color:0x008800,wireframe:true}),
];
mesh = THREE.SceneUtils.createMultiMaterialObject(geom,materials);
mesh.name = '源';
mesh.children.forEach(function (e) {
e.castShadow = true
});
scene.add(mesh);
mesh.position.set(0,4,0);//设定位置
renderer.shadowMap.enabled = true;
function animate(){
controls.update();
stats.update();
requestAnimationFrame(animate);
renderer.render(scene,camera);
}
function onResize(){
camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onResize, false);
animate();
onResize();
}
init();
</script>
</body>
</html>
添加可控的顶点
我想要在改变不同顶点的位置会对几何体产生什么样的影响,一个实时控制顶点位置的例子
在init函数里面加入
function addControl(x,y,z){//设置可控的顶点
var pointPositions = new function(){
this.x = x;
this.y = y;
this.z = z;
};
return pointPositions;
}
var controlPoints = [];
controlPoints.push(addControl(0,2,0));
controlPoints.push(addControl(-1,0,0));
controlPoints.push(addControl(0,0,1));
controlPoints.push(addControl(1,0,0));
controlPoints.push(addControl(0,0,-1));
controlPoints.push(addControl(0,-2,0));
var gui = new dat.GUI();//增加的gui控制组件
for(var i = 0 ; i < 6; i++ ){
fun1 = gui.addFolder("点" + (i+1) );
fun1.add(controlPoints[i],'x', -10, 10);
fun1.add(controlPoints[i], 'y', -10, 10);
fun1.add(controlPoints[i], 'z', -10, 10);
}
gui.add(new function(){
this.clone = function(){
var clonedGeometry = mesh.children[0].geometry.clone();//克隆函数 只会 克隆顶点,面与uvs(用于控制贴图与颜色的表现形式)
var materials = [
new THREE.MeshLambertMaterial({opacity:0.7,color:(0xffffff*Math.random()),transparent:true}),
new THREE.MeshBasicMaterial({color:0x44cc44,wireframe:true})
];
var mesh2 = THREE.SceneUtils.createMultiMaterialObject(clonedGeometry,materials);
mesh2.children.forEach(function(e){
e.castShadow = true;
});
//克隆出来在随机位置生成
var randomx = Math.random()*20 - 10;
var randomy = Math.random()*2 + 4;
var randomz = Math.random()*20 - 10;
mesh2.translateX(randomx);
mesh2.translateY(randomy);
mesh2.translateZ(randomz);
mesh2.name = "克隆";
scene.add(mesh2);
}
}, 'clone');
animate函数中加入
scene.traverse(function (obj) {
if(obj instanceof THREE.Mesh && obj != plane){
obj.rotation.y += (guiControl.rotateSpeed);
}
});//让每个对象旋转
var vertices = [];//更新的顶点
for( var i = 0; i < controlPoints.length; i++){
vertices.push(new THREE.Vector3(controlPoints[i].x, controlPoints[i].y, controlPoints[i].z));
}
mesh.children.forEach(function(e){
e.geometry.vertices = vertices;
//e.geometry.verticesNeedUpdate = true; //在旧版本中只需要更新顶点就能重新渲染
e.geometry.elementsNeedUpdate = true;//在新版本中只需要更新面就行 连带更新顶点 放在渲染之前, 渲染之后值会重新变为false,在实际操作中只需要在有变动的时候才进行更新,这里是每一帧都进行更新
e.geometry.computeFaceNormals();
});
最后的完整代码:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="https://cdn.bootcss.com/three.js/92/three.min.js"></script>
<!--<script src="../libs/three.js"></script>-->
<script src="https://threejs.org/examples/js/utils/SceneUtils.js"></script>
<script src="https://threejs.org/examples/js/libs/stats.min.js"></script>
<script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>
<script src="https://threejs.org/examples/js/libs/dat.gui.min.js"></script>
<title>场景</title>
<style>
body{
margin:0;padding:0;
overflow:hidden;
}
</style>
</head>
<body>
<div id="WebGL-output"></div>
<script>
var scene,camera,renderer,axes,mesh;
var planeGeometry;
var guiControl = new function(){
this.rotateSpeed = 0.02;
}
function init(){
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,1000);
camera.position.set(-15,15,15);
camera.lookAt(0,4,0);
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0Xeeeeee));
renderer.shadowMapEnabled;
renderer.shadowMapEnabled = true;
renderer.setSize(window.innerWidth,window.innerHeight);
document.getElementById("WebGL-output").appendChild(renderer.domElement);
axes = new THREE.AxesHelper(20);//辅助线
scene.add(axes);
//下面的平面
planeGeometry = new THREE.PlaneGeometry(60,30);
var planeMaterial = new THREE.MeshPhongMaterial({color:0xeeeeee});
var plane = new THREE.Mesh(planeGeometry,planeMaterial);
plane.position.set(0,0,0);
plane.rotation.x = -0.5*Math.PI;
plane.receiveShadow = true;
scene.add(plane);
//
var stats = new Stats();
document.body.appendChild(stats.dom);
//球形轨道控制器
var controls = new THREE.TrackballControls(camera,renderer.domElement);
controls.maxDistance = 400.0;
controls.minDistance = 10.0;
controls.target = new THREE.Vector3(0,3,0);
var spotlight = new THREE.SpotLight(0xffffff);
var spotlightHelper = new THREE.SpotLightHelper(spotlight);
spotlight.position.set(-50,100,2);
spotlight.castShadow = true;
spotlight.shadow.mapSize.width = 2000; // default 默认512越大越细致消耗越多资源
spotlight.shadow.mapSize.height = 2000; // default 默认512
scene.add(spotlight);
scene.add(spotlightHelper);
var vertices = [
new THREE.Vector3(0,2,0),
new THREE.Vector3(-1,0,0),
new THREE.Vector3(0,0,1),
new THREE.Vector3(1,0,0),
new THREE.Vector3(0,0,-1),
new THREE.Vector3(0,-2,0)
];
var faces = [
new THREE.Face3(0,1,2),
new THREE.Face3(0,2,3),
new THREE.Face3(0,3,4),
new THREE.Face3(0,4,1),
new THREE.Face3(5,2,1),
new THREE.Face3(5,3,2),
new THREE.Face3(5,4,3),
new THREE.Face3(5,1,4),
];
var geom = new THREE.Geometry();
geom.vertices = vertices;
geom.faces = faces;
geom.computeFaceNormals();
//geom.computeVertexNormals();
var materials = [
new THREE.MeshLambertMaterial({opacity: 0.8,color:0x44e144,transparent:true}),
new THREE.MeshBasicMaterial({color:0x008800,wireframe:true}),
];
mesh = THREE.SceneUtils.createMultiMaterialObject(geom,materials);
mesh.name = '源';
mesh.children.forEach(function (e) {
e.castShadow = true
});
scene.add(mesh);
mesh.position.set(0,4,0);
function addControl(x,y,z){//设置可控的顶点
var pointPositions = new function(){
this.x = x;
this.y = y;
this.z = z;
};
return pointPositions;
}
var controlPoints = [];
controlPoints.push(addControl(0,2,0));
controlPoints.push(addControl(-1,0,0));
controlPoints.push(addControl(0,0,1));
controlPoints.push(addControl(1,0,0));
controlPoints.push(addControl(0,0,-1));
controlPoints.push(addControl(0,-2,0));
var gui = new dat.GUI();
for(var i = 0 ; i < 6; i++ ){
fun1 = gui.addFolder("点" + (i+1) );
fun1.add(controlPoints[i],'x', -10, 10);
fun1.add(controlPoints[i], 'y', -10, 10);
fun1.add(controlPoints[i], 'z', -10, 10);
}
gui.add(new function(){
this.clone = function(){
var clonedGeometry = mesh.children[0].geometry.clone();
var materials = [
new THREE.MeshLambertMaterial({opacity:0.7,color:(0xffffff*Math.random()),transparent:true}),
new THREE.MeshBasicMaterial({color:0x44cc44,wireframe:true})
];
var mesh2 = THREE.SceneUtils.createMultiMaterialObject(clonedGeometry,materials);
mesh2.children.forEach(function(e){
e.castShadow = true;
});
var randomx = Math.random()*20 - 10;
var randomy = Math.random()*2 + 4;
var randomz = Math.random()*20 - 10;
mesh2.translateX(randomx);
mesh2.translateY(randomy);
mesh2.translateZ(randomz);
mesh2.name = "克隆";
console.log(mesh2);
console.log(scene.getObjectByName("克隆"));
//scene.remove(scene.getObjectByName("克隆"));
scene.add(mesh2);
}
}, 'clone');
gui.add(guiControl, 'rotateSpeed' , 0, 1);
//renderer.shadowMap.enabled = true;
function animate(){
controls.update();
stats.update();
scene.traverse(function (obj) {
if(obj instanceof THREE.Mesh && obj != plane){
obj.rotation.y += (guiControl.rotateSpeed);
}
});
var vertices = [];
for( var i = 0; i < controlPoints.length; i++){
vertices.push(new THREE.Vector3(controlPoints[i].x, controlPoints[i].y, controlPoints[i].z));
}
mesh.children.forEach(function(e){
e.geometry.vertices = vertices;
//e.geometry.verticesNeedUpdate = true;
e.geometry.elementsNeedUpdate = true;
e.geometry.computeFaceNormals();
});
requestAnimationFrame(animate);
renderer.render(scene,camera);
}
function onResize(){
camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onResize, false);
animate();
onResize();
}
init();
</script>
</body>
</html>
完成之后的连接:
three.js自定义几何体创建与对象克隆
照着这个链接里的内容试一下应该就可以知道这些顶点是如何影响整个几何体的渲染方式了。