新闻cms静态网站模板,网站后台对接表,大学网站建设公司,做网站要求的资料【Threejs进阶教程-着色器篇】9.顶点着色器入门 本系列教程第一篇地址#xff0c;建议按顺序学习认识顶点着色器varying介绍顶点着色器与片元着色器分别的作用Threejs在Shader中的内置变量各种矩阵gl_Position 尝试使用顶点着色器增加分段数增强效果 制作平面鼓包效果鼓包效果… 【Threejs进阶教程-着色器篇】9.顶点着色器入门 本系列教程第一篇地址建议按顺序学习认识顶点着色器varying介绍顶点着色器与片元着色器分别的作用Threejs在Shader中的内置变量各种矩阵gl_Position 尝试使用顶点着色器增加分段数增强效果 制作平面鼓包效果鼓包效果分析路障效果让路障效果变得圆滑uniform 控制鼓包效果 完整源码 本系列教程第一篇地址建议按顺序学习
本系列目前已累计第九篇这里直接省略了2到8篇可以通过上方专栏来查阅前面的教程 【Threejs进阶教程-着色器篇】1. Shader入门(ShadertoyShader和ThreejsShader入门)
本篇使用到的模板代码从这里自取一个shader模板代码即可 【模板代码】用于编写Threejs Demo的模板代码
认识顶点着色器
首先我们把着色器部分的代码拎出来逐一分析
script typex-shader/x-vertex idvertexShadervarying vec2 vUv;void main(){vUv vec2(uv.x,uv.y);vec4 mvPosition modelViewMatrix * vec4( position, 1.0 );gl_Position projectionMatrix * mvPosition;}/script
script typex-shader/x-fragment idfragmentShadervarying vec2 vUv;void main(){gl_FragColor vec4(1.0,0.0,0.0,1.0);}
/scriptvarying介绍
有人发现顶点着色器的第一行与片元着色器的第一行完全一致那么这个varying用来做什么的呢
这里我们直接借用webgl编程指南211页的介绍
varying主要用于 顶点着色器到片元着色器传输数据是个全局变量
现阶段我们只需要认识到varying声明的变量要在顶点着色器和片元着色器都要存在且初始值需要在顶点着色器中设置即可
了解了varying之后我们发现其实uv的值是从顶点着色器中传递给片元着色器的
顶点着色器与片元着色器分别的作用
现阶段我这里不打算讲渲染原理和管线那些太繁琐这里先简单的总结一句 后续在讲到后处理的时候这部分内容会再细讲
顶点着色器决定外型 片元着色器决定色彩
Threejs在Shader中的内置变量
顶点着色器中使用到了几个threejs的内置变量在下面的官方文档中有说明 Threejs内置变量-WebGLProgram 一般来说我们最需要关注的几个 position模型的顶点信息会传递到这里常用于计算模型的外观和最终渲染的外型 uv模型的uv信息会传递到这里常用于传递给顶点着色器用于计算颜色 normal模型的法线信息会传递到这里常用于计算光照等高级计算
如果看完了前面的BufferGeometry教程有没有发现这里很熟悉 【ThreeJS基础教程-高级几何体篇】2.6 BufferGeometry与BufferAttribute
你们想的没错这些就是threejs向shader系统传递的数据如果这里不懂BufferGeometry的要继续下去学习Shader就需要去前面补一下BufferGeometry的相关知识了
各种矩阵
在顶点着色器模板代码中的第四行第五行分别出现了modelViewMatrix和projectionMatrix这两个矩阵现阶段先不用管只需要记住顶点着色器最终计算是这样即可
现阶段顶点着色器的代码在模板代码的最后两行除了**vec4(position,1.0);**会稍作改变其他时间不会发生大的变动
gl_Position
一般来说顶点着色器也需要有个固定输出gl_Position就是顶点着色器的最终输出结果最终结果也是一个vec4类型的对象
这样顶点着色器的代码我们就介绍完毕了接下来我们要尝试修改一下顶点着色器感受一下顶点着色器带来的效果
尝试使用顶点着色器
我们用个最简单的方式来操作顶点
script typex-shader/x-vertex idvertexShadervarying vec2 vUv;void main(){vUv vec2(uv.x,uv.y);vec3 aPosition position;//这里我们直接操作顶点的z轴偏移的激进一点aPosition.z sin(aPosition.x * aPosition.y) * 10.0;vec4 mvPosition modelViewMatrix * vec4( aPosition , 1.0 );gl_Position projectionMatrix * mvPosition;}/script然后我们运行起来之后发现我们的平面扭曲了这是因为我们的z轴发生了改变
增加分段数增强效果
这里我们修改一下addMesh() function addMesh() {//增加到100分段let geometry new THREE.PlaneGeometry(10,10,3,3);let material new THREE.ShaderMaterial({uniforms,vertexShader:document.getElementById(vertexShader).textContent,fragmentShader:document.getElementById(fragmentShader).textContent,transparent:true})let mesh new THREE.Mesh(geometry,material);scene.add(mesh);}我们可以发现增加了分段和顶点之后我们的这个平面变化巨大已经不再是最初的PlaneGeometry了
这样我们就完成了一次顶点着色器的尝试
制作平面鼓包效果
首先我们把平面横过来然后需要做一个数据变换分段数增加到100然后设定材质的线框模式开不然我们等一下不好看到效果 然后修改回最初的顶点着色器代码
addMesh function addMesh() {//注意这里必须旋转几何体旋转了几何体我们的数据才是正确的//mesh.rotation是在矩阵层面修改了旋转方向最终会传递到modelViewMatrix中//顶点着色器的所有教程除非特殊说明否则全部使用旋转几何体let geometry new THREE.PlaneGeometry(10,10,100,100).rotateX(-Math.PI/2);let material new THREE.ShaderMaterial({uniforms,vertexShader:document.getElementById(vertexShader).textContent,fragmentShader:document.getElementById(fragmentShader).textContent,transparent:true,wireframe:true})let mesh new THREE.Mesh(geometry,material);scene.add(mesh);}顶点着色器
script typex-shader/x-vertex idvertexShadervarying vec2 vUv;void main(){vUv vec2(uv.x,uv.y);vec3 aPosition position;vec4 mvPosition modelViewMatrix * vec4( aPosition , 1.0 );gl_Position projectionMatrix * mvPosition;}
/script鼓包效果分析
既然要制作鼓包效果那么我们需要一个鼓包顶点然后鼓包顶点处的高度最高然后依次递减所以我们这里直接从顶点着色器来定义这个鼓包点
script typex-shader/x-vertex idvertexShadervarying vec2 vUv;void main(){vUv vec2(uv.x,uv.y);vec3 aPosition position;//0 为int类型0.0为float类型, 如果写0threejs会报错// 可以写成 .0 来替代0.0以及任何 0.X 的数字, 但是个人不是很喜欢这种写法,看着太混乱vec3 swelling vec3(0.0);//计算鼓包点到顶点的距离float dis distance(swelling,aPosition);dis clamp(dis,0.0,5.0);aPosition.y 5.0 - dis;vec4 mvPosition modelViewMatrix * vec4( aPosition , 1.0 );gl_Position projectionMatrix * mvPosition;}
/script路障效果
这里我们先声明了鼓包点在中心点然后我们计算鼓包点到四周的距离但是要做一下限制如果大于5.0的值则直接赋值为5.0紧接着直接把这个计算出来的dis值丢给aPosition.y我们得到了一个漏斗型 既然我们计算的dis的最大值为5那么我们把大小做一下交换即可用5.0 - dis即可把漏斗形改成路障型
让路障效果变得圆滑
这里我们使用指数函数来优化
我们现在知道了最高点是5最低点为0那么我们就可以计算它的高度比例然后把线性的比例换成指数型比例 保持最高点和最低点不变然后我们直接带入图像上面的数学公式即可得到我们的鼓包效果
script typex-shader/x-vertex idvertexShadervarying vec2 vUv;void main(){vUv vec2(uv.x,uv.y);vec3 aPosition position;//0 为int类型0.0为float类型, 如果写0threejs会报错// 可以写成 .0 来替代0.0以及任何 0.X 的数字, 但是个人不是很喜欢这种写法,看着太混乱vec3 swelling vec3(0.0);//计算鼓包点到顶点的距离float dis distance(swelling,aPosition);dis clamp(dis,0.0,5.0);dis pow( dis / 5.0, 2.0 ) * 5.0;aPosition.y 5.0 - dis;vec4 mvPosition modelViewMatrix * vec4( aPosition , 1.0 );gl_Position projectionMatrix * mvPosition;}
/scriptuniform 控制鼓包效果
uniform亦可用于顶点着色器
我们在代码中多次使用到5.0这个实际上是鼓包的最大高度这里我们抽出来这个常数作为鼓包最大高度指数函数用的2次幂这个参数可以抽出一个参数为鼓包圆滑率我们写到uniform和lil.gui来调试 当然我们的鼓包中心点也可以单独拎出来放到uniform中
顶点着色器中编写uniform与片元着色器基本一致
修改后的顶点着色器
script typex-shader/x-vertex idvertexShadervarying vec2 vUv;uniform float maxSwelling;uniform vec3 swellingCenter;uniform float swellingPower;void main(){vUv vec2(uv.x,uv.y);vec3 aPosition position;//计算鼓包点到顶点的距离float dis distance(swellingCenter,aPosition);dis clamp(dis,0.0,maxSwelling);dis pow( dis / maxSwelling, swellingPower ) * maxSwelling;aPosition.y maxSwelling - dis;vec4 mvPosition modelViewMatrix * vec4( aPosition , 1.0 );gl_Position projectionMatrix * mvPosition;}
/script修改后的addMesh()和uniforms
let uniforms {maxSwelling:{value:5.0},swellingCenter:{value:new THREE.Vector3()},swellingPower:{value:2.0}}function addMesh() {//注意这里必须旋转几何体旋转了几何体我们的数据才是正确的//mesh.rotation是在矩阵层面修改了旋转方向最终会传递到modelViewMatrix中//顶点着色器的所有教程除非特殊说明否则全部使用旋转几何体let geometry new THREE.PlaneGeometry(10,10,100,100).rotateX(-Math.PI/2);let material new THREE.ShaderMaterial({uniforms,vertexShader:document.getElementById(vertexShader).textContent,fragmentShader:document.getElementById(fragmentShader).textContent,transparent:true,wireframe:true})let mesh new THREE.Mesh(geometry,material);scene.add(mesh);//注意自己引入lil.guilet gui new GUI();gui.add(uniforms.maxSwelling,value,0,10).step(0.01).name(最大鼓包高度);gui.add(uniforms.swellingPower,value,0,10).step(0.01).name(鼓包曲线);let folder gui.addFolder(鼓包中心);folder.add(uniforms.swellingCenter.value,x,-5,5);folder.add(uniforms.swellingCenter.value,y,-5,5);folder.add(uniforms.swellingCenter.value,z,-5,5);}完整源码
!DOCTYPE html
html langen
headmeta charsetUTF-8titleTitle/titlestylebody{width:100vw;height: 100vh;overflow: hidden;margin: 0;padding: 0;border: 0;}/style
/head
bodyscript typeimportmap{imports: {three: ../three/build/three.module.js,three/addons/: ../three/examples/jsm/}}/scriptscript typex-shader/x-vertex idvertexShadervarying vec2 vUv;uniform float maxSwelling;uniform vec3 swellingCenter;uniform float swellingPower;void main(){vUv vec2(uv.x,uv.y);vec3 aPosition position;//计算鼓包点到顶点的距离float dis distance(swellingCenter,aPosition);dis clamp(dis,0.0,maxSwelling);dis pow( dis / maxSwelling, swellingPower ) * maxSwelling;aPosition.y maxSwelling - dis;vec4 mvPosition modelViewMatrix * vec4( aPosition , 1.0 );gl_Position projectionMatrix * mvPosition;}
/script
script typex-shader/x-fragment idfragmentShadervarying vec2 vUv;void main(){gl_FragColor vec4(1.0,0.0,0.0,1.0);}
/scriptscript typemoduleimport * as THREE from ../three/build/three.module.js;import {OrbitControls} from ../three/examples/jsm/controls/OrbitControls.js;import {GUI} from ../three/examples/jsm/libs/lil-gui.module.min.js;window.addEventListener(load,e{init();addMesh();render();})let scene,renderer,camera;let orbit;function init(){scene new THREE.Scene();renderer new THREE.WebGLRenderer({alpha:true,antialias:true});renderer.setSize(window.innerWidth,window.innerHeight);document.body.appendChild(renderer.domElement);camera new THREE.PerspectiveCamera(50,window.innerWidth/window.innerHeight,0.1,2000);camera.add(new THREE.PointLight());camera.position.set(15,15,15);scene.add(camera);orbit new OrbitControls(camera,renderer.domElement);orbit.enableDamping true;scene.add(new THREE.GridHelper(10,10));}let uniforms {maxSwelling:{value:5.0},swellingCenter:{value:new THREE.Vector3()},swellingPower:{value:2.0}}function addMesh() {//注意这里必须旋转几何体旋转了几何体我们的数据才是正确的//mesh.rotation是在矩阵层面修改了旋转方向最终会传递到modelViewMatrix中//顶点着色器的所有教程除非特殊说明否则全部使用旋转几何体let geometry new THREE.PlaneGeometry(10,10,100,100).rotateX(-Math.PI/2);let material new THREE.ShaderMaterial({uniforms,vertexShader:document.getElementById(vertexShader).textContent,fragmentShader:document.getElementById(fragmentShader).textContent,transparent:true,wireframe:true})let mesh new THREE.Mesh(geometry,material);scene.add(mesh);let gui new GUI();gui.add(uniforms.maxSwelling,value,0,10).step(0.01).name(最大鼓包高度);gui.add(uniforms.swellingPower,value,0,10).step(0.01).name(鼓包曲线);let folder gui.addFolder(鼓包中心);folder.add(uniforms.swellingCenter.value,x,-5,5);folder.add(uniforms.swellingCenter.value,y,-5,5);folder.add(uniforms.swellingCenter.value,z,-5,5);}function render() {renderer.render(scene,camera);orbit.update();requestAnimationFrame(render);}/script
/body
/html