- 创建场馆场景模型
- 首先使用blender进行模型的创建,选在添加柱体,可以选择面数,面数越多越圆滑。
接着进行调整场馆的地面的抬升
- 调整整体场馆的尺寸,使用「物体模式」,选中当前柱体,然后分别设置x、y以及z轴的长度,在此我分别设置的值为30m、30m和3m。
- 这时,场馆场景主要的柱体创建完毕,但是场馆中间应该降下去当成地面,首先选择「编辑模式」选中顶面,然后选择挤出面,可以选中面体上升或者下降,这里选择为下降,同时避免闪烁问题,删除原本的这个柱体的底面。
- 这样基本的场馆周围建立完成,没有入口,这时选择几个柱面,点击删除面即可。
- 因为删除边框,导致暴露出来的缺口两侧是中空的,这时需要选择暴露中空的框体,选择其中一边,然后进行遮掩,打到封口的效果。
- 此时场馆外围一圈创建完毕。同时在中心,再次添加柱体,作为中间支柱的底座。
- 对底部支座进行形状的调整,选中两侧拉长,对应保持两侧的距离。
- 这样展馆的中心的底座创建完毕。接着创建一个柱体,立于底座上。
- 进行拉伸后,通过选择中线,然后选择沿法线翻折,调整为如下效果,形成站台的遮光柱。
- 在「中心柱体选择」后,点击上方添加文本,进行文本的添加,因为如果选择底座的物体无法进行文本的添加,这里需要注意!
- 选择text,点击键盘「Tab」输入2023,同时进行字体选择加厚,同时调整角度,使其视角正对。
- 同时在字体下面添加底座,调整位置以及大小,并挪到文字下方。
- 在场景中再次添加立方体,作为大屏幕,展示视频使用。
- 选中地面,给地面添加纹理。
- 这样基本的大屏,以及展示文本,同时地面的地砖纹理添加完毕,进行光源的添加
场馆基本建立完毕,这时,将免费的角色导入,以及将大屏,目前展示的2023字体进行视频的播放。
- 导入角色、场景到threejs中,并添加操作
- 对角色进行操作控制,映射w,以及s的前进、后退操作,同时在停止、动作的切换,进行人物角色动作的切换,不至于让人物从静止到走动,变换的太生硬。同时增加探针,在角色前后触碰到物体,无法继续移动,防止“穿模”情况的发生,具体代码在下面展示。
import * as THREE from 'three';
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
let mixer;
let playerMixer;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 50);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera.position.set(5, 10, 25);
// const controls = new OrbitControls(camera, renderer.domElement);
scene.background = new THREE.Color(0.2, 0.2, 0.2);
const ambientLight = new THREE.AmbientLight(0xffffff, 0.1);
scene.add(ambientLight);
const directionLight = new THREE.DirectionalLight(0xffffff, 0.2);
scene.add(directionLight);
// directionLight.position.set (10, 10, 10);
directionLight.lookAt(new THREE.Vector3(0, 0, 0));
directionLight.castShadow = true;
directionLight.shadow.mapSize.width = 2048;
directionLight.shadow.mapSize.height = 2048;
const shadowDistance = 20;
directionLight.shadow.camera.near = 0.1;
directionLight.shadow.camera.far = 40;
directionLight.shadow.camera.left = -shadowDistance;
directionLight.shadow.camera.right = shadowDistance;
directionLight.shadow.camera.top = shadowDistance;
directionLight.shadow.camera.bottom = -shadowDistance;
directionLight.shadow.bias = -0.001;
// const boxGeometry = new THREE.BoxGeometry(1,1,1);
// const boxMaterial = new THREE.MeshBasicMaterial({color: 0x00ff00});
// const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial);
// scene.add(boxMesh);
// const axesHelper = new THREE.AxesHelper(10);
// scene.add(axesHelper)
let playerMesh;
let actionWalk, actionIdle;
const lookTarget = new THREE.Vector3(0, 2, 0);
new GLTFLoader().load('../resources/models/player.glb', (gltf) => {
playerMesh = gltf.scene;
scene.add(gltf.scene);
playerMesh.traverse((child)=>{
child.receiveShadow = true;
child.castShadow = true;
})
playerMesh.position.set(0, 0, 11.5);
playerMesh.rotateY(Math.PI);
playerMesh.add(camera);
camera.position.set(0, 2, -5);
camera.lookAt(lookTarget);
const pointLight = new THREE.PointLight(0xffffff, 1.5);
playerMesh.add(pointLight);
pointLight.position.set(0, 1.8, -1);
playerMixer = new THREE.AnimationMixer(gltf.scene);
const clipWalk = THREE.AnimationUtils.subclip(gltf.animations[0], 'walk', 0, 30);
actionWalk = playerMixer.clipAction(clipWalk);
// actionWalk.play();
const clipIdle = THREE.AnimationUtils.subclip(gltf.animations[0], 'idle', 31, 281);
actionIdle = playerMixer.clipAction(clipIdle);
actionIdle.play();
// const clips = gltf.animations; // 播放所有动画
// clips.forEach(function (clip) {
// const action = mixer.clipAction(clip);
// action.loop = THREE.LoopOnce;
// // 停在最后一帧
// action.clampWhenFinished = true;
// action.play();
// });
});
let isWalk = false;
const playerHalfHeight = new THREE.Vector3(0, 0.8, 0);
window.addEventListener('keydown', (e) => {
if (e.key === 'w') {
// playerMesh.translateZ(0.1);
const curPos = playerMesh.position.clone();
playerMesh.translateZ(1);
const frontPos = playerMesh.position.clone();
playerMesh.translateZ(-1);
const frontVector3 = frontPos.sub(curPos).normalize()
const raycasterFront = new THREE.Raycaster(playerMesh.position.clone().add(playerHalfHeight), frontVector3);
const collisionResultsFrontObjs = raycasterFront.intersectObjects(scene.children);
console.log(collisionResultsFrontObjs);
if (collisionResultsFrontObjs && collisionResultsFrontObjs[0] && collisionResultsFrontObjs[0].distance > 1) {
playerMesh.translateZ(0.1);
}
if (!isWalk) {
crossPlay(actionIdle, actionWalk);
isWalk = true;
}
}
if (e.key === 's') {
playerMesh.translateZ(-0.1);
}
})
window.addEventListener('keyup', (e) => {
if (e.key === 'w') {
crossPlay(actionWalk, actionIdle);
isWalk = false;
}
});
let preClientX;
window.addEventListener('mousemove', (e) => {
if (preClientX && playerMesh) {
playerMesh.rotateY(-(e.clientX - preClientX) * 0.01);
}
preClientX = e.clientX;
});
new GLTFLoader().load('../resources/models/changjing.glb', (gltf) => {
// console.log(gltf);
scene.add(gltf.scene);
gltf.scene.traverse((child) => {
console.log(child.name);
child.castShadow = true;
child.receiveShadow = true;
if (child.name === '2023') {
const video = document.createElement('video');
video.src = "./resources/yanhua.mp4";
video.muted = true;
video.autoplay = "autoplay";
video.loop = true;
video.play();
const videoTexture = new THREE.VideoTexture(video);
const videoMaterial = new THREE.MeshBasicMaterial({ map: videoTexture });
child.material = videoMaterial;
}
if (child.name === '文字001') {
const video = document.createElement('video');
video.src = "./resources/yanhua.mp4";
video.muted = true;
video.autoplay = "autoplay";
video.loop = true;
video.play();
const videoTexture = new THREE.VideoTexture(video);
const videoMaterial = new THREE.MeshBasicMaterial({ map: videoTexture });
child.material = videoMaterial;
}
if (child.name === '大屏幕01' || child.name === '大屏幕02' || child.name === '操作台屏幕' || child.name === '环形屏幕2') {
const video = document.createElement('video');
video.src = "./resources/video01.mp4";
video.muted = true;
video.autoplay = "autoplay";
video.loop = true;
video.play();
const videoTexture = new THREE.VideoTexture(video);
const videoMaterial = new THREE.MeshBasicMaterial({ map: videoTexture });
child.material = videoMaterial;
}
if (child.name === '环形屏幕') {
const video = document.createElement('video');
video.src = "./resources/video02.mp4";
video.muted = true;
video.autoplay = "autoplay";
video.loop = true;
video.play();
const videoTexture = new THREE.VideoTexture(video);
const videoMaterial = new THREE.MeshBasicMaterial({ map: videoTexture });
child.material = videoMaterial;
}
if (child.name === '柱子屏幕') {
const video = document.createElement('video');
video.src = "./resources/yanhua.mp4";
video.muted = true;
video.autoplay = "autoplay";
video.loop = true;
video.play();
const videoTexture = new THREE.VideoTexture(video);
const videoMaterial = new THREE.MeshBasicMaterial({ map: videoTexture });
child.material = videoMaterial;
}
})
mixer = new THREE.AnimationMixer(gltf.scene);
const clips = gltf.animations; // 播放所有动画
clips.forEach(function (clip) {
const action = mixer.clipAction(clip);
action.loop = THREE.LoopOnce;
// 停在最后一帧
action.clampWhenFinished = true;
action.play();
});
})
// new RGBELoader()
// .load('../resources/sky.hdr', function (texture) {
// // scene.background = texture;
// texture.mapping = THREE.EquirectangularReflectionMapping;
// scene.environment = texture;
// renderer.outputEncoding = THREE.sRGBEncoding;
// renderer.render(scene, camera);
// });
function crossPlay(curAction, newAction) {
curAction.fadeOut(0.3);
newAction.reset();
newAction.setEffectiveWeight(1);
newAction.play();
newAction.fadeIn(0.3);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
// controls.update();
// if (donuts){
// donuts.rotation.y += 0.01;
// }
if (mixer) {
mixer.update(0.02);
}
if (playerMixer) {
playerMixer.update(0.015);
}
}
animate();
最终的效果部分截图为:
最后总结一下,因为时间原因,写的比较潦草,不过在过程中了解了blender以及threejs的相关操作。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。