在线预览:https: //dragonir.github.io/3d/#/olympic |
详情步骤看Readme |
冰墩墩外面有一层透明塑料或玻璃质感外壳,这个效果可以通过修改模型的透明度、金属度、粗糙度等材质参数 |
/* eslint-disable */ |
import React from 'react' ; |
import Stats from "three/examples/jsm/libs/stats.module" ; |
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls" ; |
import { TWEEN } from "three/examples/jsm/libs/tween.module.min.js" ; |
import Animations from '../../assets/utils/animations' ; |
import landModel from './models/land.glb' ; |
import treeModel from './models/tree.gltf' ; |
import bingdwendwenModel from './models/bingdwendwen.glb' ; |
import xuerongrongModel from './models/xuerongrong.glb' ; |
import flagModel from './models/flag.glb' ; |
import skyTexture from './images/sky.jpg' ; |
import snowTexture from './images/snow.png' ; |
import treeTexture from './images/tree.png' ; |
import flagTexture from './images/flag.png' ; |
require( './libs/GLTFLoader.js' ); |
import './index.css' ; |
export default class Olympic extends React.Component { |
constructor(props) { |
super (props); |
} |
state = { |
loadingProcess: 0 |
} |
componentDidMount () { |
this .initThree() |
} |
initThree = () => { |
var container, controls, stats, mixer; |
var camera, scene, renderer, light, land = null , meshes = [], points = []; |
var fiveCyclesGroup = new THREE.Group(), clock = new THREE.Clock(); |
var mouseX = 0, mouseY = 0; |
var windowHalfX = window.innerWidth / 2; |
var windowHalfY = window.innerHeight / 2; |
var _this = this ; |
init(); |
animate(); |
function init () { |
container = document.getElementById( 'container' ); |
// 抗锯齿 |
renderer = new THREE.WebGLRenderer({ antialias: true }); |
// 设置像素比 |
renderer.setPixelRatio(window.devicePixelRatio); |
renderer.setSize(window.innerWidth, window.innerHeight); |
renderer.shadowMap.enabled = true ; |
// renderer.shadowMap.type = THREE.PCFSoftShadowMap; // // 0: THREE.BasicShadowMap, 1: THREE.PCFShadowMap, 2: THREE.PCFSoftShadowMap |
container.appendChild(renderer.domElement); |
scene = new THREE.Scene(); |
scene.background = new THREE.TextureLoader().load(skyTexture); |
scene.fog = new THREE.Fog(0xffffff, 10, 100); |
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); |
camera.position.set(0, 30, 100); |
camera.lookAt( new THREE.Vector3(0, 0, 0)); |
// 性能工具 |
// stats = new Stats(); |
// document.documentElement.appendChild(stats.dom); |
const cubeGeometry = new THREE.BoxGeometry(0.001, 0.001, 0.001); |
const cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xdc161a }); |
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial); |
cube.position.set(0, 0, 0); |
light = new THREE.DirectionalLight(0xffffff, 1); |
light.intensity = 1; |
light.position.set(16, 16, 8); |
light.castShadow = true ; |
light.target = cube; |
light.shadow.mapSize.width = 512 * 12; |
light.shadow.mapSize.height = 512 * 12; |
light.shadow.camera.top = 40; |
light.shadow.camera.bottom = -40; |
light.shadow.camera.left = -40; |
light.shadow.camera.right = 40; |
scene.add(light); |
// const lightHelper = new THREE.DirectionalLightHelper(light, 1, 'red'); |
// scene.add(lightHelper); |
// const lightCameraHelper = new THREE.CameraHelper(light.shadow.camera); |
// scene.add(lightCameraHelper); |
var ambientLight = new THREE.AmbientLight(0xcfffff); |
ambientLight.intensity = 1; |
scene.add(ambientLight); |
const manager = new THREE.LoadingManager(); |
manager.onStart = (url, loaded, total) => { }; |
manager.onLoad = () => { console.log( 'Loading complete!' ) }; |
manager.onProgress = async (url, loaded, total) => { |
if (Math.floor(loaded / total * 100) === 100) { |
_this.loadingProcessTimeout && clearTimeout(_this.loadingProcessTimeout); |
_this.loadingProcessTimeout = setTimeout(() => { |
_this.setState({ loadingProcess: Math.floor(loaded / total * 100) }); |
Animations.animateCamera(camera, controls, { x: 0, y: -1, z: 20 }, { x: 0, y: 0, z: 5 }, 3600, () => { }); |
}, 800); |
} else { |
_this.setState({ loadingProcess: Math.floor(loaded / total * 100) }); |
} |
}; |
// 添加地面 |
var loader = new THREE.GLTFLoader(manager); |
loader.load(landModel, function (mesh) { |
mesh.scene.traverse( function (child) { |
if (child.isMesh) { |
meshes.push(child) |
child.material.metalness = .1; |
child.material.roughness = .8; |
// 地面 |
if (child.name === 'Mesh_2' ) { |
child.material.metalness = .5; |
child.receiveShadow = true ; |
} |
// 围巾 |
if (child.name === 'Mesh_17' ) { |
child.material.metalness = .2; |
child.material.roughness = .8; |
} |
// 帽子 |
if (child.name === 'Mesh_17' ) { } |
} |
}); |
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); |
}); |
// 旗帜 |
loader.load(flagModel, mesh => { |
mesh.scene.traverse(child => { |
if (child.isMesh) { |
meshes.push(child) |
child.castShadow = true ; |
// 旗帜 |
if (child.name === 'mesh_0001' ) { |
child.material.metalness = .1; |
child.material.roughness = .1; |
child.material.map = new THREE.TextureLoader().load(flagTexture); |
} |
// 旗杆 |
if (child.name === '柱体' ) { |
child.material.metalness = .6; |
child.material.roughness = 0; |
child.material.refractionRatio = 1; |
child.material.color = new THREE.Color(0xeeeeee); |
} |
} |
}); |
mesh.scene.rotation.y = Math.PI / 24; |
mesh.scene.position.set(2, -7, -1); |
mesh.scene.scale.set(4, 4, 4); |
// 动画 |
let meshAnimation = mesh.animations[0]; |
mixer = new THREE.AnimationMixer(mesh.scene); |
let animationClip = meshAnimation; |
let clipAction = mixer.clipAction(animationClip).play(); |
animationClip = clipAction.getClip(); |
scene.add(mesh.scene); |
}); |
// bingdwendwen |
loader.load(bingdwendwenModel, function (mesh) { |
mesh.scene.traverse( function (child) { |
if (child.isMesh) { |
meshes.push(child) |
if (child.name === '皮肤' ) { |
child.material.metalness = .3; |
child.material.roughness = .8; |
} |
if (child.name === '外壳' ) { |
child.material.transparent = true ; |
child.material.opacity = .4; |
child.material.metalness = .4; |
child.material.roughness = 0; |
child.material.refractionRatio = 1.6; |
child.castShadow = true ; |
child.material.envMap = new THREE.TextureLoader().load(skyTexture); |
child.material.envMapIntensity = 1; |
} |
if (child.name === '围脖' ) { |
child.material.transparent = true ; |
child.material.opacity = .6; |
child.material.metalness = .4; |
child.material.roughness = .6; |
} |
} |
}); |
mesh.scene.rotation.y = Math.PI / 24; |
mesh.scene.position.set(-5, -11.5, 0); |
mesh.scene.scale.set(24, 24, 24); |
scene.add(mesh.scene); |
}); |
// xuerongrong |
loader.load(xuerongrongModel, function (mesh) { |
mesh.scene.traverse( function (child) { |
if (child.isMesh) { |
child.castShadow = true ; |
meshes.push(child) |
} |
}); |
mesh.scene.rotation.y = Math.PI / 3; |
mesh.scene.position.set(-20, -10.8, 0); |
mesh.scene.scale.set(12, 12, 12); |
scene.add(mesh.scene); |
}); |
// 添加树 |
let treeMaterial = 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 treeCustomDepthMaterial = new THREE.MeshDepthMaterial({ |
depthPacking: THREE.RGBADepthPacking, |
map: new THREE.TextureLoader().load(treeTexture), |
alphaTest: 0.5 |
}); |
loader.load(treeModel, function (mesh) { |
mesh.scene.traverse( function (child) { |
if (child.isMesh) { |
meshes.push(child) |
child.material = treeMaterial; |
child.custromMaterial = treeCustomDepthMaterial; |
} |
}); |
mesh.scene.position.set(14, -9, 0); |
mesh.scene.scale.set(16, 16, 16); |
scene.add(mesh.scene); |
let tree2 = mesh.scene.clone(); |
tree2.position.set(10, -8, -15); |
tree2.scale.set(18, 18, 18); |
scene.add(tree2) |
let tree3 = mesh.scene.clone(); |
tree3.position.set(-18, -8, -16); |
tree3.scale.set(22, 22, 22); |
scene.add(tree3) |
}); |
// 创建五环 |
const fiveCycles = [ |
{ key: 'cycle_0' , color: 0x0885c2, position: { x: -250, y: 0, z: 0 } }, |
{ key: 'cycle_1' , color: 0x000000, position: { x: -10, y: 0, z: 5 } }, |
{ key: 'cycle_2' , color: 0xed334e, position: { x: 230, y: 0, z: 0 } }, |
{ key: 'cycle_3' , color: 0xfbb132, position: { x: -125, y: -100, z: -5 } }, |
{ key: 'cycle_4' , color: 0x1c8b3c, 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), |
side: THREE.DoubleSide |
})); |
cycleMesh.castShadow = true ; |
cycleMesh.position.set(item.position.x, item.position.y, item.position.z); |
meshes.push(cycleMesh); |
fiveCyclesGroup.add(cycleMesh); |
}); |
fiveCyclesGroup.scale.set(.036, .036, .036); |
fiveCyclesGroup.position.set(0, 10, -8); |
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; |
// 水平旋转角度限制 |
controls.minAzimuthAngle = -.8; |
controls.maxAzimuthAngle = .8; |
window.addEventListener( 'resize' , onWindowResize, false ); |
} |
function onWindowResize () { |
camera.aspect = window.innerWidth / window.innerHeight; |
camera.updateProjectionMatrix(); |
renderer.setSize(window.innerWidth, window.innerHeight); |
} |
function animate () { |
requestAnimationFrame(animate); |
renderer.render(scene, camera); |
stats && stats.update(); |
controls && controls.update(); |
TWEEN && TWEEN.update(); |
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 ; |
let time = clock.getDelta(); |
mixer && mixer.update(time); |
} |
// 增加点击事件,声明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 () { |
return ( |
<div> |
<div id= "container" ></div> |
{ this .state.loadingProcess === 100 ? '' : ( |
<div className= "olympic_loading" > |
<div className= "box" >{ this .state.loadingProcess} %</div> |
</div> |
) |
} |
</div> |
) |
} |
} |
高级设计师
by: Python自学 发表于:2022-03-23 02:48:14 顶(2) | 踩(0) 回复
文件比较大 20多M
回复评论