threejs:用着色器给模子添加光带扫描效果

打印 上一主题 下一主题

主题 967|帖子 967|积分 2901

第一步:给模子添加光带

起首创建一个立方体,不举行任何缩放平移操纵,也不要set position。
底子代码如下:
在顶点着色器代码里varying vec3 vPosition;vPosition = position;得到threejs主动计算的顶点坐标插值(也就是这个模子上每个点的xy坐标),然后在片元着色器代码里同样varying vec3 vPosition;来获取xy坐标值。
先设置整体颜色gl_FragColor = vec4(0.0,1.0,1.0,1.0);
然后再通过if条件判定,符合条件的片元设置其他颜色,光带就形成了。
gl_Position = projectionMatrix*viewMatrix*modelMatrix*vec4( position, 1.0 );是固定写法,你可以试下去掉会发生什么。
  1. import * as THREE from 'three';
  2. const geometry = new THREE.BoxGeometry(30,60,30);
  3. const vertexShader = `
  4.         varying vec3 vPosition;//表示顶点插值后位置数据,与片元数量相同,一一对应
  5.         void main(){
  6.                 vPosition = position;// 顶点位置坐标插值计算
  7.                 // 投影矩阵 * 视图矩阵 * 模型矩阵 * 模型顶点坐标
  8.                 gl_Position = projectionMatrix*viewMatrix*modelMatrix*vec4( position, 1.0 );
  9.         }
  10. `;
  11. const fragmentShader = `
  12.         varying vec3 vPosition;
  13.         void main(){
  14.                 // 设置整体颜色
  15.                 gl_FragColor = vec4(0.0,1.0,1.0,1.0);
  16.                 // 当vPosition.y的位置符合if条件时,设置其他颜色,就会形成光带
  17.                 if(vPosition.y > 20.0 && vPosition.y < 22.0 ){
  18.                         gl_FragColor = vec4(1.0,1.0,0.0,1.0);
  19.                 }
  20.         }
  21. `;
  22. // 以下代码是使用着色器材料进行颜色设置
  23. export const material = new THREE.ShaderMaterial({
  24.         //顶点着色器对象vertexShader
  25.         vertexShader: vertexShader,
  26.         // 片元着色器对象fragmentShader
  27.         fragmentShader: fragmentShader
  28. });
  29. export const model = new THREE.Mesh(geometry, material);
复制代码
在y坐标20的地方,有一条宽度为2的光带,由于限制条件是20<y<22。 

第二步:让光带动起来 

想要让光带动起来,只必要限制条件也动起来,好比之前的20<y<22,要让这两个数字随时间发生变化。这时间必要在shader材质里利用uniforms界说一个对象变量startY,包含value属性。
  1. export const material = new THREE.ShaderMaterial({
  2.         uniforms:{
  3.                 startY:{value:-30.0} //立方体位于原点,y的最小值是-30.0,而不是0.0
  4.         },
  5.         //顶点着色器对象vertexShader
  6.         vertexShader: vertexShader,
  7.         // 片元着色器对象fragmentShader
  8.         fragmentShader: fragmentShader
  9. });
复制代码
在片元着色器里接收uniform里的变量,名字必须跟shader材质里界说的雷同,留意这里是uniform,shader材质里是uniforms,将vPosition.y的范围限定在startY和startY+2.0之间。
  1. const fragmentShader = `
  2.         varying vec3 vPosition;
  3.         uniform float startY;
  4.         void main(){
  5.                 // 设置整体颜色,不然模型会设置为默认白色
  6.                 gl_FragColor = vec4(0.0,1.0,1.0,1.0);
  7.                 // 当vPosition.y的位置符合if条件时,设置其他颜色,就会形成光带
  8.                 if(vPosition.y > startY && vPosition.y < startY + 2.0 ){
  9.                         gl_FragColor = vec4(1.0,1.0,0.0,1.0);
  10.                 }
  11.         }
  12. `;
复制代码
在渲染循环里让startY不断改变,片元着色器里的startY跟着变化,光带就动起来了。
特别留意,startY的起始值是-30.0,而不是0.0,startY的最大值是30.0,由于vPosition.y是浮点型数据,在对其举行计算的变量也必须是浮点型。
  1. // 渲染循环
  2. function render() {
  3.     material.uniforms.startY.value += 0.5;
  4.     // 当y超过模型高度后,y重置到模型底部
  5.     if(material.uniforms.startY.value>30.0){
  6.         material.uniforms.startY.value = -30.0;
  7.     }
  8.     renderer.render(scene, camera);
  9.     requestAnimationFrame(render);
  10. }
复制代码

这时会发现光带移动到顶部的时间,会出现闪烁,我们把startY的值再缩小一点,克制这个题目。
之前是material.uniforms.startY.value>30.0 的时间,startY重置,改成material.uniforms.startY.value>25.0没这个现象了,详细上限是多少,跟模子高度和光带宽度有关,根据自己的实际项目来设置即可。
第三步:美化光带

光带的上半部门,从下往上,从光带颜色渐变到模子自己的颜色;下半部门,从上往下,从光带颜色渐变到模子自己的颜色。
  1. const fragmentShader = `
  2.         varying vec3 vPosition;
  3.         uniform float startY;
  4.         const float bandWidth = 20.0;//光带宽度
  5.         float halfBandWidth = bandWidth*0.5;//光带宽度的一半
  6.         const vec3 bandColor = vec3(1.0,0.0,0.0);//光带的颜色
  7.         const vec3 baseColor = vec3(0.0,1.0,1.0);//模型本身的颜色
  8.         void main(){
  9.                 // 设置整体颜色,不然模型会设置为默认白色
  10.                 gl_FragColor = vec4(baseColor,1.0);
  11.                 // 光带上半部分
  12.                 if(vPosition.y > startY && vPosition.y < startY + halfBandWidth ){
  13.                         float percent = (vPosition.y-startY)/halfBandWidth;//范围0~1
  14.                       gl_FragColor.rgb = mix( bandColor, baseColor, percent);
  15.                 }
  16.                 // 光带下半部分
  17.                 if(vPosition.y <= startY && vPosition.y > startY - halfBandWidth ){
  18.                         float percent = (startY - vPosition.y)/halfBandWidth;//范围0~1
  19.                       gl_FragColor.rgb = mix( bandColor, baseColor, percent);
  20.                 }
  21.         }
  22. `;
复制代码
为了更方便看渐变色效果,先把光带移动停下了,光带加宽,光带设置为红色。
留意:gl_FragColor是vec4类型,表示片元颜色的rgba,gl_FragColor.rgb表示当前片元颜色的rgb,不带a(透明度)。
mix是着色器语言GLSL ES的内置函数,可以直接利用,好比参数1和2分表示一个颜色值,通过参数3百分比per,就可以控制两个颜色color1、color2的混淆比例,参数3范围控制在0~1就行。
mix的参数1和2顺序,不用刻意记住,用代码测试下就行,不对就反过来。
mix的两个颜色参数,是vec3的,只包含rgb信息,以是只必要赋值给gl_FragColor.rgb即可,此时默认的透明度是1.0,如果确实必要设置a,可以写成gl_FragColor = vec4( mix( bandColor, baseColor, percent),0.8); 把vec3变成vec4.
要特别留意,光带颜色和模子颜色不要设置为一样,否则就会跟我一样,看不到光带,还以为是代码逻辑有题目,查抄了好几遍才发现。

第四步:增长光带

想要显示多条移动光带,startY不仅要在渲染循环量不断变化,还要在for循环变化,直接写startY += float(i);会报错uniform里的变量不能修改,我们得换个方法,界说另一个uniform变量time,在片元着色器里将time赋值给另一个普通的float变量startY,再在for循环了来改变startY。
  1. // 以下代码是使用着色器材料进行颜色设置
  2. export const material = new THREE.ShaderMaterial({
  3.         uniforms:{
  4.                 time:{value:0.0} //立方体位于原点,y的最小值是-30.0,而不是0.0
  5.         },
  6.         //顶点着色器对象vertexShader
  7.         vertexShader: vertexShader,
  8.         // 片元着色器对象fragmentShader
  9.         fragmentShader: fragmentShader
  10. });
复制代码
  1. const fragmentShader = `
  2.         varying vec3 vPosition;
  3.         uniform float time;
  4.        
  5.         const float bandWidth = 4.0;//光带宽度
  6.         const float bandSpacing = 4.0;//光带间隔
  7.         float halfBandWidth = bandWidth*0.5;//光带宽度的一半
  8.         const vec3 bandColor = vec3(1.0,1.0,0.0);//光带的颜色
  9.         const vec3 baseColor = vec3(0.0,1.0,1.0);//模型本身的颜色
  10.         void main(){
  11.                 // 设置整体颜色,不然模型会设置为默认白色
  12.                 gl_FragColor = vec4(baseColor,1.0);
  13.                 float startY = -30.0+time; //-30.0是模型y坐标的起始值
  14.           //循环产生多条光带
  15.                 for(int i=0;i<10;i++){
  16.                         startY += float(i)+bandSpacing;
  17.                         // 光带上半部分
  18.                         if(vPosition.y > startY && vPosition.y < startY + halfBandWidth ){
  19.                         float percent = (vPosition.y-startY)/halfBandWidth;//范围0~1
  20.                       gl_FragColor.rgb = mix( bandColor, baseColor, percent);
  21.                         }
  22.                         // 光带下半部分
  23.                         if(vPosition.y <= startY && vPosition.y > startY - halfBandWidth ){
  24.                         float percent = (startY - vPosition.y)/halfBandWidth;//范围0~1
  25.                       gl_FragColor.rgb = mix( bandColor, baseColor, percent);
  26.                         }
  27.                 }
  28.         }
  29. `;
复制代码
  1. function render() {
  2.     material.uniforms.time.value += 0.5;
  3.     // 当y超过模型高度后,y重置到模型底部
  4.     if(material.uniforms.time.value>15.0){
  5.         material.uniforms.time.value = 0.0;
  6.     }
  7.     renderer.render(scene, camera);
  8.     requestAnimationFrame(render);
  9. }
复制代码
 

运行后看效果,顶部又开始了闪烁,必要将vPosition.y的值必要限制一下。
在if条件里加上vPosition.y <30.0,留意,光带的上半部门和下半部门都要加这句话。
我的模子里是小于30,详细小于多少,以自己的项目来调解。
旋转后发现底部也有闪烁,继承做限制vPosition.y >-30.0。
模子底部没有光带,必要将startY的下限继承下移。把-30改成-40,总之要比模子自己y的最小值更小。当time=0的时间,startY等于-40,比模子的底部更低,详细值多少,以自己的项目来调解。
float startY = -40.0+time; //模子y坐标的起始值。
除了调解以上数据,还可以调解time重置的条件,好比time大于10和大于20,效果是差别的。 
调解后的效果。

第五步:将光带换成彩色

创建光带颜色数组,在for循环里对数组长度循环取值即可。
换成彩色后,光带直接有一个很大的空隙,这是光带数组中有一个光带颜色跟模子自己的颜色一样,mix后就看不出来光带颜色了,这也是一个必要留意的地方,换个模子颜色后,间隔恢复正常了。

完整代码:
  1. import * as THREE from 'three';const geometry = new THREE.BoxGeometry(30,60,30);const vertexShader = `        varying vec3 vPosition;//表示顶点插值后位置数据,与片元数量雷同,逐一对应        void main(){                vPosition = position;// 顶点位置坐标插值计算                // 投影矩阵 * 视图矩阵 * 模子矩阵 * 模子顶点坐标                gl_Position = projectionMatrix*viewMatrix*modelMatrix*vec4( position, 1.0 );        }`;const fragmentShader = `        varying vec3 vPosition;        uniform float time;                const float bandWidth = 4.0;//光带宽度        const float bandSpacing = 4.0;//光带间隔        float halfBandWidth = bandWidth*0.5;//光带宽度的一半        //光带的颜色        const vec3 bandColor[7] = vec3[7](                vec3(1.0,0.0,0.0),                 vec3(1.0,0.5,0.0),                 vec3(1.0,1.0,0.0),                vec3(0.0,1.0,0.0),                 vec3(0.0,1.0,1.0),                 vec3(0.0,0.0,1.0),                vec3(1.0,0.0,1.0)        );        const vec3 baseColor = vec3(1.0,1.0,1.0);//模子自己的颜色        void main(){                // 设置整体颜色,不然模子会设置为默认白色                gl_FragColor = vec4(baseColor,1.0);                float startY = -40.0+time; //模子y坐标的起始值                float percent = 0.0;                int colorIndex = 0;                for(int i=0;i<10;i++){                        startY += float(i)+bandSpacing;                        colorIndex = int(mod(float(i),float(bandColor.length())));                        // 光带上半部门                        if(vPosition.y > startY && vPosition.y < startY + halfBandWidth && vPosition.y <30.0 && vPosition.y >-30.0){                        percent = (vPosition.y-startY)/halfBandWidth;//范围0~1                      gl_FragColor.rgb = mix( bandColor[colorIndex], baseColor, percent);                        }                        // 光带下半部门                        if(vPosition.y <= startY && vPosition.y > startY - halfBandWidth && vPosition.y <30.0 && vPosition.y >-30.0){                        percent = (startY - vPosition.y)/halfBandWidth;//范围0~1                        gl_FragColor.rgb = mix( bandColor[colorIndex], baseColor, percent);                        }                }        }`;// 以下代码是使用着色器材料进行颜色设置
  2. export const material = new THREE.ShaderMaterial({
  3.         uniforms:{
  4.                 time:{value:0.0} //立方体位于原点,y的最小值是-30.0,而不是0.0
  5.         },
  6.         //顶点着色器对象vertexShader
  7.         vertexShader: vertexShader,
  8.         // 片元着色器对象fragmentShader
  9.         fragmentShader: fragmentShader
  10. });export const model = new THREE.Mesh(geometry, material);
复制代码
  1. // 渲染循环
  2. function render() {
  3.     material.uniforms.time.value += 0.5;
  4.     if(material.uniforms.time.value>10.0){
  5.         material.uniforms.time.value = 0.0;
  6.     }
  7.     renderer.render(scene, camera);
  8.     requestAnimationFrame(render);
  9. }
复制代码




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

前进之路

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表