ThreeJS-圣诞节表白3D贺卡(三十)
素材分享:
链接: https://pan.baidu.com/s/1l0mZWfkiLaXJfdvZ7XoY8w
提取码: i69h
提前预知:
向下滚动鼠标滑轮切换视角
关键代码:
//初始化渲染器
const render = new THREE.WebGLRenderer({
//设置抗锯齿,防失真
antialis: true,
//对数深度缓冲区,防止模型闪烁
logarithmicdepthbuffer: true,
});
/*设置场景渲染编码threejs将贴图的编码都默认设置为THREE.LinearEncoding,
*导致图片色彩失真(色彩不像正常那么鲜艳,会灰蒙蒙的),所以务必将场景中的所有贴图的编码都调整为THREE.sRGBEncoding
*/
render.outputEncoding = THREE.sRGBEncoding;
//设置曝光类型(电影类型、文本类型、游戏类型),电影类型
render.toneMapping = THREE.ACESFilmicToneMapping;
//曝光强度
render.toneMappingExposure = 0.5;
//开启物理灯光
render.physicallyCorrectLights = true;
//创建轨道控制器,可以拖动,控制的是摄像头
const controls = new OrbitControls(camera, render.domElement);
//控制器焦点,需要随着相机的更新而更新
controls.target.set(-8, 2, 0);
//设置控制阻尼,让控制器有更真实的效果
controls.enableDamping = true;
完整代码:
<template>
<div id="three_div"></div>
</template>
<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
import { Water } from "three/examples/jsm/objects/Water2";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import gsap from "gsap";
export default {
name: "HOME",
components: {
// vueQr,
// glHome,
},
data() {
return {};
},
mounted() {
//使用控制器控制3D拖动旋转OrbitControls
//控制3D物体移动
//1.创建场景
const scene = new THREE.Scene();
//2.创建相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
20000
);
//设置相机位置
camera.position.set(0, 0, 10);
//将相机添加到场景
scene.add(camera);
//初始化渲染器
const render = new THREE.WebGLRenderer({
//设置抗锯齿,防失真
antialis: true,
//对数深度缓冲区,防止模型闪烁
logarithmicdepthbuffer: true,
});
/*设置场景渲染编码threejs将贴图的编码都默认设置为THREE.LinearEncoding,
*导致图片色彩失真(色彩不像正常那么鲜艳,会灰蒙蒙的),所以务必将场景中的所有贴图的编码都调整为THREE.sRGBEncoding
*/
render.outputEncoding = THREE.sRGBEncoding;
//设置渲染器的尺寸
render.setSize(window.innerWidth, window.innerHeight);
//清除默认设置颜色
render.setClearColor("#000");
//设置曝光类型(电影类型、文本类型、游戏类型),电影类型
render.toneMapping = THREE.ACESFilmicToneMapping;
//曝光强度
render.toneMappingExposure = 0.5;
//开启物理灯光
render.physicallyCorrectLights = true;
//创建轨道控制器,可以拖动,控制的是摄像头
const controls = new OrbitControls(camera, render.domElement);
//控制器焦点,需要随着相机的更新而更新
controls.target.set(-8, 2, 0);
//设置控制阻尼,让控制器有更真实的效果
controls.enableDamping = true;
//将webgl渲染的canvas内容添加到body上
document.getElementById("three_div").appendChild(render.domElement);
//渲染下一帧的时候就会调用回调函数
let renderFun = () => {
//更新阻尼数据
controls.update();
//需要重新绘制canvas画布
render.render(scene, camera);
//监听屏幕刷新(60HZ,120HZ),每次刷新触发一次requestAnimationFrame回调函数
//但是requestAnimationFrame的回调函数注册生命只有一次,因此需要循环注册,才能达到一直调用的效果
window.requestAnimationFrame(renderFun);
};
// window.requestAnimationFrame(renderFun);
renderFun();
//添加灯光
const light = new THREE.DirectionalLight(0xffffff, 1);
scene.add(light);
//加载模型
const sceneLoader = new GLTFLoader().setPath("three/glb/");
//创建解码器
const dencoderLoader = new DRACOLoader().setDecoderPath(
"three/draco/gltf/"
);
sceneLoader.setDRACOLoader(dencoderLoader);
sceneLoader.load("christmas .glb", (loader) => {
console.log(loader);
const model = loader.scene;
model.traverse((child) => {
//隐藏模型水面
if (child.name == "Plane") {
child.visible = false;
}
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
scene.add(model);
});
//加入自己的水面
const waterPlane = new THREE.CircleBufferGeometry(400, 100);
const textureLoader = new THREE.TextureLoader();
const waterMesh = new Water(waterPlane, {
textureWidth: 1024,
textureHeight: 1024,
color: 0xeeeeff,
flowDirection: new THREE.Vector2(1, 1),
scale: 100,
flowMap: textureLoader.load("three/textures/water/Water_1_M_Flow.webp"),
normalMap1: textureLoader.load(
"three/textures/water/Water_1_M_Normal.webp"
),
normalMap2: textureLoader.load(
"three/textures/water/Water_2_M_Normal.webp"
),
});
waterMesh.rotation.x = -Math.PI / 2;
waterMesh.position.y = -0.7;
scene.add(waterMesh);
//添加场景背景
const rgbeLoader = new RGBELoader();
rgbeLoader.loadAsync("three/christmas-sky.hdr").then((load) => {
//将用于等距圆柱投影的环境贴图,也被叫做经纬线映射贴图。
//等距圆柱投影贴图表示沿着其水平中线360°的视角,以及沿着其垂直轴向180°的视角。
//贴图顶部和底部的边缘分别对应于它所映射的球体的北极和南极。
load.mapping = THREE.EquirectangularReflectionMapping;
scene.background = load;
scene.environment = load;
});
//屋子创建灯光
const hotelLight = new THREE.PointLight(0xffffff, 1);
hotelLight.position.set(0, 2.4, 0);
scene.add(hotelLight);
render.shadowMap.enabled = true;
//开启灯光动态投影
hotelLight.castShadow = true;
//创建灯光组
const lightGroup = new THREE.Group();
lightGroup.position.set(-8, 2.5, -1.5);
for (let index = 0; index < 3; index++) {
const geometry = new THREE.SphereBufferGeometry(0.2, 50, 5);
const material = new THREE.MeshBasicMaterial({
color: 0xffffff,
emissive: 0xffffff,
emissiveintensity: 10,
});
const mesh = new THREE.Mesh(geometry, material);
const light = new THREE.PointLight(0xffff, 1);
mesh.add(light);
mesh.position.set(
Math.cos(Math.random()) * 3,
Math.cos(Math.random()) * 1,
Math.sin(Math.random()) * 3
);
lightGroup.add(mesh);
}
console.log("组", lightGroup);
scene.add(lightGroup);
let options = {
angle: 0,
};
gsap.to(options, {
angle: Math.PI * 2,
duration: 10,
repeat: -1, //无线循环
ease: "linear",
onUpdate: () => {
lightGroup.children.forEach((elem, index) => {
elem.position.set(
Math.cos(options.angle + (Math.PI / 2) * index) * 3,
Math.cos(3 * options.angle) * 1,
Math.sin(options.angle + (Math.PI / 2) * index) * 3
);
});
},
});
// 使用补间动画移动相机
let timeLine1 = gsap.timeline();
let timeline2 = gsap.timeline();
// 定义相机移动函数
function translateCamera(position, target) {
timeLine1.to(camera.position, {
x: position.x,
y: position.y,
z: position.z,
duration: 1,
ease: "power2.inOut",
});
timeline2.to(controls.target, {
x: target.x,
y: target.y,
z: target.z,
duration: 1,
ease: "power2.inOut",
});
}
// 实例化创建漫天星星
let starsInstance = new THREE.InstancedMesh(
new THREE.SphereGeometry(0.1, 32, 32),
new THREE.MeshStandardMaterial({
color: 0xffffff,
emissive: 0xffffff,
emissiveIntensity: 10,
}),
100
);
// 星星随机到天上
let starsArr = [];
let endArr = [];
for (let i = 0; i < 100; i++) {
let x = Math.random() * 100 - 50;
let y = Math.random() * 100 - 50;
let z = Math.random() * 100 - 50;
starsArr.push(new THREE.Vector3(x, y, z));
let matrix = new THREE.Matrix4();
matrix.setPosition(x, y, z);
starsInstance.setMatrixAt(i, matrix);
}
scene.add(starsInstance);
// 创建爱心路径
let heartShape = new THREE.Shape();
heartShape.moveTo(25, 25);
heartShape.bezierCurveTo(25, 25, 20, 0, 0, 0);
heartShape.bezierCurveTo(-30, 0, -30, 35, -30, 35);
heartShape.bezierCurveTo(-30, 55, -10, 77, 25, 95);
heartShape.bezierCurveTo(60, 77, 80, 55, 80, 35);
heartShape.bezierCurveTo(80, 35, 80, 0, 50, 0);
heartShape.bezierCurveTo(35, 0, 25, 25, 25, 25);
// 根据爱心路径获取点
let center = new THREE.Vector3(0, 2, 10);
for (let i = 0; i < 100; i++) {
let point = heartShape.getPoint(i / 100);
endArr.push(
new THREE.Vector3(
point.x * 0.1 + center.x,
point.y * 0.1 + center.y,
center.z
)
);
}
// 创建爱心动画
function makeHeart() {
let params = {
time: 0,
};
gsap.to(params, {
time: 1,
duration: 1,
onUpdate: () => {
for (let i = 0; i < 100; i++) {
let x = starsArr[i].x + (endArr[i].x - starsArr[i].x) * params.time;
let y = starsArr[i].y + (endArr[i].y - starsArr[i].y) * params.time;
let z = starsArr[i].z + (endArr[i].z - starsArr[i].z) * params.time;
let matrix = new THREE.Matrix4();
matrix.setPosition(x, y, z);
starsInstance.setMatrixAt(i, matrix);
}
starsInstance.instanceMatrix.needsUpdate = true;
},
});
}
function restoreHeart() {
let params = {
time: 0,
};
gsap.to(params, {
time: 1,
duration: 1,
onUpdate: () => {
for (let i = 0; i < 100; i++) {
let x = endArr[i].x + (starsArr[i].x - endArr[i].x) * params.time;
let y = endArr[i].y + (starsArr[i].y - endArr[i].y) * params.time;
let z = endArr[i].z + (starsArr[i].z - endArr[i].z) * params.time;
let matrix = new THREE.Matrix4();
matrix.setPosition(x, y, z);
starsInstance.setMatrixAt(i, matrix);
}
starsInstance.instanceMatrix.needsUpdate = true;
},
});
}
let scenes = [
{
text: "圣诞快乐",
callback: () => {
// 执行函数切换位置
translateCamera(
new THREE.Vector3(-3.23, 3, 4.06),
new THREE.Vector3(-8, 2, 0)
);
},
},
{
text: "感谢在这么大的世界里遇见了你",
callback: () => {
// 执行函数切
translateCamera(
new THREE.Vector3(7, 0, 23),
new THREE.Vector3(0, 0, 0)
);
},
},
{
text: "愿与你探寻世界的每一个角落",
callback: () => {
// 执行函数切
translateCamera(
new THREE.Vector3(10, 3, 0),
new THREE.Vector3(5, 2, 0)
);
},
},
{
text: "愿将天上的星星送给你",
callback: () => {
// 执行函数切
translateCamera(
new THREE.Vector3(7, 0, 23),
new THREE.Vector3(0, 0, 0)
);
makeHeart();
},
},
{
text: "愿疫情结束,大家健康快乐!",
callback: () => {
// 执行函数切
translateCamera(
new THREE.Vector3(-20, 1.3, 6.6),
new THREE.Vector3(5, 2, 0)
);
},
},
];
let index = 0;
let isAnimate = false;
//创建滑轮滚动切换摄像头位置
window,
addEventListener("wheel", (e) => {
if (isAnimate) return;
isAnimate = true;
if (e.deltaY > 0) {
index++;
index %= scenes.length;
if(index == 0){
restoreHeart();
}
console.log(index);
}
scenes[index].callback();
setTimeout(() => {
isAnimate = false;
}, 1000);
});
//画布全屏
window.addEventListener("dblclick", () => {
if (document.fullscreenElement) {
document.exitFullscreen();
} else {
//document.documentElement.requestFullscreen();
render.domElement.requestFullscreen();
}
});
//监听画面变化,更新渲染画面,(自适应的大小)
window.addEventListener("resize", () => {
//更新摄像机的宽高比
camera.aspect = window.innerWidth / window.innerHeight;
//更新摄像机的投影矩阵
camera.updateProjectionMatrix();
//更新渲染器宽度和高度
render.setSize(window.innerWidth, window.innerHeight);
//设置渲染器的像素比
render.setPixelRatio(window.devicePixelRatio);
});
},
methods: {},
};
</script>
<style scoped lang="scss">
* {
margin: 0;
padding: 0;
}
.home-content {
position: fixed;
top: 0;
right: 20px;
}
.select-item-color {
width: 50px;
height: 50px;
border: 1px solid #ccc;
margin: 10px;
display: inline-block;
cursor: pointer;
border-radius: 10px;
}
.select {
display: flex;
}
</style>
效果图:
相关文章:

ThreeJS-圣诞节表白3D贺卡(三十)
素材分享: 链接: https://pan.baidu.com/s/1l0mZWfkiLaXJfdvZ7XoY8w 提取码: i69h 提前预知: 向下滚动鼠标滑轮切换视角 关键代码: //初始化渲染器 const render new THREE.WebGLRenderer({ //设置抗锯齿,防失真 antialis: …...

040:cesium加载World Terrain地形图
第040个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中加载世界地形图。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共64行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:https://xiaozh…...

逻辑运算和位移指令
逻辑运算指令 AND OR NOT XOR TEST 逻辑位移指令 SHL SHR 算术位移指令 SAL SAR 小循环位移指令 ROL ROR 大循环位移指令 RCL RCR AND 逻辑与指令 汇编格式:AND 目的操作数,源操作数 执行操作:(目的操作数)&…...

大家现在都去做Linux运维了吗?
运维自互联网出现以来,都是以基础技术部门的形式出现在各个互联网公司或者其他需要网络设备的公司里面,职位由来已久,也是多次徘徊在被淘汰的边缘。很多运维人都是靠着自己良好乐观的心态坚持到现在,接受新技术并学习新技术&#…...
Webpack的编译流程是怎么样的?webpack是如何工作的?
Webpack是一款非常流行的前端构建工具,用于将多个模块打包成一个或多个静态资源。它的工作原理是将模块的依赖关系图转化为最终的静态资源。Webpack的编译流程是一个非常复杂的过程,本文将从四个方面详细介绍Webpack的编译流程,分别是入口点分…...
【ZOJ 1151】Word Reversal 题解(字符串+模拟)
问题描述 对于每个单词列表,在不改变单词顺序的情况下,将每个单词反转输出一行。 此问题包含多个测试用例! 多重输入的第一行是整数N,然后是空行,后面跟着N个输入块。每个输入块 采用问题描述中所示的格式。输入块之间…...

Dart语言操作符?和!的用法
一.基本使用 1. ? 操作符跟在类型后面,表示当前变量可为null。 int a null; //这句代码在有空安全时,编译会提示错误如果想给一个变量赋值null要如何处理呢?只需要在类型 后面添加操作符?即可,eg: int? a null…...

聚类 kmeans | 机器学习
聚类 刘建平 1、算法原理: 是一种无监督学习算法,其主要目的是将数据点分为k个簇,距离近的样本具有更高的相似度,距离近的划分为一个簇,一共划分k个簇,**让簇内距离小,簇间距离大。**距离是样…...

求职咨询Job Information
前言 加油 原文 求职咨询常用会话 ❶ I want to apply for a job which enables me to use my major. 我想要申请一个能用到我的专业知识的职业。 ❷ I have the capability of operating the computer. 我有操作电脑的能力。 ❸ My dream is to be an excellent interpret…...

怎么去除pdf文件的水印?好用软件说明
怎么去除pdf文件的水印?在某些情况下,PDF 文件的水印可能会影响文件的可读性和美观度。为了解决这个问题,您可以考虑使用其他方法来标记文档,例如添加页眉或页脚。另一种选择是使用透明度更低的水印,这样它就不会太过分…...

1-ELK+ Elasticsearch+head+kibana、企业内部日志分析系统
ELK:日志收集平台 ELK由ElasticSearch、Logstash和Kibana三个开源工具组成: 概念图 组件介绍 1、Elasticsearch: ElasticSearch是一个基于Lucene的开源分布式搜索服务。只搜索和分析日志 特点:分布式,零配置,自…...

ctfshow愚人杯web复现
easy_signin 题目url base64解码是face.png,尝试flag.txt和flag.php,base64加密后传入都不对,用index.php加密后传入,看源码 将后面的base64解密得到flag 被遗忘的反序列化 源码 <?php# 当前目录中有一个txt文件哦 error_r…...

商品推荐Promoting Products
目录 前言原文内容:推荐常用会话商品推荐常用会话商品推荐常用会话前言 加油 原文内容: ❶ I promise that our product is superior. 我承诺我们的产品比别的家的好。 ❷ Our product is very attractive to young people. 我们的产品很吸引年轻人。 ❸ I want to buy th…...

整懵了,蚂蚁金服4面成功拿下测开offer,涨薪6k,突然觉得跳槽也不是那么难
蚂蚁的面试挺独特的,每轮面试都没有HR约时间,一般是晚上8点左右面试官来一个电话,问是否能面试,能的话开始面,不能就约一个其他时间。 全程4面,前四面技术面,电话面试,最后一面是HR面…...

《扬帆优配》个人养老金投资最新成绩出炉 七成养老FOF跑输基准
自去年底落地以来,个人养老金制度运转已有4个多月。运转以来,设置Y比例的个人养老FOF(基金中的基金)、个人养老金理财、个人养老储蓄、个人养老金稳妥四大产品继续扩容,形成了个人养老金初期的业态样貌。并且历经一季度…...

用Qt编写STM32烧录软件(ISP模式)代码
1.前言 之前写了一篇【用Qt编写STM32烧录软件(ISP模式)】,但是在文中没有具体的实现代码。 现在补上,各位有兴趣的同学可以参考以下。但是代码里面还有很多没有完善的,必定会存在一些bug,目前只是堪堪能用…...

Excel技能之美观排版
一个普通的Excel文件,想要变得好看,除了要掌握相关技能,还要用心。 美观排版,离不开的技能有字体、字体大小、字体颜色、背景色,等等。了解不同的效果用在什么样的场景,才能得心应手,融会贯通&…...

兆芯最新X86 CPU曝光:性能与英特尔/AMD相比,没落后10年
众所周知,在PC领域,X86完全是处于垄断地全的,至少占了90%以上的份额。其它的像MIPS、ARM、RISC-V等等,都不是X86的对手。 这与X86是复杂指令集有关,更与X86绑定了windows操作系统,有坚固的intel联盟有关&am…...
【Go自学】一文搞懂Go的strconv模块
一、 strconv包 strconv包实现了基本数据类型与其字符串表示的转换,主要有以下常用函数: Atoi()、Itoa()、parse系列、format系列、append系列。 其中需要注意的是Atoi、parse系列的返回值是两个,分别是result和error,造成这种现…...

SpringBoot整合Admin服务监控(图文详细)
SpringBoot整合Admin服务监控组件 1 SpringBootAdmin 简介 1.1 概述 SpringBootAdmin 是一个非常好用的监控和管理的开源组件,该组件能够将 Actuator 中的信息进行界面化的展示,也可以监控所有 Spring Boot 应用的健康状况,提供实时警报功…...

XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...

STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...

华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...