主要运用TubeGeometry来制作自定义的隧道效果
1.初始化场景
function init(){
//创建 WebGL renderer
var canvas = document.querySelector("canvas");
renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true,
shadowMapEnabled: true,
shadowMapType: THREE.PCFSoftShadowMap });
renderer.setSize(ww, wh);
//创建场景 添加雾效果,是场景朦胧些
scene = new THREE.Scene();
scene.fog = new THREE.Fog(0x194794, 0, 100);
var clock = new THREE.Clock();
//创建透视类型的相机
cameraRotationProxyX = 3.14159;
cameraRotationProxyY = 0;
camera = new THREE.PerspectiveCamera(45, ww / wh, 0.001, 200);
camera.rotation.y = cameraRotationProxyX;
camera.rotation.z = cameraRotationProxyY;
group = new THREE.Group();
group.position.z = 400;
group.add(camera);
scene.add(group);
//创建点光源
light = new THREE.PointLight(0xffffff, .35, 4, 0);
light.castShadow = true;
scene.add(light);
}
2.创建星空管道
用连续的空间点创建自定义曲线,然后根据曲线来创建管道 再添加贴图
function createSkyTube(){
//Array of points
var points = [
[10, 89, 0],[50, 88, 10],[76, 139, 20],
[126, 141, 12],[150, 112, 8],[157, 73, 0],
[180, 44, 5],[207, 35, 10],[232, 36, 0]];
var p1, p2;
//points转化为 vertices
for (var i = 0; i < points.length; i++) {
var x = points[i][0];
var y = points[i][2];
var z = points[i][1];
points[i] = new THREE.Vector3(x, y, z);
}
//根据路径点创建自定义曲线
path = new THREE.CatmullRomCurve3(points);
path.tension = .5;
//创建TubeGeometry
var geometry = new THREE.TubeGeometry(path, 300, 4, 32, false);
//贴图
var texture = new THREE.TextureLoader().load('res/3d_space_5.jpg', function (texture) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.offset.set(0, 0);
texture.repeat.set(15, 2);
});
//凹凸贴图,影响光照感知深度
var mapHeight = new THREE.TextureLoader().load('res/waveform-bump3.jpg', function (texture) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.offset.set(0, 0);
texture.repeat.set(15, 2);
});
//创建MeshPhong镜面高光材质
var material = new THREE.MeshPhongMaterial({
side: THREE.BackSide,
map: texture,
shininess: 20,
bumpMap: mapHeight,
bumpScale: -.03,
specular: 0x0b2349 }
);
//Create a mesh
var tube = new THREE.Mesh(geometry, material);
scene.add(tube);
}
此时效果如下,效果很干不够立体,然后我们加上辅助线效果
3.添加立体辅助线
思路是创建比星空管道半径更小的TubeGeometry,然后用EdgesGeometry来创建
//再创建半径略小的管道,添加几何体辅助线
function createTubeEdges(){
var geometry = new THREE.TubeGeometry(path, 150, 3.4, 32, false);
//创建边缘几何体
var geo = new THREE.EdgesGeometry(geometry);
var mat = new THREE.LineBasicMaterial({
linewidth: 2,
opacity: .2,
transparent: 1 }
);
//创建线段
var wireframe = new THREE.LineSegments(geo, mat);
scene.add(wireframe);
}
此时效果如图,显得更加立体了 但是光线还是太暗,我们再用后期效果处理一下
4.添加后期泛光通道
UnrealBloonPass是three.js后期处理的扩展库,可以用来模拟生活中的泛光或眩光效果,用在星空效果里恰到好处 用法如下
function initRenderPass(){
var params = {
exposure: 1.3,
bloomStrength: .9,
bloomThreshold: 0,
bloomRadius: 0
};
//set up render pass
var renderScene = new THREE.RenderPass(scene, camera);
//创建泛光通道
var bloomPass = new THREE.UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);
bloomPass.renderToScreen = true;
bloomPass.threshold = params.bloomThreshold; //阈值
bloomPass.strength = params.bloomStrength; //强度
bloomPass.radius = params.bloomRadius; //半径
composer = new THREE.EffectComposer(renderer);
composer.setSize(window.innerWidth, window.innerHeight);
composer.addPass(renderScene);
composer.addPass(bloomPass);
}
此时效果图:
5.加上粒子效果来点缀
这里以创建一个为例,项目中创建了三个来交叉运动,加上之后就有了文章开头的效果
var particleSystem1,particleSystem2,particleSystem3;
function createParticles(){
var lastPlace = 0;
var newPlace = 0;
//粒子贴图
var spikeyTexture = new THREE.TextureLoader().load('res/spikey.png');
var particleCount = 6800;
particles1 = new THREE.Geometry();
//粒子材质
pMaterial = new THREE.ParticleBasicMaterial({
color: 0xFFFFFF,
size: .5,
map: spikeyTexture,
transparent: true,
blending: THREE.AdditiveBlending
});
for (var p = 0; p < particleCount; p++) {
// 随机创建粒子
// 随机值 -250 -> 250
var pX = Math.random() * 500 - 250,
pY = Math.random() * 50 - 25,
pZ = Math.random() * 500 - 250,
particle = new THREE.Vector3(pX, pY, pZ);
particles1.vertices.push(particle);
}
// create the particle system
particleSystem1 = new THREE.ParticleSystem(particles1,pMaterial);
// add it to the scene
scene.add(particleSystem1);
}
6. render实时变化,让粒子动起来
function render() {
//...
particleSystem1.rotation.y += 0.00002;
particleSystem2.rotation.x += 0.00005;
particleSystem3.rotation.z += 0.00001;
//...
requestAnimationFrame(render);
}
requestAnimationFrame(render);
7. 关联鼠标滚动,让摄像机跟随
用gsap的ScrollTrigger插件来关联滚动,用法如下:
//初始化gsap鼠标滚动
function initAction(){
gsap.defaultEase = Linear.easeNone;
var tubePerc = {percent: 0 };
//注册ScrollTrigger
gsap.registerPlugin(ScrollTrigger);
var tl = gsap.timeline({
scrollTrigger: {
trigger: ".scrollTarget",
start: "top top",
end: "bottom 100%",
scrub: 5,
markers: { color: "white" } }
});
tl.to(tubePerc, {
percent: .96,
ease: Linear.easeNone,
duration: 10,
onUpdate: function () {
cameraTargetPercentage = tubePerc.percent;
}
});
}
这里根据onupdate的cameraTargetPercentage值可以去更新相机的位置
8.更新相机的位置
function updateCameraPercentage(percentage) {
//camera位于p1点看向p2点
p1 = path.getPointAt(percentage);
p2 = path.getPointAt(percentage + 0.03);
group.position.set(p1.x, p1.y, p1.z);
group.lookAt(p2);
light.position.set(p2.x, p2.y, p2.z);
}