three.js如何实现3D动态文字效果
前言
大家好,这里是CSS魔法使——alphardex。
之前在逛国外网站的时候,发现有些网站的文字是刻在3D图形上的,并且能在图形上运动,视觉效果相当不错,于是笔者就也想用three.js来尝试复现出这种效果
上图只是所有效果的其中之一,接下来让我们一起开干吧~
准备工作
笔者自行封装的three.js模板:Three.jsStarter
读者可以点击右下角fork一份后再开始本项目
本项目需要用到位图字体,可以直接复制demo的HTML里的font字体代码
一个注意点:three-bmfont-text这个库依赖全局的three.js,因此要在JS里额外引入一次three.js,如下图
实现思路
- 加载位图字体文件,将其转化为文字对象所需要的形状和材质
- 创建文字对象
- 创建渲染目标,可以理解为canvas中的canvas,因为接下来我们要将文字对象本身当做贴图
- 创建承载字体的容器,将文字对象作为贴图贴上去
- 动画
正片
搭好架子
一坨从demo里CV而来的字体代码
:root{
--blue-color-1:#2c3e50;
}
.bg-blue-1{
background:var(--blue-color-1);
}
importcreateGeometryfrom"https://cdn.skypack.dev/three-bmfont-text@3.0.1";
importMSDFShaderfrom"https://cdn.skypack.dev/three-bmfont-text@3.0.1/shaders/msdf";
importparseBmfontXmlfrom"https://cdn.skypack.dev/parse-bmfont-xml@1.1.4";
constfont=parseBmfontXml(document.querySelector(".font").innerHTML);
constfontAtlas="https://i.loli.net/2021/02/20/DcEhuYNjxCgeU42.png";
constkineticTextTorusKnotVertexShader=`(顶点着色器代码,先空着,具体见下文)`;
constkineticTextTorusKnotFragmentShader=`(片元着色器代码,先空着,具体见下文)`;
classKineticTextextendsBase{
constructor(sel:string,debug:boolean){
super(sel,debug);
this.cameraPosition=newTHREE.Vector3(0,0,4);
this.clock=newTHREE.Clock();
this.meshConfig={
torusKnot:{
vertexShader:kineticTextTorusKnotVertexShader,
fragmentShader:kineticTextTorusKnotFragmentShader,
geometry:newTHREE.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
};
}
//初始化
asyncinit(){
this.createScene();
this.createPerspectiveCamera();
this.createRenderer(true);
awaitthis.createKineticText(this.params.text);
this.createLight();
this.createOrbitControls();
this.addListeners();
this.setLoop();
}
//创建动态文字
asynccreateKineticText(text:string){
awaitthis.createFontText(text);
this.createRenderTarget();
this.createTextContainer();
}
}
加载和创建字体
首先加载字体文件,并创建出形状和材质,有了这两样就能创建出字体对象了
classKineticTextextendsBase{
loadFontText(text:string):any{
returnnewPromise((resolve)=>{
constfontGeo=createGeometry({
font,
text
});
constloader=newTHREE.TextureLoader();
loader.load(fontAtlas,(texture)=>{
constfontMat=newTHREE.RawShaderMaterial(
MSDFShader({
map:texture,
side:THREE.DoubleSide,
transparent:true,
negate:false,
color:0xffffff
})
);
resolve({fontGeo,fontMat});
});
});
}
asynccreateFontText(text:string){
const{fontGeo,fontMat}=awaitthis.loadFontText(text);
consttextMesh=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即可
varyingvec2vUv;
varyingvec3vPosition;
voidmain(){
vec4modelPosition=modelMatrix*vec4(position,1.);
vec4viewPosition=viewMatrix*modelPosition;
vec4projectedPosition=projectionMatrix*viewPosition;
gl_Position=projectedPosition;
vUv=uv;
vPosition=position;
}
片元着色器
利用fract函数创建重复的贴图,加上位移距离displacement使得贴图能随着时间的增加而动起来,再用clamp函数来根据z轴大小限定阴影的范围,意思是离画面越远则阴影越重,反之离画面越近则阴影越轻
uniformsampler2DuTexture;
uniformfloatuTime;
uniformfloatuVelocity;
uniformfloatuShadow;
varyingvec2vUv;
varyingvec3vPosition;
voidmain(){
vec2repeat=vec2(12.,3.);
vec2repeatedUv=vUv*repeat;
vec2displacement=vec2(uTime*uVelocity,0.);
vec2uv=fract(repeatedUv+displacement);
vec3texture=texture2D(uTexture,uv).rgb;
//texture*=vec3(uv.x,uv.y,1.);
floatshadow=clamp(vPosition.z/uShadow,0.,1.);//fartherdarker(to0).
vec3color=vec3(texture*shadow);
gl_FragColor=vec4(color,1.);
}
此时文本显示到了屏幕上
创建渲染目标
为了将字体对象本身作为贴图,创建了一个渲染目标
classKineticTextextendsBase{
createRenderTarget(){
constrt=newTHREE.WebGLRenderTarget(
window.innerWidth,
window.innerHeight
);
this.rt=rt;
constrtCamera=newTHREE.PerspectiveCamera(45,1,0.1,1000);
rtCamera.position.z=this.params.cameraZ;
this.rtCamera=rtCamera;
constrtScene=newTHREE.Scene();
rtScene.add(this.textMesh);
this.rtScene=rtScene;
}
}
创建字体容器
创建一个容器,并将字体对象本身作为贴图贴上去,再应用动画即可完成
classKineticTextextendsBase{
createTextContainer(){
if(this.mesh){
this.scene.remove(this.mesh);
this.mesh=null;
this.material!.dispose();
this.material=null;
}
this.rtScene.background=newTHREE.Color(this.params.color);
constmeshConfig=this.meshConfig[this.params.meshName];
constgeometry=meshConfig.geometry;
constmaterial=newTHREE.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;
constmesh=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);
}
constelapsedTime=this.clock.getElapsedTime();
if(this.material){
this.material.uniforms.uTime.value=elapsedTime;
}
}
}
别忘了把相机调远一些
this.cameraPosition=newTHREE.Vector3(0,0,40);
风骚的动态文字出现了:)
项目地址
KineticText
demo里不止本文创建的这一种形状,大家可以随意把玩。
总结
到此这篇关于three.js如何实现3D动态文字效果的文章就介绍到这了,更多相关three.js3D动态文字内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。