一、Basic
- dat.GUI的使用
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test - dat.gui</title>
<link rel="stylesheet" href="../node_modules/normalize.css/normalize.css">
<link rel="stylesheet" href="../style/global.css">
</head>
<body>
<div class="three-dom" id="threeDom"></div>
<script type="importmap">
{
"imports": {
"three": "../node_modules/three/build/three.module.js",
"three/addons/": "../node_modules/three/examples/jsm/",
"datGui":"../node_modules/dat.gui/build/dat.gui.module.js"
}
}
</script>
<script type="module">
import {
AxesHelper,
Color,
FogExp2, Mesh,
MeshBasicMaterial,
PerspectiveCamera,
PlaneGeometry,
Scene, SphereGeometry,
WebGLRenderer
} from 'three'
import {OrbitControls} from 'three/addons/controls/OrbitControls.js'
import datGui from 'datGui'
function init(threeDom) {
const scene = new Scene()
scene.background = new Color(0x333333)
scene.fog = new FogExp2(0xcccccc, 0.002)
const camera = new PerspectiveCamera(120, threeDom.clientWidth / threeDom.clientHeight, 0.1, 1000)
camera.position.set(-30, 40, 30)
camera.lookAt(scene.position)
const renderer = new WebGLRenderer()
renderer.setSize(threeDom.clientWidth, threeDom.clientHeight)
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setClearColor(new Color(0xdddddd, 1))
threeDom.appendChild(renderer.domElement)
const planeGeometry = new PlaneGeometry(60, 40, 1, 1)
const planeMaterial = new MeshBasicMaterial({
wireframe: false,
color: 0xcccccc
})
const plane = new Mesh(planeGeometry, planeMaterial)
plane.position.set(10, 0, 0)
plane.rotation.x = -0.5 * Math.PI
scene.add(plane)
const orbitControls = new OrbitControls(camera, renderer.domElement)
orbitControls.listenToKeyEvents(window)
orbitControls.enableDamping = true
orbitControls.dampingFactor = 0.05
orbitControls.screenSpacePanning = false
orbitControls.minDistance = 1
orbitControls.maxDistance = 50
orbitControls.maxPolarAngle = Math.PI / 2
const axes = new AxesHelper(10)
scene.add(axes)
return [scene, camera, renderer, orbitControls, planeGeometry]
}
window.onload = function () {
const threeDom = document.getElementById('threeDom')
const [scene, camera, renderer, orbitControls, planeGeometry] = init(threeDom)
window.addEventListener('resize', () => {
camera.aspect = threeDom.clientWidth / threeDom.clientHeight
camera.updateProjectionMatrix()
renderer.setSize(threeDom.clientWidth, threeDom.clientHeight)
})
function render() {
orbitControls.update()
renderer.render(scene, camera)
requestAnimationFrame(render)
}
const controls = new function () {
this.numberOfObjects = scene.children.length
this.addCube = function () {
const sphereSize = Math.ceil((Math.random() * 3))
const sphereGeometry = new SphereGeometry(sphereSize)
const sphereMaterial = new MeshBasicMaterial({
wireframe: true,
color:Math.random()* 0xffffff
})
const sphere = new Mesh(sphereGeometry, sphereMaterial)
sphere.position.x = -30 + Math.round((Math.random() * planeGeometry.parameters.width));
sphere.position.y = Math.round((Math.random() * 5));
sphere.position.z = -20 + Math.round((Math.random() * planeGeometry.parameters.height));
sphere.name = 'sphere-' + scene.children.length
scene.add(sphere)
this.numberOfObjects = scene.children.length;
}
this.removeCube = function () {
const allChildren = scene.children
const lastObject = allChildren[allChildren.length - 1]
if (lastObject instanceof Mesh) {
scene.remove(lastObject)
this.numberOfObjects = scene.children.length
}
}
this.outputObjects = function () {
console.log(scene.children);
}
}
const gui = new datGui.GUI()
gui.add(controls, 'addCube')
gui.add(controls, 'removeCube');
gui.add(controls, 'outputObjects');
gui.add(controls, 'numberOfObjects').listen();
render()
}
</script>
</body>
</html>
- 全屏显示
window.addEventListener("dblclick", () => {
console.log('dbclick')
const fullScreen = document.fullscreenElement
if (fullScreen) {
document.exitFullscreen()
} else {
renderer.domElement.requestFullscreen()
}
})
- 自适应全屏
window.addEventListener('resize', () => {
camera.aspect = threeDom.clientWidth / threeDom.clientHeight
camera.updateProjectionMatrix()
renderer.setSize(threeDom.clientWidth, threeDom.clientHeight)
})
- 使用多种材质创建几何体
SceneUtils 是Scene对象的一个工具类 位于three/examples/jsm/utils/SceneUtils.js中 SceneUtils.createMultiMaterialObject(cubeGeometry, materials) 方法返回为materials数组中指定的材质创建一个实例,并把实例保存在组里。可以像使用Mesh对象那样使用组。
<script type="importmap">
{
"imports": {
"three": "../node_modules/three/build/three.module.js",
"three/addons/": "../node_modules/three/examples/jsm/",
"datGui":"../node_modules/dat.gui/build/dat.gui.module.js",
"SceneUtils":"../node_modules/three/examples/jsm/utils/SceneUtils.js"
}
}
</script>
<script type="module">
import * as SceneUtils from 'SceneUtils'
import { Mesh, MeshBasicMaterial, BoxGeometry, MeshLambertMaterial } from 'three'
const controls = new function () {
this.addComplexCube = function () {
const cubeGeometry = new BoxGeometry(6, 6, 6)
const materials = [
new MeshBasicMaterial({color: 0x000000, wireframe: true}),
new MeshLambertMaterial({opacity: 0.6, color: 0x44ff55, transparent: true})
]
const cube = new SceneUtils.createMultiMaterialObject(cubeGeometry, materials)
scene.add(cube)
}
}
</script>
- clone函数
this.clone = function () {
const geom = new BoxGeometry(2, 2, 2)
const materials = [
new MeshBasicMaterial({color: 0x000000, wireframe: true}),
new MeshLambertMaterial({opacity: 0.6, color: 0x44ff44, transparent: true})
]
const mesh = SceneUtils.createMultiMaterialObject(geom, materials)
mesh.name = 'mesh'
scene.add(mesh)
this.numberOfObjects += 1
// 复制几何体网格
console.log(mesh.children[0])
console.log(mesh instanceof Group) // true
const cloneGeometry = mesh.children[0].geometry.clone()
const materialsOther = [
new MeshLambertMaterial({opacity: 0.8, color: 0xff44ff, transparent: true}),
new MeshBasicMaterial({color: 0x000000, wireframe: true})
]
const meshOther = SceneUtils.createMultiMaterialObject(cloneGeometry, materialsOther)
meshOther.translateX(5)
meshOther.translateZ(5)
meshOther.name = 'cloneBox'
setTimeout(() => {
scene.remove(scene.getObjectByName("mesh"))
this.numberOfObjects -= 1
}, 1000)
scene.add(meshOther)
this.numberOfObjects += 1
}
PerspectiveCamera( fov?: number, aspect?: number, near?: number, far?: number)函数
fov:表示视场。这是在摄像机中能看到的那部分场景。 aspect:渲染结果的横向尺寸和纵向尺寸的比值。 near:定义了从相机多进的距离开始渲染。 far:定义了摄像机从它所处的位置能够看多远。 zoom:属性可以放大和缩小场景。
OrthographicCamera.constructor( left?: number, right?: number, top?: number, bottom?: number, near?: number, far?: number)正交投影相机
left:可是范围的左平面 right:可是范围的右平面 top:可是范围的上平面 bottom:可是范围的下平面 near:基于摄像机所处的位置,从这一点开始渲染场景 far:基于摄像机所有的位置,渲染到场景到这一点为止
- 将摄像机聚焦在指定点
通常摄像机会指向场景中心position(0,0,0);我们可以改变摄像机指向的位置 camera.lookAt(new Vector(x,y,z))
二、Light
- Threejs中的光源的分类
所有光源的基类都是Light对象的扩展。分为两类: 一、简单光源; AmbientLight、PointLight、SpotLight、DirectionalLight 二、特殊用途的光源和效果 HemisphereLight、AreaLight、LensFlare。
- AmbientLight
AmbientLight光源的特性:会应用到全局、该光源没有特别的来源方向、不会产生阴影。 用法:不能将AmbientLight作为唯一的光源;在使用其他光源时使用它。
使用AmbientLight 和SpotLight小案例
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>chapter three</title>
<link rel="stylesheet" href="../node_modules/normalize.css/normalize.css">
<link rel="stylesheet" href="../style/global.css">
</head>
<body>
<div class="three-dom" id="threeDom"></div>
<script type="importmap">
{
"imports": {
"three": "../node_modules/three/build/three.module.js",
"three/addons/": "../node_modules/three/examples/jsm/",
"datGui":"../node_modules/dat.gui/build/dat.gui.module.js",
"SceneUtils":"../node_modules/three/examples/jsm/utils/SceneUtils.js",
"utils":"../utils/index.js",
"stats":"../node_modules/three/examples/jsm/libs/stats.module.js"
}
}
</script>
<script type="module">
import {initRenderer, initCamera, initStats,initOrbitControls} from 'utils'
import {
AmbientLight,
Color,
Mesh,
MeshBasicMaterial,
Scene,
SpotLight,
PlaneGeometry,
MeshLambertMaterial, SphereGeometry, BoxGeometry
} from "three";
import datGui from 'datGui'
const init = (threeDom) => {
const stats = initStats(undefined, threeDom)
const renderer = initRenderer(undefined, threeDom)
const camera = initCamera(undefined, threeDom)
const scene = new Scene()
const orbicontrols = initOrbitControls(camera,renderer)
const ambientLight = new AmbientLight('#606008', 1)
scene.add(ambientLight)
const spotLight = new SpotLight(0xffffff, 1, 180, Math.PI / 4);
spotLight.shadow.mapSize.set(2048, 2048);
spotLight.position.set(-30, 40, -10);
spotLight.castShadow = true;
scene.add(spotLight);
initObject(scene)
setUpControls(ambientLight, spotLight)
return {
stats,
renderer,
camera,
scene,
orbicontrols
}
}
function setUpControls(ambientLight, spotLight) {
const controls = new function () {
this.intensity = ambientLight.intensity
this.ambientColor = ambientLight.color.getStyle()
this.diabledSpotLight = false
}
const gui = new datGui.GUI()
gui.add(controls, 'intensity', 0, 3, 0.1).onChange(function (e) {
ambientLight.color = new Color(e)
ambientLight.intensity = controls.intensity
})
gui.add(controls, 'ambientColor').onChange(function (e) {
ambientLight.color = new Color(controls.ambientColor)
ambientLight.intensity = controls.intensity
})
gui.add(controls, 'diabledSpotLight').onChange(function (e) {
spotLight.visible = !e
})
return controls
}
function initObject(scene) {
const planeGeometry = new PlaneGeometry(60, 20, 1, 1)
const planeMaterial = new MeshBasicMaterial({
color: 0xaaaaaa
})
const plane = new Mesh(planeGeometry, planeMaterial)
plane.position.set(15, 0, 0)
plane.rotation.x = -0.5 * Math.PI
scene.add(plane)
const sphereGeometry = new SphereGeometry(4,20,20)
const sphereMaterial = new MeshLambertMaterial({
color:0x7777ffff,
wireframe:true
})
const sphere = new Mesh(sphereGeometry,sphereMaterial)
sphere.position.set(20,4,2)
scene.add(sphere)
const boxGeometry = new BoxGeometry(4,4,4)
const boxMaterial = new MeshLambertMaterial({
color:0xff0000,
wireframe: true
})
const box = new Mesh(boxGeometry,boxMaterial)
box.position.set(-4,3,0)
scene.add(box)
}
window.onload = function () {
const threeDom = document.getElementById('threeDom')
const {stats, renderer, camera, scene,orbicontrols} = init(threeDom)
window.addEventListener('resize', () => {
camera.aspect = threeDom.clientWidth / threeDom.clientHeight
camera.updateProjectionMatrix()
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(threeDom.clientWidth, threeDom.clientHeight)
})
function render() {
stats.update()
orbicontrols.update()
requestAnimationFrame(render)
renderer.render(scene, camera)
}
render()
}
</script>
</body>
</html>
utils.js文件内容
import {PerspectiveCamera, WebGLRenderer, PCFSoftShadowMap, Color, Vector3} from 'three'
import Stats from 'stats'
import {OrbitControls} from 'three/addons/controls/OrbitControls.js'
function initStats(type, threeDom) {
const panelType = (typeof type !== 'undefined' && type) && (!isNaN(type)) ? parseInt(type) : 0
const stats = new Stats()
stats.showPanel(panelType)
threeDom.appendChild(stats.dom)
return stats
}
function initRenderer(additionalProperties, threeDom) {
const props = (typeof additionalProperties !== 'undefined' && additionalProperties) ? additionalProperties : {};
const renderer = new WebGLRenderer(props);
renderer.shadowMap.enabled = true;
renderer.shadowMapSoft = true;
renderer.shadowMap.type = PCFSoftShadowMap;
renderer.setClearColor(new Color(0x000000));
renderer.setSize(threeDom.clientWidth, threeDom.clientHeight);
renderer.shadowMap.enabled = true;
threeDom.appendChild(renderer.domElement);
return renderer;
}
function initCamera(initialPosition, threeDom) {
const position = (initialPosition !== undefined) ? initialPosition : new Vector3(-30, 40, 30);
const camera = new PerspectiveCamera(45, threeDom.clientWidth / threeDom.clientHeight, 0.1, 1000);
camera.position.copy(position);
camera.lookAt(new Vector3(0, 0, 0));
return camera;
}
function initOrbitControls (camera,renderer){
const orbitControls = new OrbitControls(camera, renderer.domElement)
orbitControls.listenToKeyEvents(window)
orbitControls.enableDamping = true
orbitControls.dampingFactor = 0.05
orbitControls.screenSpacePanning = false
orbitControls.minDistance = 1
orbitControls.maxDistance = 50
orbitControls.maxPolarAngle = Math.PI / 2
return orbitControls
}
export {
initStats,
initCamera,
initRenderer,
initOrbitControls
}