百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 文章教程 > 正文

预言已显,新月将至!Threejs复刻原神绝美空月之歌场景

yund56 2025-07-20 07:15 4 浏览

预言已显,新月将至!Threejs复刻原神绝美空月之歌场景


不得不说原神的美工这一块真的没话说,身为一个前端切图仔,马上被他的 web 特效吸引。刚好最近在学习 Three.js,想着能否通过复刻一个新活动场景来练练手。于是,我开始了这场 Three.js 的奇妙之旅。


个人精力有限,只做了一个页面,历时五天。差不多还原了这个页面的 90% 的功能。

二、场景分析

这个页面其实不太难,在动手之前,我先仔细观察了原神空月之歌场景,发现主要有以下几个部分:

  • 主体背景:包括渐变的主体背景和左上角旋落的流星、场景中会闪现随机分布的星星。
  • 星环:由多个同心圆环组成,具有动态旋转效果。
  • 坐标轴:场景中的坐标轴装饰。

2.1 主体背景实现

主体背景是一个静态的图片,要实现的话直接导入就可以。

我直接从原神的活动页面抓取了背景图片资源。通过分析网络请求,找到了背景图片的链接,并将其下载到本地项目中。在 Three.js 中,使用 THREE.TextureLoader 加载背景图片,并将其设置为场景的背景。

  1. 勾选请求类型 image
  2. 右侧下载


可以看到除了背景也有很多其他的资源,我们全部 copy 下来放到我们的项目中。使用 Threejs 进行加载:

const textureLoader = new THREE.TextureLoader();
const backgroundTexture = textureLoader.load('path/to/background.jpg');
scene.background = backgroundTexture;

使用 threejs 加载后,为了符合深空的背景,我整体使用紫色和暗色调。在使用 gui 调整整体的透明度,旋转、位置等参数后,得到了初步的效果。


背景中还有一些散落的星星,我在抓包的时候看到老米使用的背景星星好像是使用贴图是不断放大缩小实现的,我觉得这样还是太敷衍了。所以我决定使用 Threejs 中的网格自己实现一个。


首先,创建了一个包含大量点的 THREE.Points 网格每里面大量的点的位置是随机的。

// 创建星星点点
function createStarField() {
const vertices = [];
for (let i = 0; i < 10000; i++) {
const x = (Math.random() - 0.5) * 2000;
const y = (Math.random() - 0.5) * 2000;
const z = (Math.random() - 0.5) * 2000;
vertices.push(x, y, z);
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
const material = new THREE.PointsMaterial({
color: 0xffffff,
size: 1,
transparent: true
});
return new THREE.Points(geometry, material);
}


然后对于每个点我们再使用 shader 进行优化,为了让星星具有闪烁效果,使用了自定义的 ShaderMaterial,通过在顶点着色器和片段着色器中添加时间变量,控制星星的透明度和亮度变化。以下是部分着色器代码:

// 顶点着色器
varying vec3 vPosition;
void main() {
vPosition = position;
gl_PointSize = 1.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
// 片段着色器
uniform float time;
varying vec3 vPosition;
void main() {
float brightness = sin(time + length(vPosition)) * 0.5 + 0.5;
gl_FragColor = vec4(vec3(brightness), brightness);
}


通过不断调整着色器中的参数,最终实现了星星的闪烁效果,使其更加贴近原神中的视觉表现。

2.2 星环实现

星环是整个场景中最复杂的部分之一。为了实现星环的动态旋转效果,需要创建了多个同心圆环,并为每个圆环添加了不同的旋转速度和方向。每个圆环由一系列点组成,这些点同样使用了自定义的着色器来实现发光和闪烁效果。

export function createCircle(
imagePath = CirclePath,
circleName = "circlename",
defaultValue = {
circleSize: 3.5,
rotationSpeed: 0.5, // 默认旋转速度
opacity: 0.5,
}
) {
// 添加图片到圆环中心
const textureLoader = new THREE.TextureLoader();
const circleTexture = textureLoader.load(imagePath);
const circleGeometry = new THREE.PlaneGeometry(1, 1); // 平面大小根据图片调整
const circleMaterial = new THREE.MeshBasicMaterial({
map: circleTexture,
transparent: true,
side: THREE.DoubleSide,
alphaMap: alphaTexture,
opacity: defaultValue.opacity,
alphaTest: 0.1,
});
const circleMesh = new THREE.Mesh(circleGeometry, circleMaterial);
circleMesh.scale.set(Number(defaultValue.circleSize), Number(defaultValue.circleSize), 1);
circleMesh.position.z = 0.1; // 稍微调整z轴避免与星星重叠
const folder = gui.addFolder(circleName);
folder.close(); // 默认收起面板
const controls = {
...defaultValue,
};
// 新增图片大小控制
folder.add(controls, "circleSize", 1, 10).onChange((value) => {
circleMesh.scale.set(Number(value), Number(value), 1);
});
// 添加旋转速度控制
folder.add(controls, "rotationSpeed", -0.1, 0.1).name("旋转速度");
function animate() {
// return
requestAnimationFrame(animate);
// 更新圆环旋转
circleMesh.rotation.z += controls.rotationSpeed * 0.01;
}
animate();
return circleMesh;
}

如果直接加载图片的话会显得不那么真实,看到老米的星环是有一种模糊的质感,于是通过分析发现老米应该是用了贴图纹理,找到资源中相关的图片,将星环加上质感。


// 加载星环纹理
const starRingTexture = textureLoader.load('path/to/star-ring-texture.png');
starRingTexture.wrapS = THREE.RepeatWrapping;
starRingTexture.wrapT = THREE.RepeatWrapping;

2.3 、坐标轴

图片场景还有一个坐标轴,我们直接使用 Point 将点随机分布在坐标轴上就行。

export function createAxisStars() {
const geometry = new THREE.BufferGeometry();
const vertices = [];
for (let i = 0; i < 100; i++) {
const x = -500;
const y = (Math.random() - 0.5) * 1000;
const z = (Math.random() - 0.5) * 1000;
vertices.push(x, y, z);
}
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
const material = new THREE.PointsMaterial({
color: 0xffffff,
size: 2
});
return new THREE.Points(geometry, material);
}

三、最中相机视角确定

目前我们已经完成了需要使用到的所有组件,但是目前我们还是在一个三维的视角中。


现在需要完成最终相机的位置确定,就是用户最终看到的画面。 为了方便,我们将背景的坐标固定在 xy 平面上,将中间倾斜的主体放到一个 group中进行整体旋转

const galaxyGroup = new THREE.Group();
galaxyGroup.add(ringItem1);
galaxyGroup.add(ringItem2);
galaxyGroup.add(startRing1);
galaxyGroup.add(startRing2);
galaxyGroup.add(startRing3);
galaxyGroup.add(startRing4);
galaxyGroup.add(circle1);
galaxyGroup.add(circle2);
galaxyGroup.add(circle3);
galaxyGroup.add(circle4);
galaxyGroup.add(circle5);
galaxyGroup.add(axisStar);
galaxyGroup.rotation.x = -0.8;
galaxyGroup.rotation.y = -0.21;
galaxyGroup.rotation.z = -0.18;

然再调整相关的参数(炼丹),就大差不差了:


四、整体优化

最后,再对整个场景进行了优化,以确保在不同设备上都能流畅运行。

  • 性能优化:通过减少星星和星环的数量、降低着色器的复杂度等方式,在保证视觉效果的同时提高渲染性能。
  • 响应式设计:添加了窗口大小变化的监听器,使场景能够自适应不同的屏幕尺寸。
  • 参数调整:使用 dat.GUI 添加了调试界面,方便在运行时调整各个元素的参数,如星星的大小、星环的旋转速度等。
const gui = new dat.GUI();
gui.add(material.uniforms.size, 'value', 0.1, 5).name('星星大小');
gui.add(material.uniforms.glowIntensity, 'value', 0, 2).name('发光强度');

通过不断调试和优化,最终实现了与原神空月之歌场景相似的视觉效果。

相关推荐

[西门子PLC] S7-1200+触摸屏TP700动画:3种办法制作技巧与案例

有同学问:西门子博图精简触摸屏与2个S71200能同时通讯吗?我现在有两个西门子S-1200PLC,想和一个西门子7寸精简型触摸屏通讯。具体该怎么配置,请教一下各位师傅回答:S7-1200和TP700...

HTML5画布类库EaselJS生成鼠标控制的游戏动画人物

第一节:EaselJS生成游戏动画人物效果本节中将使用SpriteSheet和Sprite生成HTML5游戏中的动画人物形象使用SpriteSheet定义动画帧相关的图片,并且使用Sprite定义动画...

一亿家长五星推荐!金牌动画《萌鸡小队》4K大电影来咯

一亿家长五星推荐200亿播放量的金牌动画IP首度亮相大银幕啦合家欢动画电影《萌鸡小队:萌闯新世界》江苏有线4K特别版块全网首播收获成长和友谊理解爱与勇气陪孩子一起快乐冒险吧~来源:江苏有线今日看点...

JavaScript简介:从概念、特点、组成和用法全面带你快速了解JS

“这里是云端源想IT,帮你轻松学IT”嗨~今天的你过得还好吗?我们总是先扬起尘土然后抱怨自己看不见-2024.04.15-JavaScript,简称JS,是一种轻量级的解释型编程语言,它是网页开...

总结100+前端优质库,让你成为前端百事通

1年多时间,陆陆续续整理了一些常用且实用的开源项目,方便大家更高效地学习和工作.js相关库js常用工具类「lodash」一个一致性、模块化、高性能的JavaScript实用工具库。「xij...

实现一个九宫格跳动loading动画 #前端开发

今天来实现一个九宫格跳动loading动画。·九个小方格会不断地交错跳动。·准备一个方块容器,小方块通过js来生成。js动态生成9个小方格,每个方格的动画延迟错开0.1秒。·使用flex布局让整体居中...

Dynamics.js – 创建逼真的物理动画的 JS 库

Dynamics.js是一个用于创建物理动画JavaScript库。你只需要把dynamics.js引入你的页面,然后就可以激活任何DOM元素的CSS属性动画,也可以结合SVG使...

《火影忍者 博人传》动画化决定!OVA将于JSAF2016先行上映!

JUMP人气漫画《火影忍者》虽然说已经完结很久了,但JUMP和岸本貌似没有停下来的节奏。各种原创动画、外传漫画、剧场版动画接二连三地推出,有些火迷们都感觉看腻了。然而这波节奏还没停!据悉,曾经推出过剧...

强大 WebView2 + 不用写 JavaScript 的 htmx.js 「小轻快」开发桌面程序

WebView2是越来越香了。WebView2不但是Win11自带的系统组件,Win10也已经自动推送安装。即使是少量没有安装WebView2的系统——使用aardio中的we...

小学数学老师用DeepSeek做动画课件,一个提示词轻松搞定。

  前几天我分享了一篇关于小学数学老师想做动画课件,DeepSeek-V3让数学知识"动"起来!。这篇文章收到了很多老师的好评,他们直呼好用,并表示学到了很多实用技巧。但也有老师反馈了...

手把手教你H5实现工厂游戏的CSS动画效果「实践」

作者:吴冠禧WecTeam转发连接:https://mp.weixin.qq.com/s/u5GHsA0vHz8A_MmGslRw2g0契机与背景今年Q1(2020年第一季度)参与了京喜事业部「京...

10个帅酷的HTML5最新动画应用(html5简单的动画)

在上个月,我们收集了不少来自国内外的HTML5相关资源和jQuery插件,其中包括很多经典帅酷的HTML5动画应用,有些还利用了Canvas和SVG的相关特性,从而让HTML5动画更加具有强烈的视觉效...

你需要知道的 15 个很棒的 CSS 动画库

从一种CSS样式配置到另一种的过渡可以使用CSS动画进行动画处理。描述CSS动画的样式和指示动画样式的开始和结束状态的一组关键帧,以及可能的中间路点,构成了动画。与传统的脚本驱动动画技术相...

Three.js 实现虎年春节3D创意页面

前言本文由dragonir授权发布,作者还有很多关于Three.js的作品,刚开始看到这个作品,觉得很有趣,虽然对Three.js完全不懂,哈哈,原文地址:https://segmentfault.c...

性能出色,纯CSS实现的loading动画——Loaders.css

介绍loaders.css是Github上一个使用纯粹的css实现的开源loading动画库,完全用CSS编写的加载动画的集合。每个动画仅限于CSS属性的一小部分,以避免复杂的绘画和布局计算。下面这张...