绘制多个立方体
1.搭建react+ts 项目
bash
复制代码
npx create-react-app basics-demo --template typescript
2.安装three依赖
bash
复制代码
npm install three @types/three --save
3.安装路由
bash
复制代码
npm install react-router@6 react-router-dom@6
4.用路由组件包裹APP。
ts
复制代码
import { BrowserRouter } from "react-router-dom";
import { createRoot } from "react-dom/client";
import App from "./App";
const container = document.getElementById("root") as HTMLElement;
const root = createRoot(container);
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
5.构建项目页面
ts
复制代码
import React from "react";
const Basics: React.FC = (): JSX.Element => {
return (
<nav style={{ width: "60%", margin: "auto" }}>
<h2>three.js 基础示例</h2>
</nav>
);
};
export default Basics;
- src/view/RenderStructure.tsx
ts
复制代码
import React from "react";
const RenderStructure: React.FC = (): JSX.Element => {
return <div>RenderStructure 渲染结构</div>;
};
export default RenderStructure;
6.用useRoutes hook 搭建路由。
ts
复制代码
import React from "react";
import { useRoutes } from "react-router-dom";
import "./App.css";
import Basics from "./view/Basics";
import RenderStructure from "./view/RenderStructure";
const App: React.FC = (): JSX.Element => {
const routing = useRoutes([
{
path: "/",
element: <Basics />,
},
{
path: "RenderStructure",
element: <RenderStructure />,
]);
return <>{routing}</>;
};
export default App;
7.建立导航栏
ts
复制代码
import React from "react";
import { Link } from "react-router-dom";
const Basics: React.FC = (): JSX.Element => {
return (
<nav style={{ width: "60%", margin: "auto" }}>
<h2>three.js 基础示例</h2>
<ul>
<li>
<Link to="/RenderStructure">RenderStructure 渲染结构</Link>
</li>
</ul>
</nav>
);
};
export default Basics;
8.在RenderStructure.tsx 页面导入立方体
- src/view/RenderStructure.tsx
ts
复制代码
import React, { useRef, useEffect } from "react";
import { BoxGeometry, DirectionalLight, Mesh, MeshNormalMaterial, MeshPhongMaterial, PerspectiveCamera, Scene, WebGLRenderer } from "three";
const { innerWidth, innerHeight } = window;
const scene = new Scene();
const camera = new PerspectiveCamera(75, innerWidth / innerHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
const geometry = new BoxGeometry();
const material = new MeshNormalMaterial();
const cube = new Mesh(geometry, material);
scene.add(cube);
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
const RenderStructure: React.FC = (): JSX.Element => {
const divRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const { current } = divRef;
if (current) {
current.innerHTML = "";
current.append(renderer.domElement);
animate();
}
}, []);
return <div ref={divRef}></div>;
};
export default RenderStructure;
- 在上面的代码中,没有直接建立 ,而是在WebGLRenderer 对象的实例化方法里建立的,在其源码可以找到相关逻辑:
js
复制代码
function WebGLRenderer( parameters = {} ) {
const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement()
......
this.domElement = _canvas;
......
}
- 通过WebGLRenderer 对象建立了canvas后,再在react的函数组件的useEffect hook 中,将canvas 添加到div 中。
js
复制代码
const RenderStructure: React.FC = (): JSX.Element => {
const divRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const { current } = divRef;
current && current.append(renderer.domElement);
animate();
}, []);
return <div ref={divRef}></div>;
};
- 当前这个立方体的材质是MeshNormalMaterial,并不受光照影响
9.给立方体换个MeshPhongMaterial 材质,再添加光源
ts
复制代码
const geometry = new BoxGeometry();
const material = new MeshPhongMaterial({ color: 0x44aa88 });
const cube = new Mesh(geometry, material);
scene.add(cube);
const color = 0xffffff;
const intensity = 1;
const light = new DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
- 当前的渲染结构如下:
- 效果如下:
10. 添加两个立方体,几何体和材质可被多个Mesh 对象共享
ts
复制代码
const geometry = new BoxGeometry();
const material = new MeshPhongMaterial({ color: 0x44aa88 });
const cubes = [-2, 0, 2].map((num) => makeInstance(num));
scene.add(...cubes);
function makeInstance(x: number) {
const cube = new Mesh(geometry, material);
cube.position.x = x;
return cube;
}
function animate() {
requestAnimationFrame(animate);
cubes.forEach((cube) => {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
});
renderer.render(scene, camera);
}
- 当前的渲染结构如下:
- 效果如下: