温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

使用three.js怎么实现一个露珠滴落动画效果

发布时间:2021-03-01 16:41:54 来源:亿速云 阅读:236 作者:Leah 栏目:开发技术

本篇文章为大家展示了使用three.js怎么实现一个露珠滴落动画效果,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

首先将相机换成正交相机,再将平面的长度调整为2,使其填满屏幕

class RayMarching extends Base {  constructor(sel: string, debug: boolean) {  super(sel, debug);  this.clock = new THREE.Clock();  this.cameraPosition = new THREE.Vector3(0, 0, 0);  this.orthographicCameraParams = {   left: -1,   right: 1,   top: 1,   bottom: -1,   near: 0,   far: 1,   zoom: 1  };  }  // 初始化  init() {  this.createScene();  this.createOrthographicCamera();  this.createRenderer();  this.createRayMarchingMaterial();  this.createPlane();  this.createLight();  this.trackMousePos();  this.addListeners();  this.setLoop();  }  // 创建平面  createPlane() {  const geometry = new THREE.PlaneBufferGeometry(2, 2, 100, 100);  const material = this.rayMarchingMaterial;  this.createMesh({   geometry,   material  });  } }

使用three.js怎么实现一个露珠滴落动画效果

创建材质

创建好着色器材质,里面定义好所有要传递给着色器的参数

const matcapTextureUrl = "https://i.loli.net/2021/02/27/7zhBySIYxEqUFW3.png"; class RayMarching extends Base {  // 创建光线追踪材质  createRayMarchingMaterial() {  const loader = new THREE.TextureLoader();  const texture = loader.load(matcapTextureUrl);  const rayMarchingMaterial = new THREE.ShaderMaterial({   vertexShader: rayMarchingVertexShader,   fragmentShader: rayMarchingFragmentShader,   side: THREE.DoubleSide,   uniforms: {   uTime: {    value: 0   },   uMouse: {    value: new THREE.Vector2(0, 0)   },   uResolution: {    value: new THREE.Vector2(window.innerWidth, window.innerHeight)   },   uTexture: {    value: texture   },   uProgress: {    value: 1   },   uVelocityBox: {    value: 0.25   },   uVelocitySphere: {    value: 0.5   },   uAngle: {    value: 1.5   },   uDistance: {    value: 1.2   }   }  });  this.rayMarchingMaterial = rayMarchingMaterial;  } }

顶点着色器 rayMarchingVertexShader ,这个只要用模板现成的就可以了

重点是片元着色器 rayMarchingFragmentShader

片元着色器

背景

作为热身运动,先创建一个辐射状的背景吧

varying vec2 vUv; vec3 background(vec2 uv){  float dist=length(uv-vec2(.5));  vec3 bg=mix(vec3(.3),vec3(.0),dist);  return bg; } void main(){  vec3 bg=background(vUv);  vec3 color=bg;  gl_FragColor=vec4(color,1.); }

使用three.js怎么实现一个露珠滴落动画效果

sdf

如何在光照模型中创建物体呢?我们需要sdf。

sdf的意思是符号距离函数:若传递给函数空间中的某个坐标,则返回那个点与某些平面之间的最短距离,返回值的符号表示点在平面的内部还是外部,故称符号距离函数。

如果我们要创建一个球,就得用球的sdf来创建。球体方程可以用如下的glsl代码来表示

float sdSphere(vec3 p,float r) {  return length(p)-r; }

方块的代码如下

float sdBox(vec3 p,vec3 b) {  vec3 q=abs(p)-b;  return length(max(q,0.))+min(max(q.x,max(q.y,q.z)),0.); }

看不懂怎么办?没关系,国外已经有大牛把 常用的sdf公式 都整理出来了

在sdf里先创建一个方块

float sdf(vec3 p){  float box=sdBox(p,vec3(.3));  return box; }

画面上仍旧一片空白,因为我们的嘉宾——光线还尚未入场。

光线步进

接下来就是本文的头号人物——光线步进了。在介绍她之前,我们先来看看她的好姬友光线追踪吧。

使用three.js怎么实现一个露珠滴落动画效果

首先,我们需要知道光线追踪是如何进行的:给相机一个位置 eye ,在前面放一个网格,从相机的位置发射一束射线 ray ,穿过网格打在物体上,所成的像的每一个像素对应着网格上的每一个点。

而在光线步进中,整个场景会由一系列的sdf的角度定义。为了找到场景和视线之间的边界,我们会从相机的位置开始,沿着射线,一点一点地移动每个点,每一步都会判断这个点在不在场景的某个表面内部,如果在则完成,表示光线击中了某东西,如果不在则光线继续步进。

使用three.js怎么实现一个露珠滴落动画效果

上图中,p0是相机位置,蓝色的线代表射线。可以看出光线的第一步p0p1就迈的非常大,它也恰好是此时光线到表面的最短距离。表面上的点尽管是最短距离,但并没有沿着视线的方向,因此要继续检测到p4这个点

shadertoy上有一个 可交互的例子

以下是光线步进的glsl代码实现

const float EPSILON=.0001; float rayMarch(vec3 eye,vec3 ray,float end,int maxIter){  float depth=0.;  for(int i=0;i<maxIter;i++){   vec3 pos=eye+depth*ray;   float dist=sdf(pos);   depth+=dist;   if(dist<EPSILON||dist>=end){    break;   }  }  return depth; }

在主函数中创建一条射线,将其投喂给光线步进算法,即可获得光线到表面的最短距离

void main(){  ...  vec3 eye=vec3(0.,0.,2.5);  vec3 ray=normalize(vec3(vUv,-eye.z));  float end=5.;  int maxIter=256;  float depth=rayMarch(eye,ray,end,maxIter);  if(depth<end){   vec3 pos=eye+depth*ray;   color=pos;  }  ... }

使用three.js怎么实现一个露珠滴落动画效果

在光线步进的引诱下,野生的方块出现了!

居中材质

目前的方块有2个问题:1. 没有居中 2. x轴方向上被拉伸

居中+拉伸素质2连走起

vec2 centerUv(vec2 uv){  uv=2.*uv-1.;  float aspect=uResolution.x/uResolution.y;  uv.x*=aspect;  return uv; } void main(){  ...  vec2 cUv=centerUv(vUv);  vec3 ray=normalize(vec3(cUv,-eye.z));  ... }

使用three.js怎么实现一个露珠滴落动画效果

方块瞬间飘到了画面的正中央,但此时的她还没有颜色

计算表面法线

在光照模型中,我们需要 计算出表面法线 ,才能给材质赋予颜色

vec3 calcNormal(in vec3 p) {  const float eps=.0001;  const vec2 h=vec2(eps,0);  return normalize(vec3(sdf(p+h.xyy)-sdf(p-h.xyy),  sdf(p+h.yxy)-sdf(p-h.yxy),  sdf(p+h.yyx)-sdf(p-h.yyx))); } void main(){  ...  if(depth<end){   vec3 pos=eye+depth*ray;   vec3 normal=calcNormal(pos);   color=normal;  }  ... }

使用three.js怎么实现一个露珠滴落动画效果

此时方块被赋予了蓝色,但我们还看不出她是个立体图形

动起来

让方块360°旋转起来吧,3D旋转函数直接在 gist 上搜一下就有了

uniform float uVelocityBox; mat4 rotationMatrix(vec3 axis,float angle){  axis=normalize(axis);  float s=sin(angle);  float c=cos(angle);  float oc=1.-c;    return mat4(oc*axis.x*axis.x+c,oc*axis.x*axis.y-axis.z*s,oc*axis.z*axis.x+axis.y*s,0.,   oc*axis.x*axis.y+axis.z*s,oc*axis.y*axis.y+c,oc*axis.y*axis.z-axis.x*s,0.,   oc*axis.z*axis.x-axis.y*s,oc*axis.y*axis.z+axis.x*s,oc*axis.z*axis.z+c,0.,  0.,0.,0.,1.); } vec3 rotate(vec3 v,vec3 axis,float angle){  mat4 m=rotationMatrix(axis,angle);  return(m*vec4(v,1.)).xyz; } float sdf(vec3 p){  vec3 p1=rotate(p,vec3(1.),uTime*uVelocityBox);  float box=sdBox(p1,vec3(.3));  return box; }

使用three.js怎么实现一个露珠滴落动画效果

融合效果

单单一个方块太孤单了,创建一个球来陪陪她吧

如何让球和方块贴在一起呢,你需要 smin 这个函数

uniform float uProgress; float smin(float a,float b,float k) {  float h=clamp(.5+.5*(b-a)/k,0.,1.);  return mix(b,a,h)-k*h*(1.-h); } float sdf(vec3 p){  vec3 p1=rotate(p,vec3(1.),uTime*uVelocityBox);  float box=sdBox(p1,vec3(.3));  float sphere=sdSphere(p,.3);  float sBox=smin(box,sphere,.3);  float mixedBox=mix(sBox,box,uProgress);  return mixedBox; }

uProgress 的值设为0,她们成功地贴在了一起

使用three.js怎么实现一个露珠滴落动画效果

uProgress 的值调回1,她们又分开了

动态融合

接下来就是露珠滴落的动画实现了,其实就是对融合图形应用了一个位移变换

uniform float uAngle; uniform float uDistance; uniform float uVelocitySphere; const float PI=3.14159265359; float movingSphere(vec3 p,float shape){  float rad=uAngle*PI;  vec3 pos=vec3(cos(rad),sin(rad),0.)*uDistance;  vec3 displacement=pos*fract(uTime*uVelocitySphere);  float gotoCenter=sdSphere(p-displacement,.1);  return smin(shape,gotoCenter,.3); } float sdf(vec3 p){  vec3 p1=rotate(p,vec3(1.),uTime*uVelocityBox);  float box=sdBox(p1,vec3(.3));  float sphere=sdSphere(p,.3);  float sBox=smin(box,sphere,.3);  float mixedBox=mix(sBox,box,uProgress);  mixedBox=movingSphere(p,mixedBox);  return mixedBox; }

使用three.js怎么实现一个露珠滴落动画效果

matcap贴图

默认的材质太土了?我们有帅气的matcap贴图来助阵

uniform sampler2D uTexture; vec2 matcap(vec3 eye,vec3 normal){  vec3 reflected=reflect(eye,normal);  float m=2.8284271247461903*sqrt(reflected.z+1.);  return reflected.xy/m+.5; } float fresnel(float bias,float scale,float power,vec3 I,vec3 N) {  return bias+scale*pow(1.+dot(I,N),power); } void main(){  ...  if(depth<end){   vec3 pos=eye+depth*ray;   vec3 normal=calcNormal(pos);   vec2 matcapUv=matcap(ray,normal);   color=texture2D(uTexture,matcapUv).rgb;   float F=fresnel(0.,.4,3.2,ray,normal);   color=mix(color,bg,F);  }  ... }

上述内容就是使用three.js怎么实现一个露珠滴落动画效果,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI