【Threejs进阶教程-着色器篇】9.顶点着色器入门

【Threejs进阶教程-着色器篇】9.顶点着色器入门

  • 本系列教程第一篇地址,建议按顺序学习
  • 认识顶点着色器
    • varying介绍
    • 顶点着色器与片元着色器分别的作用
    • Threejs在Shader中的内置变量
    • 各种矩阵
    • gl_Position
  • 尝试使用顶点着色器
    • 增加分段数增强效果
  • 制作平面鼓包效果
    • 鼓包效果分析
    • 路障效果
    • 让路障效果变得圆滑
    • uniform 控制鼓包效果
  • 完整源码

本系列教程第一篇地址,建议按顺序学习

本系列目前已累计第九篇,这里直接省略了2到8篇,可以通过上方专栏来查阅前面的教程
【Threejs进阶教程-着色器篇】1. Shader入门(ShadertoyShader和ThreejsShader入门)

本篇使用到的模板代码,从这里自取一个shader模板代码即可
【模板代码】用于编写Threejs Demo的模板代码

认识顶点着色器

首先我们把着色器部分的代码拎出来逐一分析

<script type="x-shader/x-vertex" id="vertexShader">varying vec2 vUv;void main(){vUv = vec2(uv.x,uv.y);vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );gl_Position = projectionMatrix * mvPosition;}</script>
<script type="x-shader/x-fragment" id="fragmentShader">varying vec2 vUv;void main(){gl_FragColor = vec4(1.0,0.0,0.0,1.0);}
</script>

varying介绍

有人发现顶点着色器的第一行与片元着色器的第一行,完全一致,那么这个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 type="x-shader/x-vertex" id="vertexShader">varying 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 type="x-shader/x-vertex" id="vertexShader">varying 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 type="x-shader/x-vertex" id="vertexShader">varying vec2 vUv;void main(){vUv = vec2(uv.x,uv.y);vec3 aPosition = position;//0 为int类型,0.0为float类型, 如果写0,threejs会报错// 可以写成 .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 type="x-shader/x-vertex" id="vertexShader">varying vec2 vUv;void main(){vUv = vec2(uv.x,uv.y);vec3 aPosition = position;//0 为int类型,0.0为float类型, 如果写0,threejs会报错// 可以写成 .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;}
</script>

在这里插入图片描述

uniform 控制鼓包效果

uniform亦可用于顶点着色器

我们在代码中多次使用到5.0,这个实际上是鼓包的最大高度,这里我们抽出来这个常数作为鼓包最大高度,指数函数用的2次幂,这个参数可以抽出一个参数为鼓包圆滑率,我们写到uniform和lil.gui来调试
当然,我们的鼓包中心点,也可以单独拎出来放到uniform中

顶点着色器中编写uniform与片元着色器基本一致

修改后的顶点着色器

<script type="x-shader/x-vertex" id="vertexShader">varying 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 lang="en">
<head><meta charset="UTF-8"><title>Title</title><style>body{width:100vw;height: 100vh;overflow: hidden;margin: 0;padding: 0;border: 0;}</style>
</head>
<body><script type="importmap">{"imports": {"three": "../three/build/three.module.js","three/addons/": "../three/examples/jsm/"}}</script><script type="x-shader/x-vertex" id="vertexShader">varying 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 type="x-shader/x-fragment" id="fragmentShader">varying vec2 vUv;void main(){gl_FragColor = vec4(1.0,0.0,0.0,1.0);}
</script><script type="module">import * 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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/479908.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

w058基于web的美发门店管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0…

leetcode 二叉树的最大深度

104. 二叉树的最大深度 已解答 简单 相关标签 相关企业 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3…

VMware ubuntu创建共享文件夹与Windows互传文件

1.如图1所示&#xff0c;点击虚拟机&#xff0c;点击设置&#xff1b; 图1 2.如图2所示&#xff0c;点击选项&#xff0c;点击共享文件夹&#xff0c;如图3所示&#xff0c;点击总是启用&#xff0c;点击添加&#xff1b; 图2 图3 3.如图4所示&#xff0c;出现命名共享文件夹…

matlab 实现混沌麻雀搜索算法的光伏MPPT控制仿真

1、内容简介 略 103-可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略

Unity3D 截图

使用 Unity3D 自带的截图接口&#xff0c;制作截图工具。 截图 有时候我们想对 Unity 的窗口进行截图&#xff0c;如果直接使用一些截图工具&#xff0c;很难截取到一张完整分辨率的图片&#xff08;例如&#xff0c;我们想要截取一张 1920 * 1080 的图片&#xff09;。 其实…

STM32F10x 定时器

使用定时器实现&#xff1a;B5 E5的开关 添加相关的.h路径文件 添加相关的.c配置文件 led.h文件 用于声明LED函数 #ifndef __LED_H //没有定义__LED_H #define __LED_H //就定义__LED_H #define LED1_ON GPIO_ResetBits(GPIOB,GPIO_Pin_5) #defi…

PMP好考吗,有多大的价值?

非常好考&#xff01;PMP目前大陆地区的笔试是只有选择题的&#xff0c;运气好的话 蒙一个都能对&#xff0c;所以PMP的通过率高&#xff0c;这也是很多人考了吐槽PMP没用&#xff0c;是“水证”&#xff0c;但是每年考PMP 的人不减反增&#xff0c;大家可以想一下&#xff0c;…

css:项目

这是一个完整的网站制作的流程 美工会先制作一个原型图&#xff1a; 原型图写的不详细&#xff0c;就是体现一个网页大致的布局 然后美工再做一个psd样例图片 然后再交给程序员 项目 模块化开发&#xff1a;把代码的不同的样式封装起来&#xff0c;需要用到相同样式的标签就…

VsCode 插件推荐(个人常用)

VsCode 插件推荐&#xff08;个人常用&#xff09;

黑马程序员Java项目实战《苍穹外卖》Day01

苍穹外卖-day01 课程内容 软件开发整体介绍苍穹外卖项目介绍开发环境搭建导入接口文档Swagger 项目整体效果展示&#xff1a; ​ 管理端-外卖商家使用 ​ 用户端-点餐用户使用 当我们完成该项目的学习&#xff0c;可以培养以下能力&#xff1a; 1. 软件开发整体介绍 作为一…

Python双向链表、循环链表、栈

一、双向链表 1.作用 双向链表也叫双面链表。 对于单向链表而言。只能通过头节点或者第一个节点出发&#xff0c;单向的访问后继节点&#xff0c;每个节点只能记录其后继节点的信息&#xff08;位置&#xff09;&#xff0c;不能向前遍历。 所以引入双向链表&#xff0c;双…

k8s网络服务

k8s 中向外界提供服务的几种方法port-forward、NodePort&#xff0c;以及 更加常用的提供服务的资源ingress。 1 kubectl port-forward service/redis 6379:6379 现在k8s中有一个pod运行在6379&#xff0c;本机访问映射到6379上&#xff0c;它可以针对部署&#xff0c;服务&…

eduSRC挖洞思路

声明 学习视频来自 B 站UP主泷羽sec&#xff0c;如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识&#xff0c;以下网站只涉及学习内容&#xff0c;其他的都与本人无关&#xff0c;切莫逾越法律红线&#xff0c;否则后果自负。 ✍&#x1f3fb;作者简介&#xff1a;致…

Leetcode - 周赛424

目录 一&#xff0c;3354. 使数组元素等于零 二&#xff0c; 3355. 零数组变换 I 三&#xff0c;3356. 零数组变换 II 四&#xff0c;3357. 最小化相邻元素的最大差值 一&#xff0c;3354. 使数组元素等于零 本题实际上是一个前/后缀和的问题&#xff0c;就是判断前缀和与后…

Vue2中 vuex 的使用

1.安装 vuex 安装vuex与vue-router类似&#xff0c;vuex是一个独立存在的插件&#xff0c;如果脚手架初始化没有选 vuex&#xff0c;就需要额外安装。 yarn add vuex3 或者 npm i vuex3 233 Vue2 Vue-Router3 Vuex3 344 Vue3 Vue-Router4 Vuex4 2. 新建 store/index.j…

数据结构C语言描述5(图文结合)--队列,数组、链式、优先队列的实现

前言 这个专栏将会用纯C实现常用的数据结构和简单的算法&#xff1b;有C基础即可跟着学习&#xff0c;代码均可运行&#xff1b;准备考研的也可跟着写&#xff0c;个人感觉&#xff0c;如果时间充裕&#xff0c;手写一遍比看书、刷题管用很多&#xff0c;这也是本人采用纯C语言…

Windows修复SSL/TLS协议信息泄露漏洞(CVE-2016-2183) --亲测

漏洞说明&#xff1a; 打开链接&#xff1a;https://docs.microsoft.com/zh-cn/troubleshoot/windows-server/windows-security/restrict-cryptographic-algorithms-protocols-schannel 可以看到&#xff1a; 找到&#xff1a;应通过配置密码套件顺序来控制 TLS/SSL 密码 我们…

深度学习图像视觉 RKNN Toolkit2 部署 RK3588S边缘端 过程全记录

深度学习图像视觉 RKNN Toolkit2 部署 RK3588S边缘端 过程全记录 认识RKNN Toolkit2 工程文件学习路线&#xff1a; Anaconda Miniconda安装.condarc 文件配置镜像源自定义conda虚拟环境路径创建Conda虚拟环境 本地训练环境本地转换环境安装 RKNN-Toolkit2&#xff1a;添加 lin…

controller中的参数注解@Param @RequestParam和@RequestBody的不同

现在controller中有个方法&#xff1a;&#xff08;LoginUserRequest是一个用户类对象&#xff09; PostMapping("/test/phone")public Result validPhone(LoginUserRequest loginUserRequest) {return Result.success(loginUserRequest);}现在讨论Param("login…

Android按键点击事件三种实现方法

1. 在xml文件中为 Button 添加android:onclick属性 由于没有onclick这个函数&#xff0c;onclick下面会提示红色波浪线错误&#xff0c;然后单击一下"onclick"按住键盘上AltEnter键,选择在activity中生成函数 public void onclick(View view) {Toast.makeText(this,&…