了解明日方舟页面球体动画
这是对明日方舟登录界面与官网球体动画名称的探索记录,并没有什么实际意义
结论
最后的结论是,就是球体网格动画
起因
在登录游戏与进入游戏官网时一直都能看到以下这个球体动画
我就一直很好奇这玩意到底是个啥,有没有什么学名。但是从各种搜索引擎,游戏论坛乃至群友里问都没问出个所以然(可能是问的少了,就问了一次)。于是只好自己想办法看看。
动画分析
游戏客户端的没录,上面的是网页端的动画gif录屏。很明显是一个带有节点三角面组成的球体动画的一部分(我也不清楚相关名词有没有用对,只是搜刮了贫瘠的大脑拼凑一句描述)
动画来源
但是光分析出 三角面 球体 动画 3d 这几个关键词似乎还不够搜出精确的名词
类似的图倒是有,但是也没有给出具体的名词。(此时继续翻个十来页多翻几十个博客或许能找到,但还是决定先多找一些关键词来定位)
动画显示的位置有客户端登陆界面和官网界面,客户端应该是unity打包的,对unity不太熟,而且原生apk反编译下就算找到控件位置估计也看不明白,只是感觉可能是opengl还是啥引擎实现的动画。于是决定从官网着手
对官网的查看
还好最近学了几天前端,直接打开开发者工具开找
最后一看是一个canvas标签展示的动画,那估计是js控制显示的了,然后这里的id也说明了应该是用webgl,也就是还是opengl实现的动画。但是网页的js很明显是压缩加密过的,对这玩意也不够了解,还是决定继续开搜。
对网页布局的查看最后又多了几个关键词 canvas js动画 webgl
接下来就是排列组合继续搜索一下
得到结果
然后找到了该博客在浏览器中画圆都会,但是画个 3D 的球呢? - 掘金 (juejin.cn)
(这和联想的关键词好像多少有点不沾边)
但是这里给出的第一个案例勾引(了我,看起来似曾相似
Sphere (woopen.github.io)
从这里了解到画标准球(UV Sphere)的方法,但是搜索得到C++ 生成球体网格 - 知乎 (zhihu.com)后发现UV Sphere只是一种绘制球体的方式,此处引入四种绘制球体的方式与相关图片
The UV Sphere
The lcosphere
The Quad Sphere
Goldberg Polyhedra
很明显 The Icosphere 更接近我想要搜索的球体动画的名字,但这只是一种生成球体网格的方式。从第一句中看见了球体网格 这个名词,让我感觉应该找到结果了。
于是我现在可能会用The Icosphere 方式生成的球体网格 动画来形容这个动画,简单说就是一个球体网格 动画。虽然可能没什么意义,但大概或许还是能锻炼到搜索信息的能力的罢。我原本还以为是个有什么意义的图形(什么代表脑细胞啥的)
可能是因为以前看过类似这张图的图片吧
抄一个球体网格动画实现
来都来了,前面在浏览器中画圆都会,但是画个 3D 的球呢? - 掘金 (juejin.cn)也给出了绘制球体网格动画的源码,干脆直接抄一个玩玩(也没指望学到啥,只是练练敲js,毕竟量子学习的时候只管用,对原理啥的记得不多)
最后看着改源码就弄出了这玩意(难视)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="referrer" content="no-referrer"/>
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/gl-matrix@3.3.0/gl-matrix.js"></script>
<style>
</style>
<script>
</script>
</head>
<body>
<div id="app">
<h2>Sphere Anim</h2>
<script>
window.mat4 = glMatrix.mat4;
window.vec3 = glMatrix.vec3;
const canvas = document.createElement('canvas');
canvas.width = 400;
canvas.height = 400;
document.body.appendChild(canvas);
const gl = canvas.getContext('webgl');
gl.viewport(0,0, gl.drawingBufferWidth, gl.drawingBufferHeight);
const vs = `
attribute vec4 aPosition;
uniform mat4 uPVMat;
uniform mat4 uModelMat;
void main() {
gl_Position = uPVMat * uModelMat * aPosition;
}
// `;
const fs = `
precision mediump float;
void main() {
gl_FragColor = vec4(0.58, 0.51, 0.07, 1.);
}
`;
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vs);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fs);
const program = createProgram(gl, vertexShader, fragmentShader);
gl.useProgram(program);
gl.clearColor(0.09, 0.09, 0.09, 1);
const baseModelMat = mat4.fromScaling(mat4.create(), [1,1,1]);
const viewMat = mat4.lookAt(mat4.create(), [0, 0, 10], [0, 0, 0], [0, 1, 0]);
const presMat = mat4.perspective(
mat4.create(),
rad(12),
gl.canvas.clientWidth / gl.canvas.clientHeight,
1,
2000
);
const pvMat = mat4.mul(mat4.create(), presMat, viewMat);
const iSphere = createIcosahedronSphere(1);
const iSphereCount = iSphere.length / 3;
const uPVMat = gl.getUniformLocation(program, "uPVMat");
const uModelMat = gl.getUniformLocation(program, "uModelMat");
const aPosition = gl.getAttribLocation(program, "aPosition");
const iBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, iBuffer);
gl.bufferData(gl.ARRAY_BUFFER, iSphere, gl.STATIC_DRAW);
gl.uniformMatrix4fv(uPVMat, false, pvMat);
gl.enableVertexAttribArray(aPosition);
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
let rotate = 0;
function draw(){
gl.clear(gl.COLOR_BUFFER_BIT);
let modelMat = mat4.rotateY(
mat4.create(),
mat4.translate([], baseModelMat, [0, 0, 0]),
rotate
);
gl.uniformMatrix4fv(uModelMat, false, modelMat);
gl.bindBuffer(gl.ARRAY_BUFFER, iBuffer);
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
for (let i = 0; i < iSphereCount; i += 3) gl.drawArrays(gl.LINE_LOOP, i, 3);
rotate += 0.01;
requestAnimationFrame(draw);
}
draw();
function createIcosahedronSphere(count = 0) {
const points = [];
const t = (1 + Math.sqrt(5)) / 2;
const v1 = vec3.normalize([], [-1, t, 0]);
const v2 = vec3.normalize([], [1, t, 0]);
const v3 = vec3.normalize([], [-1, -t, 0]);
const v4 = vec3.normalize([], [1, -t, 0]);
const v5 = vec3.normalize([], [0, -1, t]);
const v6 = vec3.normalize([], [0, 1, t]);
const v7 = vec3.normalize([], [0, -1, -t]);
const v8 = vec3.normalize([], [0, 1, -t]);
const v9 = vec3.normalize([], [t, 0, -1]);
const v10 = vec3.normalize([], [t, 0, 1]);
const v11 = vec3.normalize([], [-t, 0, -1]);
const v12 = vec3.normalize([], [-t, 0, 1]);
function tri(a, b, c) {
points.push(...a, ...b, ...c);
}
function divide(a, b, c, count) {
if (count > 0) {
const ab = vec3.normalize([], vec3.scale([], vec3.add([], a, b), 0.5));
const ac = vec3.normalize([], vec3.scale([], vec3.add([], a, c), 0.5));
const bc = vec3.normalize([], vec3.scale([], vec3.add([], b, c), 0.5));
divide(a, ab, ac, count - 1);
divide(ab, b, bc, count - 1);
divide(bc, c, ac, count - 1);
divide(ab, bc, ac, count - 1);
} else {
tri(a, b, c);
}
}
divide(v1, v12, v6, count);
divide(v1, v6, v2, count);
divide(v1, v2, v8, count);
divide(v1, v8, v11, count);
divide(v1, v11, v12, count);
divide(v2, v6, v10, count);
divide(v6, v12, v5, count);
divide(v12, v11, v3, count);
divide(v11, v8, v7, count);
divide(v8, v2, v9, count);
divide(v4, v10, v5, count);
divide(v4, v5, v3, count);
divide(v4, v3, v7, count);
divide(v4, v7, v9, count);
divide(v4, v9, v10, count);
divide(v5, v10, v6, count);
divide(v3, v5, v12, count);
divide(v7, v3, v11, count);
divide(v9, v7, v8, count);
divide(v10, v9, v2, count);
return new Float32Array(points);
}
function rad(deg) {
return (deg * Math.PI) / 180;
}
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!success) {
throw new Error(
"could not compile shader -> " + source + gl.getShaderInfoLog(shader)
);
}
return shader;
}
function createProgram(gl, vertex, fragment) {
const program = gl.createProgram();
gl.attachShader(program, vertex);
gl.attachShader(program, fragment);
gl.linkProgram(program);
const success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!success) {
throw new Error(
"program failed to link -> " + gl.getProgramInfoLog(program)
);
}
return program;
}
</script>
</div>
</body>
</html>
Sphere_Anim (gitee.io)
|