前言 是不是看我许多天没更新可视化的内容了,以为我放弃了,哈哈,并没有,只是这一块的内容比较晦涩,我学起来也费事,多学了一会,加上这两天有点忙,然后下班后想放松下,就拖了俩天,好了,废话不多说,开始我们今天的内容分享部分,这个也是webgl的核心部分,shader。之前我们在可视化学习12中讲过shader。太久没讲了,重新复习下。
复习 让我们用shader画一个最基础的图形 先搞一份基础代码,创建一个基础平面1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 import * as THREE from "three" ;import { OrbitControls } from "three/examples/jsm/controls/OrbitControls" ;const scene = new THREE .Scene ();const camera = new THREE .PerspectiveCamera ( 90 , window .innerHeight / window .innerHeight , 0.1 , 1000 ); camera.position .set (0 , 0 , 2 ); camera.aspect = window .innerWidth / window .innerHeight ; camera.updateProjectionMatrix (); scene.add (camera); const axesHelper = new THREE .AxesHelper (5 );scene.add (axesHelper); const material = new THREE .MeshBasicMaterial ({ color : "#00ff00" });const floor = new THREE .Mesh ( new THREE .PlaneBufferGeometry (1 , 1 , 64 , 64 ), material ); scene.add (floor); const renderer = new THREE .WebGLRenderer ({ alpha : true });renderer.setSize (window .innerWidth , window .innerHeight ); window .addEventListener ("resize" , () => { camera.aspect = window .innerWidth / window .innerHeight ; camera.updateProjectionMatrix (); renderer.setSize (window .innerWidth , window .innerHeight ); renderer.setPixelRatio (window .devicePixelRatio ); }); document .body .appendChild (renderer.domElement );const controls = new OrbitControls (camera, renderer.domElement );controls.enableDamping = true ; const clock = new THREE .Clock ();function animate (t ) { const elapsedTime = clock.getElapsedTime (); requestAnimationFrame (animate); renderer.render (scene, camera); } animate ();
这是我们将threejs的材质用我们的shader替换,也可以有一样的效果1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const shaderMaterial = new THREE .ShaderMaterial ({ vertexShader : ` void main(){ gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ; } ` , fragmentShader : ` void main(){ gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); } ` ,}); const floor = new THREE .Mesh ( new THREE .PlaneBufferGeometry (1 , 1 , 64 , 64 ), shaderMaterial );
打造流动旗帜 我们先来看看效果图
接下来,我们把shader拆出去,单独写一个文件,然后引入进来
1 2 3 4 import basicVertexShader from "../shader/raw/vertex.glsl" ;import basicFragmentShader from "../shader/raw/fragment.glsl" ;
导入旗帜的图片作为纹理
1 2 3 const textureLoader = new THREE .TextureLoader ();const texture = textureLoader.load ("./texture/ca.jpeg" );
创建材质使用我们外部引入的shader并把纹理给我们的shader文件,这里因为用到了shader,因此使用了原始着色器材质
1 2 3 4 5 6 7 8 9 10 const rawShaderMaterial = new THREE .RawShaderMaterial ({ vertexShader : basicVertexShader, fragmentShader : basicFragmentShader, side : THREE .DoubleSide , uniforms : { uTexture : { value : texture, }, }, });
精度理解 精度越高就越细节,但是渲染花费的资源也就越多,所以我们要根据实际情况来选择精度
简单介绍uv坐标 就拿我们刚才的正方形来说,我们的uv坐标是这样的,从左下角开始是(0,0),右上角是(1,1),大家只需要知道这个值在0-1之间就可以了
变量介绍 简单来说就是
attribute 使用内部属性
uniform 使用外部属性
varying 着色器之间传递数据
顶点着色器内容绘制 在文件vertex.glsl中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 precision lowp float ;attribute vec3 position;attribute vec2 uv;uniform mat4 modelMatrix;uniform mat4 viewMatrix;uniform mat4 projectionMatrix;varying vec2 vUv;void main(){ vUv = uv; vec4 modelPosition = modelMatrix * vec4 ( position, 1.0 ); gl_Position = projectionMatrix * viewMatrix * modelPosition ; }
片元着色器 在外部fragment.glsl中
1 2 3 4 5 6 7 8 9 10 11 precision lowp float ;varying vec2 vUv;uniform sampler2D uTexture; void main(){ float height = 0.05 * 20.0 ; vec4 textureColor = texture2D (uTexture,vUv); textureColor.rgb*=height; gl_FragColor = textureColor; }
这样完成之后让我们看下效果
添加时间,让旗帜动起来 在main.js中,添加时间变量
1 2 3 4 5 6 7 8 uniforms : { uTime : { value : 0 , }, uTexture : { value : texture, }, },
animate赋值
1 2 3 4 5 6 7 8 function animate (t ) { const elapsedTime = clock.getElapsedTime (); rawShaderMaterial.uniforms .uTime .value = elapsedTime; requestAnimationFrame (animate); renderer.render (scene, camera); }
在顶点着色器中使用时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 precision lowp float ;attribute vec3 position;attribute vec2 uv;uniform mat4 modelMatrix;uniform mat4 viewMatrix;uniform mat4 projectionMatrix;uniform float uTime;varying vec2 vUv;varying float vElevation;void main(){ vUv = uv; vec4 modelPosition = modelMatrix * vec4 ( position, 1.0 ); modelPosition.z = sin ((modelPosition.x+uTime) * 10.0 )*0.05 ; modelPosition.z += sin ((modelPosition.y+uTime) * 10.0 )*0.05 ; vElevation = modelPosition.z; gl_Position = projectionMatrix * viewMatrix * modelPosition ; }
// 把顶点着色器位置信息传递出去,片元着色器根据偏移量设置抖动时候的颜色改变1 2 3 4 5 6 7 8 9 10 11 12 13 14 precision lowp float ;varying vec2 vUv;varying float vElevation;uniform sampler2D uTexture; void main(){ float height = vElevation+0.05 * 20.0 ; vec4 textureColor = texture2D (uTexture,vUv); textureColor.rgb*=height; gl_FragColor = textureColor; }
这样,我们就将抖动的旗帜完成了
uv搞颜色 核心就是理解uv的概念
通过顶点对应的uv,决定每一个像素在uv图像的位置,通过这个位置x,y决定颜色 顶点着色器保持不要变1 2 3 4 5 6 7 varying vec2 vUv;precision lowp float ;void main(){ vec4 modelPosition = modelMatrix * vec4 ( position, 1.0 ); vUv=uv; gl_Position = projectionMatrix * viewMatrix * modelPosition; }
片元着色器1 gl_FragColor =vec4 (vUv,0 ,1 );
简单讲解下,为什么会出现这么一样的效果,之前说过,uv是从0-1变化的,左小右大,上大下小,最左下方是(0,0),那么这个颜色就是(0,0,0,1),是黑色,最右上方是(1,1),那么这个颜色就是(1,1,0,1),红色和绿色融合就是黄色,所以就出现了这样的效果
对第一种变形 1 gl_FragColor = vec4 (vUv,1 ,1 );
利用uv实现渐变效果,从左到右 1 2 float strength = vUv.x;gl_FragColor =vec4 (strength,strength,strength,1 );
利用uv实现渐变效果,从下到上 1 2 float strength = vUv.y;gl_FragColor =vec4 (strength,strength,strength,1 );
利用uv实现渐变效果,从上到下 1 2 float strength = 1.0 -vUv.y;gl_FragColor =vec4 (strength,strength,strength,1 );
利用uv实现短范围内渐变 1 2 float strength = vUv.y * 10.0 ;gl_FragColor =vec4 (strength,strength,strength,1 );
利用通过取模达到反复效果 1 2 float strength = mod (vUv.y * 10.0 , 1.0 ) ;gl_FragColor =vec4 (strength,strength,strength,1 );
利用step(edge, x)如果x < edge,返回0.0,否则返回1.0 1 2 3 float strength = mod (vUv.y * 10.0 , 1.0 ) ;strength = step (0.5 ,strength); gl_FragColor =vec4 (strength,strength,strength,1 );
利用step(edge, x)如果x < edge,返回0.0,否则返回1.0,换个参数 1 2 3 float strength = mod (vUv.y * 10.0 , 1.0 ) ;strength = step (0.8 ,strength); gl_FragColor =vec4 (strength,strength,strength,1 );
利用step(edge, x)如果x < edge,返回0.0,否则返回1.0,换个参数 1 2 3 float strength = mod (vUv.x * 10.0 , 1.0 ) ; strength = step (0.8 ,strength); gl_FragColor =vec4 (strength,strength,strength,1 );
条纹相加 1 2 3 float strength = step (0.8 , mod (vUv.x * 10.0 , 1.0 )) ; strength += step (0.8 , mod (vUv.y * 10.0 , 1.0 )) ; gl_FragColor =vec4 (strength,strength,strength,1 );
条纹相乘 1 2 3 float strength = step (0.8 , mod (vUv.x * 10.0 , 1.0 )) ;strength *= step (0.8 , mod (vUv.y * 10.0 , 1.0 )) ; gl_FragColor =vec4 (strength,strength,strength,1 );
条纹相减 1 2 3 float strength = step (0.8 , mod (vUv.x * 10.0 , 1.0 )) ;strength -= step (0.8 , mod (vUv.y * 10.0 , 1.0 )) ; gl_FragColor =vec4 (strength,strength,strength,1 );
方块图形 1 2 3 float strength = step (0.2 , mod (vUv.x * 10.0 , 1.0 )) ; strength *= step (0.2 , mod (vUv.y * 10.0 , 1.0 )) ; gl_FragColor =vec4 (strength,strength,strength,1 );
T型图 1 2 3 4 5 float barX = step (0.4 , mod (vUv.x * 10.0 - 0.2 , 1.0 ))*step (0.8 , mod (vUv.y * 10.0 , 1.0 )) ; float barY = step (0.4 , mod (vUv.y * 10.0 , 1.0 ))*step (0.8 , mod (vUv.x * 10.0 , 1.0 )) ; float strength = barX+barY; gl_FragColor =vec4 (strength,strength,strength,1 );
利用绝对值 1 2 float strength = abs (vUv.x - 0.5 ) ;gl_FragColor =vec4 (strength,strength,strength,1 );
取2个值的最小值 1 2 float strength =min (abs (vUv.x - 0.5 ), abs (vUv.y - 0.5 )) ;gl_FragColor =vec4 (strength,strength,strength,1 );
取2个值的最大值 1 2 float strength =max (abs (vUv.x - 0.5 ), abs (vUv.y - 0.5 )) ;gl_FragColor =vec4 (strength,strength,strength,1 );
step 1 2 float strength =step (0.2 ,max (abs (vUv.x - 0.5 ), abs (vUv.y - 0.5 ))) ; gl_FragColor =vec4 (strength,strength,strength,1 );
小正方形 1 2 float strength =1.0 -step (0.2 ,max (abs (vUv.x - 0.5 ), abs (vUv.y - 0.5 ))) ; gl_FragColor =vec4 (strength,strength,strength,1 );
利用取整,实现条纹渐变 1 2 3 4 5 6 float strength = floor (vUv.x*10.0 )/10.0 ;gl_FragColor =vec4 (strength,strength,strength,1 );
条纹相乘,实现渐变格子 1 2 3 float strength = floor (vUv.x*10.0 )/10.0 *floor (vUv.y*10.0 )/10.0 ; gl_FragColor =vec4 (strength,strength,strength,1 );
向上取整 1 2 3 float strength = ceil (vUv.x*10.0 )/10.0 *ceil (vUv.y*10.0 )/10.0 ;gl_FragColor =vec4 (strength,strength,strength,1 );
随机效果 这些shader方法可以通过bookshader去查找bookshader 1 2 3 4 5 6 7 float random (vec2 st) { return fract (sin (dot (st.xy,vec2 (12.9898 ,78.233 )))*43758.5453123 ); } float strength = random(vUv); gl_FragColor =vec4 (strength,strength,strength,1 );
随机+格子效果 1 2 3 4 5 float strength = ceil (vUv.x*10.0 )/10.0 *ceil (vUv.y*10.0 )/10.0 ; strength = random(vec2 (strength,strength)); gl_FragColor =vec4 (strength,strength,strength,1 );
依据length返回向量长度 1 2 float strength = length (vUv);gl_FragColor =vec4 (strength,strength,strength,1 );
根据distance技术2个向量的距离 1 2 float strength =1.0 - distance (vUv,vec2 (0.5 ,0.5 ));gl_FragColor =vec4 (strength,strength,strength,1 );
根据相除,实现星星 1 2 float strength =0.15 / distance (vUv,vec2 (0.5 ,0.5 )) - 1.0 ;gl_FragColor =vec4 (strength,strength,strength,strength);
设置vUv水平或者竖直变量 1 2 float strength =0.15 / distance (vec2 (vUv.x,(vUv.y-0.5 )*5.0 ),vec2 (0.5 ,0.5 )) - 1.0 ;gl_FragColor =vec4 (strength,strength,strength,strength);
十字交叉的星星 1 2 float strength =0.15 / distance (vec2 (vUv.x,(vUv.y-0.5 )*5.0 ),vec2 (0.5 ,0.5 )) - 1.0 ;gl_FragColor =vec4 (strength,strength,strength,strength);
旋转飞镖,旋转uv 1 2 3 4 vec2 rotateUv = rotate(vUv,-uTime*5.0 ,vec2 (0.5 )); float strength = 0.15 / distance (vec2 (rotateUv.x,(rotateUv.y-0.5 )*5.0 +0.5 ),vec2 (0.5 ,0.5 )) - 1.0 ; strength += 0.15 / distance (vec2 (rotateUv.y,(rotateUv.x-0.5 )*5.0 +0.5 ),vec2 (0.5 ,0.5 )) - 1.0 ; gl_FragColor =vec4 (strength,strength,strength,strength);
狗皮膏药 1 2 float strength = step (0.5 ,distance (vUv,vec2 (0.5 ))+0.25 ) ;gl_FragColor =vec4 (strength,strength,strength,1 );
绘制圆 1 2 float strength = 1.0 - step (0.5 ,distance (vUv,vec2 (0.5 ))+0.25 ) ;gl_FragColor =vec4 (strength,strength,strength,1 );
圆环 1 2 3 float strength = step (0.5 ,distance (vUv,vec2 (0.5 ))+0.35 ) ;strength *= (1.0 - step (0.5 ,distance (vUv,vec2 (0.5 ))+0.25 )) ; gl_FragColor =vec4 (strength,strength,strength,1 );
渐变环 1 2 float strength = abs (distance (vUv,vec2 (0.5 ))-0.25 ) ;gl_FragColor =vec4 (strength,strength,strength,1 );
打靶 1 2 float strength = step (0.1 ,abs (distance (vUv,vec2 (0.5 ))-0.25 )) ;gl_FragColor =vec4 (strength,strength,strength,1 );
圆环 1 2 float strength = 1.0 - step (0.1 ,abs (distance (vUv,vec2 (0.5 ))-0.25 )) ;gl_FragColor =vec4 (strength,strength,strength,1 );
波浪环1 1 2 3 4 5 6 vec2 waveUv = vec2 ( vUv.x, vUv.y+sin (vUv.x*30.0 )*0.1 ); float strength = 1.0 - step (0.01 ,abs (distance (waveUv,vec2 (0.5 ))-0.25 )) ;gl_FragColor =vec4 (strength,strength,strength,1 );
波浪环2 1 2 3 4 5 6 vec2 waveUv = vec2 ( vUv.x+sin (vUv.y*30.0 )*0.1 , vUv.y+sin (vUv.x*30.0 )*0.1 ); float strength = 1.0 - step (0.01 ,abs (distance (waveUv,vec2 (0.5 ))-0.25 )) ;gl_FragColor =vec4 (strength,strength,strength,1 );
波浪环3 1 2 3 4 5 6 vec2 waveUv = vec2 ( vUv.x+sin (vUv.y*100.0 )*0.1 , vUv.y+sin (vUv.x*100.0 )*0.1 ); float strength = 1.0 - step (0.01 ,abs (distance (waveUv,vec2 (0.5 ))-0.25 )) ;gl_FragColor =vec4 (strength,strength,strength,1 );
根据角度显示视图 1 2 3 float angle = atan (vUv.x,vUv.y);float strength = angle;gl_FragColor =vec4 (strength,strength,strength,1 );
根据角度实现螺旋渐变 1 2 3 float angle = atan (vUv.x-0.5 ,vUv.y-0.5 );float strength = (angle+3.14 )/6.28 ;gl_FragColor =vec4 (strength,strength,strength,1 );
实现雷达扫射 1 2 3 4 float alpha = 1.0 - step (0.5 ,distance (vUv,vec2 (0.5 )));float angle = atan (vUv.x-0.5 ,vUv.y-0.5 );float strength = (angle+3.14 )/6.28 ;gl_FragColor =vec4 (strength,strength,strength,alpha);
通过时间实现动态 1 2 3 4 5 6 7 8 9 10 11 12 vec2 rotate(vec2 uv, float rotation, vec2 mid){ return vec2 ( cos (rotation) * (uv.x - mid.x) + sin (rotation) * (uv.y - mid.y) + mid.x, cos (rotation) * (uv.y - mid.y) - sin (rotation) * (uv.x - mid.x) + mid.y ); } vec2 rotateUv = rotate(vUv,-uTime*5.0 ,vec2 (0.5 )); float alpha = 1.0 - step (0.5 ,distance (vUv,vec2 (0.5 ))); float angle = atan (rotateUv.x-0.5 ,rotateUv.y-0.5 ); float strength = (angle+3.14 )/6.28 ; gl_FragColor =vec4 (strength,strength,strength,alpha);
万花筒 define PI 3.1415926535897932384626433832795 1 2 3 4 float angle = atan (vUv.x-0.5 ,vUv.y-0.5 )/PI; float strength = mod (angle*10.0 ,1.0 ); gl_FragColor =vec4 (strength,strength,strength,1 );
光芒四射 define PI 3.1415926535897932384626433832795 1 2 3 4 float angle = atan (vUv.x-0.5 ,vUv.y-0.5 )/(2.0 *PI);float strength = sin (angle*100.0 );gl_FragColor =vec4 (strength,strength,strength,1 );
使用噪声实现烟雾、波纹效果 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 float noise (in vec2 _st) { vec2 i = floor (_st); vec2 f = fract (_st); float a = random(i); float b = random(i + vec2 (1.0 , 0.0 )); float c = random(i + vec2 (0.0 , 1.0 )); float d = random(i + vec2 (1.0 , 1.0 )); vec2 u = f * f * (3.0 - 2.0 * f); return mix (a, b, u.x) + (c - a)* u.y * (1.0 - u.x) + (d - b) * u.x * u.y; } float strength = step (0.5 ,noise(vUv * 100.0 )) ;gl_FragColor =vec4 (strength,strength,strength,1 );
根据时间设置波形 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 vec4 permute(vec4 x){ return mod (((x*34.0 )+1.0 )*x, 289.0 ); } vec2 fade(vec2 t){ return t*t*t*(t*(t*6.0 -15.0 )+10.0 ); } float cnoise(vec2 P){ vec4 Pi = floor (P.xyxy) + vec4 (0.0 , 0.0 , 1.0 , 1.0 ); vec4 Pf = fract (P.xyxy) - vec4 (0.0 , 0.0 , 1.0 , 1.0 ); Pi = mod (Pi, 289.0 ); vec4 ix = Pi.xzxz; vec4 iy = Pi.yyww; vec4 fx = Pf.xzxz; vec4 fy = Pf.yyww; vec4 i = permute(permute(ix) + iy); vec4 gx = 2.0 * fract (i * 0.0243902439 ) - 1.0 ; vec4 gy = abs (gx) - 0.5 ; vec4 tx = floor (gx + 0.5 ); gx = gx - tx; vec2 g00 = vec2 (gx.x,gy.x); vec2 g10 = vec2 (gx.y,gy.y); vec2 g01 = vec2 (gx.z,gy.z); vec2 g11 = vec2 (gx.w,gy.w); vec4 norm = 1.79284291400159 - 0.85373472095314 * vec4 (dot (g00, g00), dot (g01, g01), dot (g10, g10), dot (g11, g11)); g00 *= norm.x; g01 *= norm.y; g10 *= norm.z; g11 *= norm.w; float n00 = dot (g00, vec2 (fx.x, fy.x)); float n10 = dot (g10, vec2 (fx.y, fy.y)); float n01 = dot (g01, vec2 (fx.z, fy.z)); float n11 = dot (g11, vec2 (fx.w, fy.w)); vec2 fade_xy = fade(Pf.xy); vec2 n_x = mix (vec2 (n00, n01), vec2 (n10, n11), fade_xy.x); float n_xy = mix (n_x.x, n_x.y, fade_xy.y); return 2.3 * n_xy; } float strength = step (uScale,cnoise(vUv * 10.0 +uTime)) ; gl_FragColor =vec4 (strength,strength,strength,1 );
发光路径 1 2 3 float strength = abs (cnoise(vUv * 10.0 )) ;gl_FragColor =vec4 (strength,strength,strength,1 );
1 2 3 float strength = 1.0 -abs (cnoise(vUv * 10.0 )) ;gl_FragColor =vec4 (strength,strength,strength,1 );
波纹效果 1 2 float strength = sin (cnoise(vUv * 10.0 )*5.0 +uTime) ;gl_FragColor =vec4 (strength,strength,strength,1 );
1 2 float strength = step (0.9 ,sin (cnoise(vUv * 10.0 )*20.0 )) ;gl_FragColor =vec4 (strength,strength,strength,1 );
使用混合函数混颜色 1 2 3 4 5 6 7 8 9 vec3 purpleColor = vec3 (1.0 , 0.0 , 1.0 );vec3 greenColor = vec3 (1.0 , 1.0 , 1.0 );vec3 uvColor = vec3 (vUv,1.0 );float strength = step (0.9 ,sin (cnoise(vUv * 10.0 )*20.0 )) ;vec3 mixColor = mix (greenColor,uvColor,strength);gl_FragColor =vec4 (mixColor,1.0 );
拖更了几天的货,是不是很大,哈哈,今天就分享到这里了,下次再见。