1.Three.js简介
Three.js是一个基于JavaScript编写的开源3D图形库,利用WebGL技术在网页上渲染3D图形。它提供了许多高级功能,如几何体、纹理、光照、阴影等,以便开发者能够快速地创建复杂且逼真的3D场景。同时,Three.js还具有很好的跨平台和跨浏览器兼容性,让用户无需安装任何插件就可以在现代浏览器上观看3D内容。
2.Three.js的历史
Three.js的发展始于2010年,由Ricardo Cabello(在线别名为mrdoob)创建。当时WebGL刚刚兴起,开发者们对于这项技术的应用充满了好奇和期待。Cabello希望为WebGL开发者提供一个简单易用的工具,因此Three.js应运而生。经过多年的发展和社区贡献,Three.js已经成为了最流行的3D图形库之一,拥有丰富的功能和庞大的用户群体。
3.Three.js可以做什么
3D游戏开发、3D效果展示、物联网3D可视化等等…
4.主要组件
在Three.js中,有了场景(scene)、相机(camera)和渲染器(renderer) 这3个组建才能将物体渲染到网页中去。
1)场景
场景是一个容器,可以看做摄影的房间,在房间中可以布置背景、摆放拍摄的物品、添加灯光设备等。
2)相机
相机是用来拍摄的工具,通过控制相机的位置和方向可以获取不同角度的图像。
3)渲染器
渲染器利用场景和相机进行渲染,渲染过程好比摄影师拍摄图像,如果只渲染一次就是静态的图像,如果连续渲染就能得到动态的画面。在JS中可以使用requestAnimationFrame实现高效的连续渲染。
我们运用以上的基础特性,实现这个3D三角函数,主要是为了方便学生直观的理解数学概念。
这个3D版本包含以下特性:
- 三维可视化系统:
- 三维坐标系显示(X:红,Y:绿,Z:蓝)
- 半透明单位圆平面
- 正弦波(蓝色)和余弦波(绿色)的三维轨迹
- 动态交互功能:
- 鼠标右键拖拽旋转视角
- 自动旋转动画控制
- 实时角度调节滑块
- 三维元素展示:
- 红色半径线在XY平面上的动态变化
- 正弦波在XY平面的展开
- 余弦波在XZ平面的展开
<!DOCTYPE html>
<html>
<head>
<title>3D三角函数演示</title>
<style>
body {
margin: 0;
overflow: hidden;
}
#info {
position: absolute;
top: 20px;
left: 20px;
background: rgba(255,255,255,0.9);
padding: 15px;
border-radius: 5px;
}
.controls {
position: absolute;
top: 20px;
right: 20px;
background: rgba(255,255,255,0.9);
padding: 15px;
border-radius: 5px;
}
</style>
</head>
<body>
<div id="info">
<div>θ: <span id="angleValue">0</span>°</div>
<div>sinθ: <span id="sinValue">0</span></div>
<div>cosθ: <span id="cosValue">1</span></div>
</div>
<div class="controls">
<input type="range" id="angleSlider" min="0" max="360" value="0">
<button id="animateBtn">播放</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
let scene, camera, renderer;
let radius = 3;
let angle = 0;
let isAnimating = false;
// 初始化场景
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 设置相机位置
camera.position.set(5, 5, 8);
camera.lookAt(0, 0, 0);
// 添加坐标系
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 创建单位圆
createUnitCircle();
createSineWave();
createCosineWave();
// 添加交互控制
addEventListeners();
animate();
}
// 创建单位圆
function createUnitCircle() {
const geometry = new THREE.CircleGeometry(radius, 64);
const material = new THREE.MeshBasicMaterial({
color: 0x888888,
transparent: true,
opacity: 0.3
});
const circle = new THREE.Mesh(geometry, material);
circle.rotation.x = -Math.PI/2;
scene.add(circle);
}
// 创建正弦波轨迹
function createSineWave() {
const points = [];
const material = new THREE.LineBasicMaterial({ color: 0x2194ce });
for(let i = 0; i <= 360; i++) {
const theta = THREE.MathUtils.degToRad(i);
points.push(new THREE.Vector3(
radius * Math.cos(theta),
radius * Math.sin(theta),
0
));
}
const geometry = new THREE.BufferGeometry().setFromPoints(points);
this.sineWave = new THREE.Line(geometry, material);
scene.add(this.sineWave);
}
// 创建余弦波轨迹
function createCosineWave() {
const points = [];
const material = new THREE.LineBasicMaterial({ color: 0x45c945 });
for(let i = 0; i <= 360; i++) {
const theta = THREE.MathUtils.degToRad(i);
points.push(new THREE.Vector3(
radius * Math.cos(theta),
0,
radius * Math.sin(theta)
));
}
const geometry = new THREE.BufferGeometry().setFromPoints(points);
this.cosineWave = new THREE.Line(geometry, material);
scene.add(this.cosineWave);
}
// 更新三角函数可视化
function updateVisuals() {
const theta = THREE.MathUtils.degToRad(angle);
const sin = Math.sin(theta);
const cos = Math.cos(theta);
// 更新半径线
if(this.radiusLine) scene.remove(this.radiusLine);
const radiusGeometry = new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(radius*cos, radius*sin, 0)
]);
this.radiusLine = new THREE.Line(
radiusGeometry,
new THREE.LineBasicMaterial({ color: 0xff4444 })
);
scene.add(this.radiusLine);
// 更新数值显示
document.getElementById('angleValue').textContent = angle.toFixed(0);
document.getElementById('sinValue').textContent = sin.toFixed(3);
document.getElementById('cosValue').textContent = cos.toFixed(3);
}
// 事件监听
function addEventListeners() {
// 角度滑块
document.getElementById('angleSlider').addEventListener('input', (e) => {
angle = e.target.value;
updateVisuals();
});
// 动画按钮
document.getElementById('animateBtn').addEventListener('click', () => {
isAnimating = !isAnimating;
e.target.textContent = isAnimating ? '暂停' : '播放';
});
// 窗口大小调整
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
}
// 动画循环
function animate() {
requestAnimationFrame(animate);
if(isAnimating) {
angle = (angle + 0.5) % 360;
document.getElementById('angleSlider').value = angle;
updateVisuals();
}
renderer.render(scene, camera);
}
init();
</script>
</body>
</html>
我们也可以进一步增强
function addAdditionalFeatures() {
// 1. 添加正切函数可视化
const tanGeometry = new THREE.BufferGeometry();
const tanPoints = [];
for(let i = 0; i < 360; i+=0.5) {
const theta = THREE.MathUtils.degToRad(i);
tanPoints.push(new THREE.Vector3(
Math.cos(theta),
Math.tan(theta),
Math.sin(theta)
));
}
tanGeometry.setFromPoints(tanPoints);
const tanLine = new THREE.Line(
tanGeometry,
new THREE.LineBasicMaterial({ color: 0xffa500 })
);
scene.add(tanLine);
// 2. 添加网格辅助平面
const gridHelper = new THREE.GridHelper(10, 20);
scene.add(gridHelper);
// 3. 添加轨道控制器
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
}