Three 之 three.js (webgl)旋转函数的简单整理,以及实现绕轴旋转的简单案例
目录
Three 之 three.js (webgl)旋转函数的简单整理,以及实现绕轴旋转的简单案例
一、简单介绍
Three js 开发的一些知识整理,方便后期遇到类似的问题,能够及时查阅使用。
本节介绍, three.js (webgl)中几个常用旋转函数的整理,并在此基础上实现一个球体从一点旋转到另一点的简单案例,实现方式很多,这里仅供思路提供,如果有不足之处,欢迎指出,或者你有更好的方法,欢迎留言。
Three js 中的几个旋转属性/函数,值得注意的是:
- 这几个旋转函数都是基于局部旋转的
- 注意,这里设置的数值是弧度,需要和角度区分开
- 角度 转 弧度 MathUtils.degToRad(deg)
- 弧度 转 角度 MathUtils.radToDeg (rad)
1、Mesh 中的 rotation 属性
属性名 说明 属性值类型 使用方法 .rotation 物体的局部旋转,以弧度来表示 弧度 object3D.rotation.x = MathUtils.degToRad(90) 2、rotateOnAxis 在局部空间中绕着该物体的轴来旋转一个物体
注意:axis 最好是单位向量
方法 传参 使用方法 .rotateOnAxis ( axis : Vector3, angle : Float ) object3D.rotateOnAxis(new Vector3(1,0,0),180) 3、rotateX / rotateY / rotateZ 绕局部空间的轴旋转这个物体
方法 传参 传参类型 使用方法 .rotateX ( rad : Float ) 弧度 object3D.rotateX (MathUtils.degToRad(90)) .rotateY ( rad : Float ) 弧度 object3D.rotateY(MathUtils.degToRad(90)) .rotateZ ( rad : Float ) 弧度 object3D.rotateZ (MathUtils.degToRad(90))
二、案例简单示意图
三、案例旋转的实现原理
1、向量 angleTo 计算 A 向量与 B 向量之前的角度
2、向量 cross 计算 A 向量与 B 向量的法向量,作为旋转轴
3、使用上面获得的数据 ,借助 Mesh.rotateOnAxis 实现从 A 点旋转到 B 点
四、注意事项
1、Mesh.rotateOnAxis 是局部旋转,这里一个技巧是,在圆心位置添加一个父物体,把 A 点物体添加到父物体,旋转父物体,从而实现 从 A 点旋转运动到 B 点
五、该案例的大概实现步骤
该案例基于Threejs GitHub - mrdoob/three.js: JavaScript 3D Library.
工程框架创建实现的
1、创建脚本,引入 Three,创建基本的 Three 环境,添加场景,摄像机,光等
2、添加进行旋转演示的 3D 物体
3、 计算需要旋转的相关参数,包括旋转角度(顺时针/逆时针角度),旋转轴向量(顺时针/逆时针轴向量)等
4、实现小球顺时针/逆时针一点一点从 A 运动到 B 点,然后在 animation 或者 render 函数中调用即可
5、到这里,就可以在浏览器运行即可 看到运行效果
六、关键代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>10TestRotate</title>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "../../../build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from '../../jsm/controls/OrbitControls.js';
import Stats from '../../jsm/libs/stats.module.js';
let scene, camera, renderer, stats;
var spheremeshT1;
var spheremeshT2;
let POSITION_A ;
let POSITION_B ;
let POSITION_Center ;
let T1RotationBasePositionObject;
let T2RotationBasePositionObject;
let targetDegreeT1
let targetDegreeT2
var degreeT1 = 0
var degreeT2 = 0
let T1RotateAixes
let T2RotateAixes
init();
animate();
function init() {
// scene
initScene();
// camera
initCamera();
// light
initLight();
// renderer
initRenderer();
// OrbitControls
initOrbitControls();
stats = new Stats();
document.body.appendChild( stats.dom );
// onWindowResize
window.addEventListener( 'resize', onWindowResize );
axiesHelper();
stats = new Stats();
document.body.appendChild( stats.dom );
// window.location.href = renderer.domElement.toDataURL( 'image/png' );
addTestRoateObject();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function initScene( ) {
scene = new THREE.Scene();
}
function initCamera( ) {
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 200 );
camera.position.set( 0, 0, 25 );
}
function initLight( ) {
const ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );
scene.add( ambientLight );
const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.6 );
directionalLight.position.set( - 1, 1, 100 );
scene.add( directionalLight );
}
function initRenderer( ) {
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setClearColor( 0xcccccc );
document.body.appendChild( renderer.domElement );
}
function initOrbitControls( ) {
const controls = new OrbitControls( camera, renderer.domElement );
controls.minDistance = 5;
controls.maxDistance = 50;
controls.enablePan = false; // 移动 禁止
}
function animate() {
requestAnimationFrame( animate );
stats.update();
render();
}
function axiesHelper( ) {
var helper = new THREE.AxesHelper( 20 );
scene.add( helper );
}
function render() {
renderer.render( scene, camera );
rotateRunDataT1T2();
}
function addTestRoateObject(){
POSITION_A = new THREE.Vector3( 0, 5, 0 );
POSITION_B = new THREE.Vector3( 5, 0, 0 );
POSITION_Center = new THREE.Vector3( 0, 0, 0 );
var geometry = new THREE.SphereGeometry( 5, 30 ); //矩形平面
var material = new THREE.MeshPhongMaterial( {} );
var spheremesh1 = new THREE.Mesh( geometry, material );
spheremesh1.position.set(POSITION_Center.x, POSITION_Center.y, POSITION_Center.z);
scene.add( spheremesh1 );
var geometryA = new THREE.SphereGeometry( 1, 30 ); //矩形平面
var materialA = new THREE.MeshPhongMaterial( {
color: '#cc0000'
} );
var spheremeshA = new THREE.Mesh( geometryA, materialA );
spheremeshA.position.set( POSITION_A.x, POSITION_A.y, POSITION_A.z );
scene.add( spheremeshA );
var geometryB = new THREE.SphereGeometry( 1, 30 ); //矩形平面
var materialB = new THREE.MeshPhongMaterial( {
color: '#cc00cc'
} );
var spheremeshB = new THREE.Mesh( geometryB, materialB );
spheremeshB.position.set( POSITION_B.x, POSITION_B.y, POSITION_B.z);
scene.add( spheremeshB );
var geometryT1 = new THREE.SphereGeometry( 1, 30 ); //矩形平面
var materialT1 = new THREE.MeshPhongMaterial( {} );
spheremeshT1 = new THREE.Mesh( geometryT1, materialT1 );
spheremeshT1.position.set( POSITION_A.x, POSITION_A.y, POSITION_A.z );
// 辅助旋转
T1RotationBasePositionObject = new THREE.Mesh( new THREE.SphereGeometry( 0.1, 6 ),materialT1);
T1RotationBasePositionObject.position.set(POSITION_Center.x, POSITION_Center.y, POSITION_Center.z );
T1RotationBasePositionObject.add(spheremeshT1);
scene.add( T1RotationBasePositionObject );
var geometryT2 = new THREE.SphereGeometry( 1, 30 ); //矩形平面
var materialT2 = new THREE.MeshPhongMaterial( {} );
spheremeshT2 = new THREE.Mesh( geometryT2, materialT2 );
spheremeshT2.position.set( POSITION_A.x, POSITION_A.y, POSITION_A.z );
// 辅助旋转
T2RotationBasePositionObject = new THREE.Mesh( new THREE.SphereGeometry( 0.1, 6 ),materialT2);
T2RotationBasePositionObject.position.set(POSITION_Center.x, POSITION_Center.y, POSITION_Center.z );
T2RotationBasePositionObject.add( spheremeshT2 );
scene.add( T2RotationBasePositionObject );
rotateTestDataT1T2();
}
function rotateTestDataT1T2() {
const v1T1 = new THREE.Vector3( POSITION_A.x, POSITION_A.y, POSITION_A.z );
console.log( v1T1.x + ' ' + v1T1.y + ' ' + v1T1.z );
const v2T1 = new THREE.Vector3( POSITION_B.x, POSITION_B.y, POSITION_B.z );
console.log( v2T1.x + ' ' + v2T1.y + ' ' + v2T1.z );
targetDegreeT1 = v1T1.angleTo( v2T1 );
console.log( 'targetDegreeT1 ' + targetDegreeT1 );
v1T1.cross( v2T1 );
console.log( v1T1.x + ' ' + v1T1.y + ' ' + v1T1.z );
//T1RotationBasePositionObject.rotateOnAxis( v1T1.normalize(),targetDegreeT1 )
T1RotateAixes = v1T1.normalize();
const v1T2 = new THREE.Vector3( POSITION_A.x, POSITION_A.y, POSITION_A.z );
console.log( v1T2.x + ' ' + v1T2.y + ' ' + v1T2.z );
const v2T2 = new THREE.Vector3( POSITION_B.x, POSITION_B.y, POSITION_B.z );
console.log( v2T2.x + ' ' + v2T2.y + ' ' + v2T2.z );
targetDegreeT2 = v2T2.angleTo( v1T2 );
console.log( 'targetDegreeT2 ' + targetDegreeT2 );
const nl = v2T2.cross( v1T2 );
console.log( v1T2.x + ' ' + v1T2.y + ' ' + v1T2.z );
const modifyDegree = Math.PI * 2 - targetDegreeT2;
console.log( modifyDegree );
//T2RotationBasePositionObject.rotateOnAxis( v1T2.normalize(), modifyDegree)
targetDegreeT2 = modifyDegree;
T2RotateAixes = v2T2.normalize();
}
function rotateRunDataT1T2() {
if ( degreeT1 < targetDegreeT1 ) {
degreeT1 += 0.01;
T1RotationBasePositionObject.rotateOnAxis( T1RotateAixes, 0.01 );
}
if ( degreeT2 < targetDegreeT2 ) {
degreeT2 += 0.01;
T2RotationBasePositionObject.rotateOnAxis( T2RotateAixes, 0.01 );
}
}
</script>
</body>
</html>