first commit
This commit is contained in:
497
others/demo.html
Normal file
497
others/demo.html
Normal file
@@ -0,0 +1,497 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Three.js 技术分享 Demo</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: white;
|
||||
margin-bottom: 30px;
|
||||
font-size: 2.5em;
|
||||
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
||||
}
|
||||
.demo-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.demo-card {
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
.demo-card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
.demo-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 15px;
|
||||
color: white;
|
||||
}
|
||||
.demo-header h3 {
|
||||
margin: 0;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
.demo-header p {
|
||||
margin: 5px 0 0 0;
|
||||
opacity: 0.9;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.canvas-container {
|
||||
width: 100%;
|
||||
height: 350px;
|
||||
background: #f0f0f0;
|
||||
position: relative;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.controls {
|
||||
padding: 15px;
|
||||
background: #f9f9f9;
|
||||
}
|
||||
.control-group {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.control-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
button {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 5px;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
button:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
input[type="range"] {
|
||||
width: 100%;
|
||||
}
|
||||
.info-panel {
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||||
}
|
||||
.info-panel h2 {
|
||||
color: #667eea;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.info-panel ul {
|
||||
margin-left: 20px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
.concept-box {
|
||||
background: #f0f4ff;
|
||||
padding: 15px;
|
||||
border-left: 4px solid #667eea;
|
||||
margin: 10px 0;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.concept-box strong {
|
||||
color: #667eea;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🎨 Three.js 技术分享 - 互动 Demo</h1>
|
||||
|
||||
<div class="demo-grid">
|
||||
<!-- Demo 1: 旋转立方体 -->
|
||||
<div class="demo-card">
|
||||
<div class="demo-header">
|
||||
<h3>Demo 1: Hello Cube</h3>
|
||||
<p>最基础的 Three.js 场景:旋转的彩色立方体</p>
|
||||
</div>
|
||||
<div class="canvas-container" id="demo1"></div>
|
||||
<div class="controls">
|
||||
<div class="control-group">
|
||||
<label>旋转速度:</label>
|
||||
<input type="range" id="speed1" min="0" max="0.05" step="0.001" value="0.01">
|
||||
</div>
|
||||
<button onclick="changeColor1()">随机颜色</button>
|
||||
<button onclick="toggleRotation1()">暂停/继续</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Demo 2: 交互式场景 -->
|
||||
<div class="demo-card">
|
||||
<div class="demo-header">
|
||||
<h3>Demo 2: 交互式多物体</h3>
|
||||
<p>鼠标控制相机,点击物体改变颜色</p>
|
||||
</div>
|
||||
<div class="canvas-container" id="demo2"></div>
|
||||
<div class="controls">
|
||||
<div class="control-group">
|
||||
<label>光照强度:</label>
|
||||
<input type="range" id="lightIntensity" min="0" max="3" step="0.1" value="1">
|
||||
</div>
|
||||
<button onclick="addSphere()">添加球体</button>
|
||||
<button onclick="resetScene2()">重置场景</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Demo 3: 动画场景 -->
|
||||
<div class="demo-card">
|
||||
<div class="demo-header">
|
||||
<h3>Demo 3: 粒子系统</h3>
|
||||
<p>动态粒子效果与后处理</p>
|
||||
</div>
|
||||
<div class="canvas-container" id="demo3"></div>
|
||||
<div class="controls">
|
||||
<div class="control-group">
|
||||
<label>粒子数量: <span id="particleCount">1000</span></label>
|
||||
<input type="range" id="particleSlider" min="500" max="5000" step="100" value="1000">
|
||||
</div>
|
||||
<button onclick="changeParticleColor()">改变颜色</button>
|
||||
<button onclick="resetParticles()">重置粒子</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-panel">
|
||||
<h2>💡 Three.js 核心概念速记</h2>
|
||||
<div class="concept-box">
|
||||
<strong>Scene(场景)</strong> - 电影片场,所有3D对象的容器
|
||||
</div>
|
||||
<div class="concept-box">
|
||||
<strong>Camera(相机)</strong> - 观众的眼睛,决定从哪个角度看场景
|
||||
</div>
|
||||
<div class="concept-box">
|
||||
<strong>Renderer(渲染器)</strong> - 摄影机,把3D场景渲染成2D图像
|
||||
</div>
|
||||
<div class="concept-box">
|
||||
<strong>Mesh = Geometry + Material</strong> - 网格 = 形状骨架 + 表面材质
|
||||
</div>
|
||||
<div class="concept-box">
|
||||
<strong>Light(光源)</strong> - 片场的灯光,让物体可见
|
||||
</div>
|
||||
|
||||
<h2 style="margin-top: 30px;">🎯 与建模师沟通清单</h2>
|
||||
<ul>
|
||||
<li>✅ 文件格式:优先使用 <strong>GLTF/GLB</strong> 格式</li>
|
||||
<li>✅ 面数控制:移动端 <5万面,PC端 <20万面</li>
|
||||
<li>✅ 贴图尺寸:建议 2K 以内,使用 2 的幂次方(512, 1024, 2048)</li>
|
||||
<li>✅ 材质类型:推荐 <strong>PBR 材质</strong>(基于物理的渲染)</li>
|
||||
<li>✅ 命名规范:使用英文,有意义的名称</li>
|
||||
<li>✅ 坐标系统:统一使用米制单位</li>
|
||||
<li>✅ 轴心点:确认物体中心点位置是否正确</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||
<script>
|
||||
// ========== Demo 1: 基础旋转立方体 ==========
|
||||
let scene1, camera1, renderer1, cube1, isRotating1 = true;
|
||||
|
||||
function initDemo1() {
|
||||
const container = document.getElementById('demo1');
|
||||
scene1 = new THREE.Scene();
|
||||
scene1.background = new THREE.Color(0x1a1a2e);
|
||||
|
||||
camera1 = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000);
|
||||
camera1.position.z = 5;
|
||||
|
||||
renderer1 = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer1.setSize(container.clientWidth, container.clientHeight);
|
||||
container.appendChild(renderer1.domElement);
|
||||
|
||||
const geometry = new THREE.BoxGeometry(2, 2, 2);
|
||||
const material = new THREE.MeshPhongMaterial({
|
||||
color: 0x00ff88,
|
||||
shininess: 100
|
||||
});
|
||||
cube1 = new THREE.Mesh(geometry, material);
|
||||
scene1.add(cube1);
|
||||
|
||||
const light = new THREE.PointLight(0xffffff, 1, 100);
|
||||
light.position.set(5, 5, 5);
|
||||
scene1.add(light);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight(0x404040);
|
||||
scene1.add(ambientLight);
|
||||
|
||||
animate1();
|
||||
}
|
||||
|
||||
function animate1() {
|
||||
requestAnimationFrame(animate1);
|
||||
if (isRotating1) {
|
||||
const speed = parseFloat(document.getElementById('speed1').value);
|
||||
cube1.rotation.x += speed;
|
||||
cube1.rotation.y += speed;
|
||||
}
|
||||
renderer1.render(scene1, camera1);
|
||||
}
|
||||
|
||||
function changeColor1() {
|
||||
cube1.material.color.setHex(Math.random() * 0xffffff);
|
||||
}
|
||||
|
||||
function toggleRotation1() {
|
||||
isRotating1 = !isRotating1;
|
||||
}
|
||||
|
||||
// ========== Demo 2: 交互式场景 ==========
|
||||
let scene2, camera2, renderer2, objects2 = [], pointLight2;
|
||||
|
||||
function initDemo2() {
|
||||
const container = document.getElementById('demo2');
|
||||
scene2 = new THREE.Scene();
|
||||
scene2.background = new THREE.Color(0x16213e);
|
||||
|
||||
camera2 = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000);
|
||||
camera2.position.set(0, 3, 8);
|
||||
camera2.lookAt(0, 0, 0);
|
||||
|
||||
renderer2 = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer2.setSize(container.clientWidth, container.clientHeight);
|
||||
container.appendChild(renderer2.domElement);
|
||||
|
||||
// 添加地面
|
||||
const planeGeometry = new THREE.PlaneGeometry(20, 20);
|
||||
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x0f3460 });
|
||||
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
|
||||
plane.rotation.x = -Math.PI / 2;
|
||||
scene2.add(plane);
|
||||
|
||||
// 添加初始物体
|
||||
createInitialObjects();
|
||||
|
||||
// 光源
|
||||
pointLight2 = new THREE.PointLight(0xffffff, 1);
|
||||
pointLight2.position.set(0, 5, 5);
|
||||
scene2.add(pointLight2);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight(0x404040);
|
||||
scene2.add(ambientLight);
|
||||
|
||||
// 鼠标控制
|
||||
let isDragging = false;
|
||||
let previousMousePosition = { x: 0, y: 0 };
|
||||
|
||||
renderer2.domElement.addEventListener('mousedown', () => isDragging = true);
|
||||
renderer2.domElement.addEventListener('mouseup', () => isDragging = false);
|
||||
renderer2.domElement.addEventListener('mousemove', (e) => {
|
||||
if (isDragging) {
|
||||
const deltaX = e.offsetX - previousMousePosition.x;
|
||||
const deltaY = e.offsetY - previousMousePosition.y;
|
||||
camera2.position.x += deltaX * 0.01;
|
||||
camera2.position.y -= deltaY * 0.01;
|
||||
camera2.lookAt(0, 0, 0);
|
||||
}
|
||||
previousMousePosition = { x: e.offsetX, y: e.offsetY };
|
||||
});
|
||||
|
||||
// 点击改变颜色
|
||||
renderer2.domElement.addEventListener('click', (e) => {
|
||||
const mouse = new THREE.Vector2(
|
||||
(e.offsetX / container.clientWidth) * 2 - 1,
|
||||
-(e.offsetY / container.clientHeight) * 2 + 1
|
||||
);
|
||||
const raycaster = new THREE.Raycaster();
|
||||
raycaster.setFromCamera(mouse, camera2);
|
||||
const intersects = raycaster.intersectObjects(objects2);
|
||||
if (intersects.length > 0) {
|
||||
intersects[0].object.material.color.setHex(Math.random() * 0xffffff);
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('lightIntensity').addEventListener('input', (e) => {
|
||||
pointLight2.intensity = parseFloat(e.target.value);
|
||||
});
|
||||
|
||||
animate2();
|
||||
}
|
||||
|
||||
function createInitialObjects() {
|
||||
const geometries = [
|
||||
new THREE.BoxGeometry(1, 1, 1),
|
||||
new THREE.SphereGeometry(0.6, 32, 32),
|
||||
new THREE.ConeGeometry(0.5, 1, 32),
|
||||
new THREE.TorusGeometry(0.5, 0.2, 16, 100)
|
||||
];
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const material = new THREE.MeshStandardMaterial({
|
||||
color: Math.random() * 0xffffff,
|
||||
metalness: 0.5,
|
||||
roughness: 0.5
|
||||
});
|
||||
const mesh = new THREE.Mesh(geometries[i], material);
|
||||
mesh.position.set((i - 1.5) * 2, 1, 0);
|
||||
scene2.add(mesh);
|
||||
objects2.push(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
function animate2() {
|
||||
requestAnimationFrame(animate2);
|
||||
objects2.forEach((obj, i) => {
|
||||
obj.rotation.y += 0.01;
|
||||
obj.position.y = 1 + Math.sin(Date.now() * 0.001 + i) * 0.3;
|
||||
});
|
||||
renderer2.render(scene2, camera2);
|
||||
}
|
||||
|
||||
function addSphere() {
|
||||
const geometry = new THREE.SphereGeometry(0.5, 32, 32);
|
||||
const material = new THREE.MeshStandardMaterial({
|
||||
color: Math.random() * 0xffffff,
|
||||
metalness: 0.5,
|
||||
roughness: 0.5
|
||||
});
|
||||
const sphere = new THREE.Mesh(geometry, material);
|
||||
sphere.position.set(Math.random() * 6 - 3, 1, Math.random() * 6 - 3);
|
||||
scene2.add(sphere);
|
||||
objects2.push(sphere);
|
||||
}
|
||||
|
||||
function resetScene2() {
|
||||
objects2.forEach(obj => scene2.remove(obj));
|
||||
objects2 = [];
|
||||
createInitialObjects();
|
||||
}
|
||||
|
||||
// ========== Demo 3: 粒子系统 ==========
|
||||
let scene3, camera3, renderer3, particles3;
|
||||
|
||||
function initDemo3() {
|
||||
const container = document.getElementById('demo3');
|
||||
scene3 = new THREE.Scene();
|
||||
scene3.background = new THREE.Color(0x000000);
|
||||
|
||||
camera3 = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000);
|
||||
camera3.position.z = 50;
|
||||
|
||||
renderer3 = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer3.setSize(container.clientWidth, container.clientHeight);
|
||||
container.appendChild(renderer3.domElement);
|
||||
|
||||
createParticles(1000);
|
||||
|
||||
document.getElementById('particleSlider').addEventListener('input', (e) => {
|
||||
const count = parseInt(e.target.value);
|
||||
document.getElementById('particleCount').textContent = count;
|
||||
scene3.remove(particles3);
|
||||
createParticles(count);
|
||||
});
|
||||
|
||||
animate3();
|
||||
}
|
||||
|
||||
function createParticles(count) {
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
const positions = [];
|
||||
const colors = [];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
positions.push(
|
||||
Math.random() * 100 - 50,
|
||||
Math.random() * 100 - 50,
|
||||
Math.random() * 100 - 50
|
||||
);
|
||||
colors.push(Math.random(), Math.random(), Math.random());
|
||||
}
|
||||
|
||||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
||||
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
|
||||
|
||||
const material = new THREE.PointsMaterial({
|
||||
size: 0.5,
|
||||
vertexColors: true,
|
||||
transparent: true,
|
||||
opacity: 0.8
|
||||
});
|
||||
|
||||
particles3 = new THREE.Points(geometry, material);
|
||||
scene3.add(particles3);
|
||||
}
|
||||
|
||||
function animate3() {
|
||||
requestAnimationFrame(animate3);
|
||||
particles3.rotation.y += 0.002;
|
||||
particles3.rotation.x += 0.001;
|
||||
|
||||
const positions = particles3.geometry.attributes.position.array;
|
||||
for (let i = 0; i < positions.length; i += 3) {
|
||||
positions[i + 1] += Math.sin(Date.now() * 0.001 + i) * 0.01;
|
||||
}
|
||||
particles3.geometry.attributes.position.needsUpdate = true;
|
||||
|
||||
renderer3.render(scene3, camera3);
|
||||
}
|
||||
|
||||
function changeParticleColor() {
|
||||
const colors = particles3.geometry.attributes.color.array;
|
||||
for (let i = 0; i < colors.length; i += 3) {
|
||||
colors[i] = Math.random();
|
||||
colors[i + 1] = Math.random();
|
||||
colors[i + 2] = Math.random();
|
||||
}
|
||||
particles3.geometry.attributes.color.needsUpdate = true;
|
||||
}
|
||||
|
||||
function resetParticles() {
|
||||
const count = parseInt(document.getElementById('particleSlider').value);
|
||||
scene3.remove(particles3);
|
||||
createParticles(count);
|
||||
}
|
||||
|
||||
// 初始化所有 Demo
|
||||
window.onload = () => {
|
||||
initDemo1();
|
||||
initDemo2();
|
||||
initDemo3();
|
||||
};
|
||||
|
||||
// 响应式处理
|
||||
window.addEventListener('resize', () => {
|
||||
const containers = ['demo1', 'demo2', 'demo3'];
|
||||
const cameras = [camera1, camera2, camera3];
|
||||
const renderers = [renderer1, renderer2, renderer3];
|
||||
|
||||
containers.forEach((id, i) => {
|
||||
const container = document.getElementById(id);
|
||||
cameras[i].aspect = container.clientWidth / container.clientHeight;
|
||||
cameras[i].updateProjectionMatrix();
|
||||
renderers[i].setSize(container.clientWidth, container.clientHeight);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
others/huohuo-vue3-threejs-master.zip
Normal file
BIN
others/huohuo-vue3-threejs-master.zip
Normal file
Binary file not shown.
BIN
others/mzjc_bansw.glb
Normal file
BIN
others/mzjc_bansw.glb
Normal file
Binary file not shown.
BIN
others/three.js-dev.zip
Normal file
BIN
others/three.js-dev.zip
Normal file
Binary file not shown.
Reference in New Issue
Block a user