前言
- 这个技术我已经觊觎很久了,我一开始在createjs与threejs中犹豫到底学哪个好,后来想到createjs 3D做起来还是比较少见,而且看案例感觉更多的是动画处理或者配合animatecc进行生成,而现实中有很多乱七八糟需求都是要搞个3d的,并且threejs生态比较好,所以决定搞threejs。
官网
安装
- 可以发现很多中文教程让你去引入threejs的js或者cdn,我一开始以为只有这么用three,后来发现issue里有人提出很多ts问题,然后去npm上一搜还真有。目前可以下载到最新的120版本。
- 但是代码最好还是要自己另外下个,因为npm装的只是src内容,全量下载里编辑器和demo都是不错的工具,可以拉下来研究下。
-
不想下有个在线的可以用用:
http://www.yanhuangxueyuan.com/threejs/editor/
- 结合ts,我们学习速度会大大提升,首先开个react的ts项目,安装three
npm i three
使用
- 按照npm 上给的第一个例子,写进react-app里看能不能运作:
import React, { useRef, useEffect } from "react";
import * as THREE from "three";
var camera: THREE.Camera, scene: THREE.Object3D, renderer: THREE.WebGLRenderer;
var geometry, material, mesh: THREE.Object3D;
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render(scene, camera);
}
function App() {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (ref.current) {
camera = new THREE.PerspectiveCamera(
70,
window.innerWidth / window.innerHeight,
0.01,
10
);
camera.position.z = 1;
scene = new THREE.Scene();
geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2);
material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
ref.current.appendChild(renderer.domElement);
}
animate();
}, []);
return <div ref={ref}></div>;
}
export default App;
- 如果你能显示一个正在旋转的彩色正方体就成功了。
- 从上面那个例子可以发现,我们搞了6个全局变量,一个相机,一个场景,一个box,一个材质,还有个mesh,然后mesh参数为box和材质,场景参数为mesh,最后通过renderer传入场景和相机渲染。
透视相机PerspectiveCamera
- 这个后面2个值不太好理解。
/**
* @param [fov=50] Camera frustum vertical field of view. Default value is 50.
* @param [aspect=1] Camera frustum aspect ratio. Default value is 1.
* @param [near=0.1] Camera frustum near plane. Default value is 0.1.
* @param [far=2000] Camera frustum far plane. Default value is 2000.
*/
第一个属性是视野角度(FOV)。视野角度就是无论在什么时候,你所能在显示器上看到的场景的范围,它的值是一个角度。
第二个值是长宽比(aspect ratio)。 也就是你用一个物体的宽除以它的高的比值。比如说,当你在一个宽屏电视上播放老电影时,可以看到图像仿佛是被压扁的。
接下来的两个值是远剪切面和近剪切面。 也就是说当物体所在的位置比摄像机的远剪切面远或者所在位置比近剪切面近的时候,该物体超出的部分将不会被渲染到场景中。
BoxGeometry
- 这个是立方体,其实还有写参数,反正只要给3个值就是长宽高就行了。
/**
* @param [width=1] — Width of the sides on the X axis.
* @param [height=1] — Height of the sides on the Y axis.
* @param [depth=1] — Depth of the sides on the Z axis.
* @param [widthSegments=1] — Number of segmented faces along the width of the sides.
* @param [heightSegments=1] — Number of segmented faces along the height of the sides.
* @param [depthSegments=1] — Number of segmented faces along the depth of the sides.
*/
MeshNormalMaterial
-
这个是一种把法向量映射到RGB颜色的材质。
mesh = new THREE.Mesh(geometry, material)
就是把材质应用到几何体上。 -
调用
scene.add(mesh)
,会使得这个几何体渲染到场景000的位置
静态例子
- 来看下一个例子,分辨设置了几种常用的几何体,以及对相机进行调整,加入测试工具,最后渲染,注释都写了。
- 阴影部分有点意思,需要地面接收阴影,指定物体创造阴影。
import React, { useRef, useEffect } from "react";
import * as THREE from "three";
var camera: THREE.Camera, scene: THREE.Object3D, renderer: THREE.WebGLRenderer;
var geometry, material, mesh: THREE.Object3D;
let gPlane;
let axes;
let mGeometry;
let gmesh;
let ball;
let mball;
let meshball;
let light;
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render(scene, camera);
}
function App() {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (ref.current) {
//场景,相机渲染器初始化
//------------
renderer = new THREE.WebGLRenderer({ antialias: true });
//设置清屏色
renderer.setClearColor(new THREE.Color(0xeeeeee));
//设置场景大小
renderer.setSize(window.innerWidth, window.innerHeight);
//开启阴影,一般不开
renderer.shadowMapEnabled = true;
scene = new THREE.Scene();
//设置测试工具
axes = new THREE.AxesHelper(255);
scene.add(axes); //测试工具加入
//设置相机
camera = new THREE.PerspectiveCamera(
50,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(-30, 40, 30);
camera.lookAt(0, 0, 0); //看向场景中央
//-----------
//设置几何体
gPlane = new THREE.PlaneGeometry(70, 50, 1, 1);
material = new THREE.MeshLambertMaterial({ color: 0xeeeeee });
mesh = new THREE.Mesh(gPlane, material);
mesh.rotation.x = -0.5 * Math.PI;
mesh.position.x = 15;
mesh.position.y = 0;
mesh.position.z = 0;
mesh.receiveShadow = true; //配合render的开启阴影进行渲染
scene.add(mesh);
//设置立方体
geometry = new THREE.BoxGeometry(4, 4, 4);
// mGeometry = new THREE.MeshBasicMaterial({
// color: 0xff0000, //basic材质不对光源有反应 lamber和phong会对光源有反应
// wireframe: true,//开启线框
// });
mGeometry = new THREE.MeshLambertMaterial({
color: 0xff0000,
});
gmesh = new THREE.Mesh(geometry, mGeometry);
gmesh.position.x = -4;
gmesh.position.y = 2;
gmesh.position.z = 0;
gmesh.castShadow = true; //配合render的开启阴影进行渲染
scene.add(gmesh);
//设置球体
ball = new THREE.SphereGeometry(4, 20, 20);
mball = new THREE.MeshPhongMaterial({
//这个phong看起来比lamber有高光
color: 0x7777ff,
});
meshball = new THREE.Mesh(ball, mball);
meshball.position.x = 25;
meshball.position.y = 10;
meshball.position.z = 10;
meshball.castShadow = true; //配合render的开启阴影进行渲染
scene.add(meshball);
//设置光源
light = new THREE.SpotLight(0xffffff);
light.position.set(-40, 60, -10);
light.castShadow = true; //配合render的开启阴影进行渲染
scene.add(light);
ref.current.appendChild(renderer.domElement);
renderer.render(scene, camera);
}
// animate();
}, []);
return <div ref={ref}></div>;
}
export default App;
- 剩下的下一篇笔记写。
版权声明:本文为yehuozhili原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。