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 应用的健康状况,提供实时警报功…...

设计模式-结构型模式-组合模式
我们很容易将“组合模式”和“组合关系”搞混。组合模式最初只是用于解决树形结构的场景,更多的是处理对象组织结构之间的问题。而组合关系则是通过将不同对象封装起来完成一个统一功能. 组合模式介绍 组合模式(Composite Pattern) 的定义是:将对象组合…...

VScode开发工具总结
目录 高效使用VScode工具 vscode 字体放大缩小快捷键 Vscode翻译插件推荐 code-translator插件编辑 VsCode自动格式化代码 高效使用VScode工具...

opencv 解码视频流,c++ 代码写法
#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main() { // 打开视频流 VideoCapture cap("your_video_stream_url"); // 检查视频流是否成功打开 if (!cap.isOpened()) { …...

Android 12.0 修改wifi信号强度
1.前言 在12.0的系统rom产品定制化开发中,在进行产品开发中,对应系统定制会有各种各样的需求,对纯wifi产品而言,对于wifi要求也是越来越高,因此有客户要求对wifi信号强度做定制,修改信号强度来增强显示wifi信号,所以要对wifi显示信号强度的相关代码做修改 2.修改wifi…...

Linux——容器简介
1.容器技术 软件应用通常依赖运行时环境提供的其他库、配置文件或服务,在运行时环境安装在物理主机或虚拟机上运行的操作系统,同时应用依赖项也会随着该操作系统一起安装在主机上。 主要弊端是依赖项会受运行时环境的影响,引用所需的支持软件…...

CMOS图像传感器——pipeline像素控制
一、传统像素操作 传统CMOS图像传感器的芯片架构中,像素的控制信号从水平方向驱动,像素的源极跟随器输出电压垂直地输出到位于顶部和底部的模拟前端读出电路,其具体实现方式如下图所示,其中RST, TX和SEL是像素水平控制信号,像素输出电压PIX OUT垂直地传输到读出电路。 …...

AI工具(ChatGPT)常用指令,持续更新...
要国内使用AI工具,关注:码视野,回复:1002使用VensiGPT时,当你给的指令越精准,它回复就会越完美,例如,如果你要请它协助撰写文案,如果没有给与指定情景与目标,…...

36--Django-项目实战-全栈开发-基于django+drf+vue+elementUI企业级项目开发流程-前台项目准备
前台项目创建 1.命令行创建vue项目(参考使用Vue脚手架快速搭建项目) vue create 项目名2.安装插件vue.js 3.配置全局css,在assets/css/global.css /* 声明全局样式和项目的初始化样式 */ body, h1, h2, h3, h4, h5, h6, p, table, tr, td, ul, li, a, form, input, selec…...

游戏算法-游戏AI行为树,python实现
参考文章:Behavior trees for AI: How they work (gamedeveloper.com) 本文主要参考上述weizProject Zomboid 的开发者 Chris Simpson文章的概念,用伪代码实现代码例子 AI概述 游戏AI是对游戏内所有非玩家控制角色的行为进行研究和设计,使得游…...

【新2023Q2模拟题JAVA】华为OD机试 - 矩阵最值 or 计算二维矩阵的最大值
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:矩阵最值 or 计算二维矩阵的最…...