three.js 实现3d文字效果思路及代码
Admin 2021-04-25 群英技术资讯 1714 次浏览

上图只是所有效果的其中之一,接下来让我们一起开干吧~
笔者自行封装的three.js模板:Three.js Starter。读者可以点击右下角fork一份后再开始本项目。本项目需要用到位图字体,可以直接复制demo的HTML里的font字体代码
一个注意点:three-bmfont-text这个库依赖全局的three.js,因此要在JS里额外引入一次three.js,如下图

搭好架子
<div class="relative w-screen h-screen"> <div class="kinetic-text w-full h-full bg-blue-1"></div> <div class="font"> <font> 一坨从demo里CV而来的字体代码 </font> </div> </div>
:root {
--blue-color-1: #2c3e50;
}
.bg-blue-1 {
background: var(--blue-color-1);
}
import createGeometry from "https://cdn.skypack.dev/three-bmfont-text@3.0.1";
import MSDFShader from "https://cdn.skypack.dev/three-bmfont-text@3.0.1/shaders/msdf";
import parseBmfontXml from "https://cdn.skypack.dev/parse-bmfont-xml@1.1.4";
const font = parseBmfontXml(document.querySelector(".font").innerHTML);
const fontAtlas = "https://i.loli.net/2021/02/20/DcEhuYNjxCgeU42.png";
const kineticTextTorusKnotVertexShader = `(顶点着色器代码,先空着,具体见下文)`;
const kineticTextTorusKnotFragmentShader = `(片元着色器代码,先空着,具体见下文)`;
class KineticText extends Base {
constructor(sel: string, debug: boolean) {
super(sel, debug);
this.cameraPosition = new THREE.Vector3(0, 0, 4);
this.clock = new THREE.Clock();
this.meshConfig = {
torusKnot: {
vertexShader: kineticTextTorusKnotVertexShader,
fragmentShader: kineticTextTorusKnotFragmentShader,
geometry: new THREE.TorusKnotGeometry(9, 3, 768, 3, 4, 3)
}
};
this.meshNames = Object.keys(this.meshConfig);
this.params = {
meshName: "torusKnot",
velocity: 0.5,
shadow: 5,
color: "#000000",
frequency: 0.5,
text: "ALPHARDEX",
cameraZ: 2.5
};
}
// 初始化
async init() {
this.createScene();
this.createPerspectiveCamera();
this.createRenderer(true);
await this.createKineticText(this.params.text);
this.createLight();
this.createOrbitControls();
this.addListeners();
this.setLoop();
}
// 创建动态文字
async createKineticText(text: string) {
await this.createFontText(text);
this.createRenderTarget();
this.createTextContainer();
}
}
加载和创建字体
首先加载字体文件,并创建出形状和材质,有了这两样就能创建出字体对象了
class KineticText extends Base {
loadFontText(text: string): any {
return new Promise((resolve) => {
const fontGeo = createGeometry({
font,
text
});
const loader = new THREE.TextureLoader();
loader.load(fontAtlas, (texture) => {
const fontMat = new THREE.RawShaderMaterial(
MSDFShader({
map: texture,
side: THREE.DoubleSide,
transparent: true,
negate: false,
color: 0xffffff
})
);
resolve({ fontGeo, fontMat });
});
});
}
async createFontText(text: string) {
const { fontGeo, fontMat } = await this.loadFontText(text);
const textMesh = this.createMesh({
geometry: fontGeo,
material: fontMat
});
textMesh.position.set(-0.965, -0.525, 0);
textMesh.rotation.set(ky.deg2rad(180), 0, 0);
textMesh.scale.set(0.008, 0.025, 1);
this.textMesh = textMesh;
}
}
着色器
顶点着色器。通用模板,直接CV即可
varying vec2 vUv;
varying vec3 vPosition;
void main(){
vec4 modelPosition=modelMatrix*vec4(position,1.);
vec4 viewPosition=viewMatrix*modelPosition;
vec4 projectedPosition=projectionMatrix*viewPosition;
gl_Position=projectedPosition;
vUv=uv;
vPosition=position;
}
片元着色器
利用fract函数创建重复的贴图,加上位移距离displacement使得贴图能随着时间的增加而动起来,再用clamp函数来根据z轴大小限定阴影的范围,意思是离画面越远则阴影越重,反之离画面越近则阴影越轻
uniform sampler2D uTexture;
uniform float uTime;
uniform float uVelocity;
uniform float uShadow;
varying vec2 vUv;
varying vec3 vPosition;
void main(){
vec2 repeat=vec2(12.,3.);
vec2 repeatedUv=vUv*repeat;
vec2 displacement=vec2(uTime*uVelocity,0.);
vec2 uv=fract(repeatedUv+displacement);
vec3 texture=texture2D(uTexture,uv).rgb;
// texture*=vec3(uv.x,uv.y,1.);
float shadow=clamp(vPosition.z/uShadow,0.,1.);// farther darker (to 0).
vec3 color=vec3(texture*shadow);
gl_FragColor=vec4(color,1.);
}
此时文本显示到了屏幕上
创建渲染目标
为了将字体对象本身作为贴图,创建了一个渲染目标
class KineticText extends Base {
createRenderTarget() {
const rt = new THREE.WebGLRenderTarget(
window.innerWidth,
window.innerHeight
);
this.rt = rt;
const rtCamera = new THREE.PerspectiveCamera(45, 1, 0.1, 1000);
rtCamera.position.z = this.params.cameraZ;
this.rtCamera = rtCamera;
const rtScene = new THREE.Scene();
rtScene.add(this.textMesh);
this.rtScene = rtScene;
}
}
创建字体容器
创建一个容器,并将字体对象本身作为贴图贴上去,再应用动画即可完成
class KineticText extends Base {
createTextContainer() {
if (this.mesh) {
this.scene.remove(this.mesh);
this.mesh = null;
this.material!.dispose();
this.material = null;
}
this.rtScene.background = new THREE.Color(this.params.color);
const meshConfig = this.meshConfig[this.params.meshName];
const geometry = meshConfig.geometry;
const material = new THREE.ShaderMaterial({
vertexShader: meshConfig.vertexShader,
fragmentShader: meshConfig.fragmentShader,
uniforms: {
uTime: {
value: 0
},
uVelocity: {
value: this.params.velocity
},
uTexture: {
value: this.rt.texture
},
uShadow: {
value: this.params.shadow
},
uFrequency: {
value: this.params.frequency
}
}
});
this.material = material;
const mesh = this.createMesh({
geometry,
material
});
this.mesh = mesh;
}
update() {
if (this.rtScene) {
this.renderer.setRenderTarget(this.rt);
this.renderer.render(this.rtScene, this.rtCamera);
this.renderer.setRenderTarget(null);
}
const elapsedTime = this.clock.getElapsedTime();
if (this.material) {
this.material.uniforms.uTime.value = elapsedTime;
}
}
}
别忘了把相机调远一些
this.cameraPosition = new THREE.Vector3(0, 0, 40);
风骚的动态文字出现了:)

以上就是three.js 3d文字现实效果的介绍,上文有具体的代码,对此感兴趣的朋友能够参考上述代码来自己动手实现,此外本文能帮助到大家认识three.js。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
如果调用者在函数返回时能够获得预期结果(即获得预期返回值或看到预期效果),则该函数同步。如果函数同步,即使调用函数执行的任务耗时,也会等到得到预期的结果。
这篇文章主要介绍了nuxt.js 多环境变量配置,一般在香米开发中会有三个环境开发环境也叫测试环境(test) 、RC环境也叫预发布环境(rc) 、线上环境(production) 下面来看看文章内容的详细介绍,需要的朋友可以参考一下
Vue前端项目自适应布局的简单方法,下文有实例供大家参考,对大家了解操作过程或相关知识有一定的帮助,而且实用性强,希望这篇文章能帮助大家,下面我们一起来了解看看吧。
jquery去掉元素css属性的方法:1、利用利用“$(元素)”语句获取指定的元素对象;2、利用css()方法来去掉元素的css属性,语法为“元素对象.css("属性名称","");”。
这篇文章主要介绍JS中的new,new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。下面我们一起来看看我呢很脏具体内容的详细介绍,需要的朋友可以参考一下
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008