1. three.js 拾取描边,热点轮廓,模型轮廓,热点交互
2. 案例地址
我自己简化的案例地址
官方地址案例地址
3. 全部代码
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - post processing - Outline Pass</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="../../three.js-r115/examples/main.css">
</head>
<body>
</body>
<script type="module">
import * as THREE from '../../three.js-r115/build/three.module.js';
import Stats from '../../three.js-r115/examples/jsm/libs/stats.module.js';
import {GUI} from '../../three.js-r115/examples/jsm/libs/dat.gui.module.js';
import {OrbitControls} from '../../three.js-r115/examples/jsm/controls/OrbitControls.js';
import {OBJLoader} from '../../three.js-r115/examples/jsm/loaders/OBJLoader.js';
import {EffectComposer} from '../../three.js-r115/examples/jsm/postprocessing/EffectComposer.js';
import {RenderPass} from '../../three.js-r115/examples/jsm/postprocessing/RenderPass.js';
import {OutlinePass} from '../../three.js-r115/examples/jsm/postprocessing/OutlinePass.js';
let container, stats;
let camera, scene, renderer, controls;
let raycaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
let selectedObjects = [];
let composer, effectFXAA, outlinePass;
let obj3d = new THREE.Object3D();
let group = new THREE.Group();
let params = {
edgeStrength: 3.0,
edgeGlow: 0.0,
edgeThickness: 1.0,
pulsePeriod: 0,
rotate: false,
usePatternTexture: false
};
function initBase() {
container = document.createElement('div');
document.body.appendChild(container);
let width = window.innerWidth;
let height = window.innerHeight;
renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100);
camera.position.set(0, 0, 8);
}
function initControls() {
controls = new OrbitControls(camera, renderer.domElement);
controls.minDistance = 5;
controls.maxDistance = 20;
controls.enablePan = false;
controls.enableDamping = true;
controls.dampingFactor = 0.05;
}
function initLight() {
scene.add(new THREE.AmbientLight(0xaaaaaa, 0.2));
let light = new THREE.DirectionalLight(0xddffdd, 0.6);
light.position.set(1, 1, 1);
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
let d = 10;
light.shadow.camera.left = -d;
light.shadow.camera.right = d;
light.shadow.camera.top = d;
light.shadow.camera.bottom = -d;
light.shadow.camera.far = 1000;
scene.add(light);
}
function initModel() {
let manager = new THREE.LoadingManager();
manager.onProgress = function (item, loaded, total) {
console.log(item, loaded, total);
};
let loader = new OBJLoader(manager);
loader.load('../../three.js-r115/examples/models/obj/tree.obj', function (object) {
let scale = 1.0;
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.geometry.center();
child.geometry.computeBoundingSphere();
scale = 0.2 * child.geometry.boundingSphere.radius;
let phongMaterial = new THREE.MeshPhongMaterial({
color: 0xffffff,
specular: 0x111111,
shininess: 5
});
child.material = phongMaterial;
child.receiveShadow = true;
child.castShadow = true;
}
});
object.position.y = 1;
object.scale.divideScalar(scale);
obj3d.add(object);
});
let geometry = new THREE.SphereBufferGeometry(3, 48, 24);
for (let i = 0; i < 20; i++) {
let material = new THREE.MeshLambertMaterial();
material.color.setHSL(Math.random(), 1.0, 0.3);
let mesh = new THREE.Mesh(geometry, material);
mesh.position.x = Math.random() * 4 - 2;
mesh.position.y = Math.random() * 4 - 2;
mesh.position.z = Math.random() * 4 - 2;
mesh.receiveShadow = true;
mesh.castShadow = true;
mesh.scale.multiplyScalar(Math.random() * 0.3 + 0.1);
group.add(mesh);
}
let floorMaterial = new THREE.MeshLambertMaterial({side: THREE.DoubleSide});
let floorGeometry = new THREE.PlaneBufferGeometry(12, 12);
let floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
floorMesh.rotation.x -= Math.PI * 0.5;
floorMesh.position.y -= 1.5;
group.add(floorMesh);
floorMesh.receiveShadow = true;
let geometry2 = new THREE.TorusBufferGeometry(1, 0.3, 16, 100);
let material = new THREE.MeshPhongMaterial({color: 0xffaaff});
let torus = new THREE.Mesh(geometry2, material);
torus.position.z = -4;
group.add(torus);
torus.receiveShadow = true;
torus.castShadow = true;
scene.add(group);
group.add(obj3d);
console.log('scene: ', scene);
{
let geometry3 = new THREE.BoxBufferGeometry(1, 1, 1);
let material3 = new THREE.MeshBasicMaterial({color: 0x00ff00});
let cube = new THREE.Mesh(geometry3, material3);
cube.name = 'cube'
scene.add(cube);
}
}
function initHelper() {
stats = new Stats();
container.appendChild(stats.dom);
function gui() {
let gui = new GUI({width: 300});
gui.add(params, 'edgeStrength', 0.01, 10).onChange(function (value) {
outlinePass.edgeStrength = Number(value);
});
gui.add(params, 'edgeGlow', 0.0, 1).onChange(function (value) {
outlinePass.edgeGlow = Number(value);
});
gui.add(params, 'edgeThickness', 1, 4).onChange(function (value) {
outlinePass.edgeThickness = Number(value);
});
gui.add(params, 'pulsePeriod', 0.0, 5).onChange(function (value) {
outlinePass.pulsePeriod = Number(value);
});
gui.add(params, 'rotate');
gui.add(params, 'usePatternTexture').onChange(function (value) {
outlinePass.usePatternTexture = value;
});
let Configuration = function () {
this.visibleEdgeColor = '#ffffff';
this.hiddenEdgeColor = '#190a05';
};
let conf = new Configuration();
gui.addColor(conf, 'visibleEdgeColor').onChange(function (value) {
outlinePass.visibleEdgeColor.set(value);
});
gui.addColor(conf, 'hiddenEdgeColor').onChange(function (value) {
outlinePass.hiddenEdgeColor.set(value);
});
}
gui();
}
function initPostprocessing() {
composer = new EffectComposer(renderer);
let renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera);
composer.addPass(outlinePass);
let onLoad = function (texture) {
outlinePass.patternTexture = texture;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
};
let loader = new THREE.TextureLoader();
loader.load('../../three.js-r115/examples/textures/tri_pattern.jpg', onLoad);
window.addEventListener('mousemove', onTouchMove);
window.addEventListener('touchmove', onTouchMove);
function onTouchMove(event) {
let x, y;
if (event.changedTouches) {
x = event.changedTouches[0].pageX;
y = event.changedTouches[0].pageY;
} else {
x = event.clientX;
y = event.clientY;
}
mouse.x = (x / window.innerWidth) * 2 - 1;
mouse.y = -(y / window.innerHeight) * 2 + 1;
checkIntersection();
}
function addSelectedObject(object) {
selectedObjects = [];
selectedObjects.push(object);
}
function checkIntersection() {
raycaster.setFromCamera(mouse, camera);
let intersects = raycaster.intersectObjects([scene], true);
if (intersects.length > 0) {
let selectedObject = intersects[0].object;
addSelectedObject(selectedObject);
outlinePass.selectedObjects = selectedObjects;
} else {
outlinePass.selectedObjects = [];
}
}
}
function onWindowResize() {
let width = window.innerWidth;
let height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
composer.setSize(width, height);
}
function render() {
let timer = performance.now();
if (params.rotate) {
group.rotation.y = timer * 0.0001;
}
controls.update();
composer.render();
}
function animate() {
requestAnimationFrame(animate);
render()
stats.update();
}
(function () {
initBase();
initControls();
initPostprocessing();
initLight();
initModel();
initHelper()
window.addEventListener('resize', onWindowResize, false);
animate();
})()
</script>
</html>