feat: Olympic页面添加雪花效果、树、五环、冰墩墩冬奥会吉祥物.

This commit is contained in:
dragonir 2022-01-27 14:29:45 +08:00
parent 5838e715d2
commit 5b3f768eed
9 changed files with 403 additions and 16 deletions

View File

@ -1 +1,14 @@
# Three.js实现3D冬奥会创意页面
<!-- 迎冬奥向未来 -->
冰墩墩模型来源 https://www.justeasy.cn/3dmodel/OGxVUFg4Zy9UWnJYZkoxQXBwSndJZz09.html
3dx
模型转换
https://anyconv.com/tw/max-zhuan-obj/
在blender2.8中怎么给模型贴图
blender 模型贴图 https://jingyan.baidu.com/article/363872ecf6367f2f4ba16f95.html

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 886 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 408 B

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 KiB

View File

@ -1,8 +1,14 @@
/* eslint-disable */
import React from 'react';
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import Stats from "three/examples/jsm/libs/stats.module";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import landModel from './models/land.glb';
import pandaModel from './models/panda.gltf';
import bingdundunModel from './models/bingdundun.glb';
import skyTexture from './images/sky.jpg';
import snowTexture from './images/snow.png';
import treeTexture from './images/tree.png';
export default class Olympic extends React.Component {
@ -16,11 +22,12 @@ export default class Olympic extends React.Component {
initThree = () => {
var container, controls, stats;
var camera, scene, renderer, light, earth = null, meshes = [];
var camera, scene, renderer, light, land = null, meshes = [], points = [];
var fiveCyclesGroup = new THREE.Group();
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function init() {
@ -32,13 +39,13 @@ export default class Olympic extends React.Component {
container.appendChild(renderer.domElement);
scene = new THREE.Scene();
scene.background = new THREE.Color(0x2d2d2d);
scene.background = new THREE.TextureLoader().load(skyTexture);
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 10, 20);
camera.position.set(0, -2, 20);
camera.lookAt(new THREE.Vector3(0, 0, 0));
var axes = new THREE.AxisHelper(30);
scene.add(axes);
// var axes = new THREE.AxisHelper(30);
// scene.add(axes);
// 性能工具
stats = new Stats();
@ -64,6 +71,87 @@ export default class Olympic extends React.Component {
var ambientLight = new THREE.AmbientLight(0xdddddd);
scene.add(ambientLight);
// 添加地面
var loader = new GLTFLoader();
loader.load(landModel, function (mesh) {
mesh.scene.traverse(function (child) {
if (child.isMesh) {
meshes.push(child)
if (child.name === 'Mesh_2') {
child.material.metalness = .1
child.material.roughness = .8
child.material.color = new THREE.Color(0xffffff)
}
}
});
mesh.scene.rotation.y = Math.PI / 4;
mesh.scene.position.set(15, -20, 0);
mesh.scene.scale.set(.9, .9, .9);
land = mesh.scene;
scene.add(land);
});
// bingdundun
loader.load(bingdundunModel, function (mesh) {
mesh.scene.traverse(function (child) {
if (child.isMesh) {
meshes.push(child)
if (child.name === 'oldtiger001') {
child.material.metalness = .5
}
if (child.name === 'oldtiger002') {
child.material.transparent = true;
child.material.opacity = .5
}
}
});
mesh.scene.rotation.y = Math.PI / 24;
mesh.scene.position.set(-8, -10, 0);
mesh.scene.scale.set(24, 24, 24);
scene.add(mesh.scene);
});
// 添加树
let pandaMaterial = new THREE.MeshPhysicalMaterial({
map: new THREE.TextureLoader().load(treeTexture),
transparent: true,
side: THREE.DoubleSide,
metalness: .2,
roughness: .8,
depthTest: true,
depthWrite: false,
skinning: false,
fog: false,
reflectivity: 0.1,
refractionRatio: 0,
});
let pandaCustomDepthMaterial = new THREE.MeshDepthMaterial({
depthPacking: THREE.RGBADepthPacking,
map: new THREE.TextureLoader().load(treeTexture),
alphaTest: 0.5
});
loader.load(pandaModel, function (mesh) {
mesh.scene.traverse(function (child) {
if (child.isMesh) {
meshes.push(child)
child.material = pandaMaterial
child.custromMaterial = pandaCustomDepthMaterial
}
});
mesh.scene.position.set(14, -8, 0);
mesh.scene.scale.set(16, 16, 16);
scene.add(mesh.scene);
let tree2 = mesh.scene.clone();
tree2.position.set(8, -8, -14);
tree2.scale.set(18, 18, 18);
scene.add(tree2)
let tree3 = mesh.scene.clone();
tree3.position.set(-16, -8, -16);
tree3.scale.set(18, 18, 18);
scene.add(tree3)
});
// 创建五环
const fiveCycles = [
{
@ -92,7 +180,6 @@ export default class Olympic extends React.Component {
position: { x: 115, y: -100, z: 10 }
}
];
fiveCycles.map(item => {
let cycleMesh = new THREE.Mesh(new THREE.TorusGeometry(100, 10, 10, 50), new THREE.MeshLambertMaterial({
color: new THREE.Color(item.color),
@ -102,15 +189,47 @@ export default class Olympic extends React.Component {
meshes.push(cycleMesh);
fiveCyclesGroup.add(cycleMesh);
});
fiveCyclesGroup.scale.set(.025, .025, .025)
fiveCyclesGroup.position.set(0, 0, 0)
scene.add(fiveCyclesGroup)
fiveCyclesGroup.scale.set(.03, .03, .03);
fiveCyclesGroup.position.set(0, 8, -5);
scene.add(fiveCyclesGroup);
// 创建雪花
let texture = new THREE.TextureLoader().load(snowTexture);
let geometry = new THREE.Geometry();
let pointsMaterial = new THREE.PointsMaterial({
size: 1,
transparent: true,
opacity: 0.8,
map: texture,
blending: THREE.AdditiveBlending,
sizeAttenuation: true,
depthTest: false
});
let range = 100;
let vertices = []
for (let i = 0; i < 1500; i++) {
let vertice = new THREE.Vector3(Math.random() * range - range / 2, Math.random() * range * 1.5, Math.random() * range - range / 2);
// 纵向移动速度
vertice.velocityY = 0.1 + Math.random() / 3;
// 横向移动速度
vertice.velocityX = (Math.random() - 0.5) / 3;
// 将顶点加入几何
geometry.vertices.push(vertice);
}
geometry.center();
points = new THREE.Points(geometry, pointsMaterial);
points.position.y = -30;
scene.add(points);
controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
controls.enableDamping = true;
controls.enablePan = false;
controls.enableZoom = false;
// 最大仰角
controls.minPolarAngle = 1.4;
controls.maxPolarAngle = 1.8;
window.addEventListener('resize', onWindowResize, false);
// window.addEventListener('mousemove', onDocumentMouseMove, false);
}
function onWindowResize() {
@ -129,11 +248,34 @@ export default class Olympic extends React.Component {
renderer.render(scene, camera);
stats && stats.update();
controls && controls.update();
// camera.position.x += (mouseX - camera.position.x) * 0.005;
// camera.position.y += (-mouseY - camera.position.y) * 0.005;
// camera.lookAt(scene.position);
fiveCyclesGroup && (fiveCyclesGroup.rotation.y += .01)
let vertices = points.geometry.vertices;
vertices.forEach(function (v) {
v.y = v.y - (v.velocityY);
v.x = v.x - (v.velocityX);
if (v.y <= 0) v.y = 60;
if (v.x <= -20 || v.x >= 20) v.velocityX = v.velocityX * -1;
});
// 顶点变动之后需要更新,否则无法实现雨滴特效
points.geometry.verticesNeedUpdate = true;
}
// 增加点击事件声明raycaster和mouse变量
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
function onMouseClick(event) {
// 通过鼠标点击的位置计算出raycaster所需要的点的位置以屏幕中心为原点值的范围为-1到1.
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
// 通过鼠标点的位置和当前相机的矩阵计算出raycaster
raycaster.setFromCamera(mouse, camera);
// 获取raycaster直线和所有模型相交的数组集合
var intersects = raycaster.intersectObjects(meshes);
if (intersects.length > 0) {
console.log(intersects[0].object)
}
}
window.addEventListener('click', onMouseClick, false);
}
render () {

Binary file not shown.

View File

@ -0,0 +1,232 @@
{
"asset" : {
"generator" : "Khronos glTF Blender I/O v1.3.48",
"version" : "2.0"
},
"scene" : 0,
"scenes" : [
{
"name" : "Scene",
"nodes" : [
0,
1,
2,
3
]
}
],
"nodes" : [
{
"name" : "Light",
"rotation" : [
0.16907575726509094,
0.7558803558349609,
-0.27217137813568115,
0.570947527885437
],
"translation" : [
4.076245307922363,
5.903861999511719,
-1.0054539442062378
]
},
{
"name" : "Camera",
"rotation" : [
0.483536034822464,
0.33687159419059753,
-0.20870360732078552,
0.7804827094078064
],
"translation" : [
7.358891487121582,
4.958309173583984,
6.925790786743164
]
},
{
"mesh" : 0,
"name" : "\u5e73\u9762_1",
"rotation" : [
-0.5896493196487427,
0,
0.807659387588501,
7.263876966590033e-08
],
"scale" : [
0.009999999776482582,
0.009999999776482582,
0.009999999776482582
],
"translation" : [
0,
0.5100772976875305,
0
]
},
{
"mesh" : 1,
"name" : "\u5e73\u9762",
"rotation" : [
-0.9880464673042297,
0,
0.1541563719511032,
7.459543382992706e-08
],
"scale" : [
0.009999999776482582,
0.009999999776482582,
0.009999999776482582
],
"translation" : [
0,
0.5100772976875305,
0
]
}
],
"materials" : [
{
"doubleSided" : true,
"name" : "\u6750\u8d28.001",
"pbrMetallicRoughness" : {}
}
],
"meshes" : [
{
"name" : "\u7f51\u683c",
"primitives" : [
{
"attributes" : {
"POSITION" : 0,
"NORMAL" : 1,
"TEXCOORD_0" : 2
},
"indices" : 3,
"material" : 0
}
]
},
{
"name" : "\u7f51\u683c.001",
"primitives" : [
{
"attributes" : {
"POSITION" : 4,
"NORMAL" : 5,
"TEXCOORD_0" : 6
},
"indices" : 3,
"material" : 0
}
]
}
],
"accessors" : [
{
"bufferView" : 0,
"componentType" : 5126,
"count" : 4,
"max" : [
28.603322982788086,
48.00337219238281,
0
],
"min" : [
-28.603322982788086,
-48.00337219238281,
0
],
"type" : "VEC3"
},
{
"bufferView" : 1,
"componentType" : 5126,
"count" : 4,
"type" : "VEC3"
},
{
"bufferView" : 2,
"componentType" : 5126,
"count" : 4,
"type" : "VEC2"
},
{
"bufferView" : 3,
"componentType" : 5123,
"count" : 6,
"type" : "SCALAR"
},
{
"bufferView" : 4,
"componentType" : 5126,
"count" : 4,
"max" : [
28.603322982788086,
48.00337219238281,
0
],
"min" : [
-28.603322982788086,
-48.00337219238281,
0
],
"type" : "VEC3"
},
{
"bufferView" : 5,
"componentType" : 5126,
"count" : 4,
"type" : "VEC3"
},
{
"bufferView" : 6,
"componentType" : 5126,
"count" : 4,
"type" : "VEC2"
}
],
"bufferViews" : [
{
"buffer" : 0,
"byteLength" : 48,
"byteOffset" : 0
},
{
"buffer" : 0,
"byteLength" : 48,
"byteOffset" : 48
},
{
"buffer" : 0,
"byteLength" : 32,
"byteOffset" : 96
},
{
"buffer" : 0,
"byteLength" : 12,
"byteOffset" : 128
},
{
"buffer" : 0,
"byteLength" : 48,
"byteOffset" : 140
},
{
"buffer" : 0,
"byteLength" : 48,
"byteOffset" : 188
},
{
"buffer" : 0,
"byteLength" : 32,
"byteOffset" : 236
}
],
"buffers" : [
{
"byteLength" : 268,
"uri" : "data:application/octet-stream;base64,m9PkQXQDQEIAAACAm9PkQXQDQMIAAACAm9PkwXQDQMIAAACAm9PkwXQDQEIAAACAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAEAAgAAAAIAAwCb0+RBdANAQgAAAICb0+RBdANAwgAAAICb0+TBdANAwgAAAICb0+TBdANAQgAAAIAAAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPw=="
}
]
}