绘制多个立方体
1.搭建react+ts 项目
npx create-react-app basics-demo --template typescript
react+ts 的用法可参考此链接: https://react-typescript-cheatsheet.netlify.app/docs/basic/setup
2.安装three依赖
npm install three @types/three --save
3.安装路由
npm install react-router@6 react-router-dom@6
react-router-v6 的用法可参考此链接:https://juejin.cn/post/7088526716049555492
4.用路由组件包裹APP。
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.构建项目页面
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
import React from "react" ;
const RenderStructure: React. FC = ( ) : JSX . Element => {
return < div> RenderStructure 渲染结构< / div> ;
} ;
export default RenderStructure;
6.用useRoutes hook 搭建路由。
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.建立导航栏
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
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 对象的实例化方法里建立的,在其源码可以找到相关逻辑:
function WebGLRenderer ( parameters = { } ) {
const _canvas = parameters. canvas !== undefined ? parameters. canvas : createCanvasElement ( )
……
this . domElement = _canvas;
……
}
通过WebGLRenderer 对象建立了canvas后,再在react的函数组件的useEffect hook 中,将canvas 添加到div 中。
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 材质,再添加光源
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 对象共享
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) ;
}
当前的渲染结构如下: 效果如下: