首页
视频
资源
登录
原
Three js 着色器材质 shader
1838
人阅读
2023/7/20 11:33
总访问:
2273142
评论:
0
收藏:
0
手机
分类:
前端
 >#Three js 着色器材质 shader [TOC] 什么是 GLSL? ------------ tn2>GLSL 代表 openGL Shading Language,它是着色器程序的特定标准,您将在接下来的章节中看到。根据硬件和操作系统,还有其他类型的着色器。在这里,我们将使用由Khronos Group监管的 openGL 规范。了解 OpenGL 的历史有助于理解其大部分奇怪的约定,为此我建议您查看:openglbook.com/chapter-0-preface-what-is-opengl.html 着色器材质(ShaderMaterial) ------------ tn2>使用自定义shader渲染的材质。 shader是一个用GLSL编写的小程序 ,在GPU上运行。 您可能需要使用自定义shader,如果你要: ——要实现内置 materials 之外的效果。 ——将许多对象组合成单个BufferGeometry以提高性能。 tn>ShaderMaterial 只有使用 WebGLRenderer 才可以绘制正常, 因为 `vertexShader` 和 `fragmentShader` 属性中GLSL代码必须使用WebGL来编译并运行在GPU中。 顶点着色器和片元着色器 ------------ ### 什么是顶点着色器(vertexShader) tn2>它接收attributes, 计算/操纵每个单独顶点的位置,并将其他数据(varyings)传递给片元着色器。 ### 什么是片元着色器(fragmentShader) tn2>它设置渲染到屏幕的每个单独的片元(像素)的颜色。 ### 举例 tn2>我们通过自定义的着色器渲染一个平面材质。 | 变量 | 描述 | | ------------ | ------------ | | `gl_Position` | OpenGL中的一个内置变量,用于表示顶点在裁剪空间中的位置。 | | `vec4` | 表示一个四维向量,由x、y、z和w四个分量组成 。 | | `position` | 表示当前的位置 | | `gl_FragColor` | 是OpenGL中的一个内置变量,用于设置片元的颜色 。它是一个vec4类型的变量,由x、y、z和w四个分量组成 。其中,x、y、z分量表示颜色的红、绿、蓝分量,w分量表示透明度 。 | tn2>这里的`gl_FragColor`我设置的是黄色的面板。 ```javascript // 创建着色器材质 const shaderMaterial = new THREE.ShaderMaterial({ // 顶点着色器 vertexShader: ` void main(){ gl_Position = 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.PlaneGeometry(1, 1, 64, 64), shaderMaterial ); console.log(floor); // 添加 scene.add(floor); ```  tn2>但这样会发现个问题,由于坐标是一成不变的所以不论你如何移动旋转它将都这样显示,所以的我们在旋转移动鼠标的同时还需要乘以如下几个矩阵:`<投影矩阵>*<视图矩阵>*<模型矩阵>*<顶点坐标>` 代码改成如下所示,再次运行 ```javascript gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ; ```  tn2>相乘的顺序不能改变。物体本身拥有一个坐标系,叫本地坐标系。把物体放到世界坐标系中,采用了模型矩阵,就是执行缩放、平移、旋转操作的过程。此时物体就具有了世界坐标系。 再加入上帝之眼,就是视图矩阵,包括视点坐标,观察点坐标,和上方向。现在只差最后一步–投影矩阵,物体就可以呈现出来了。 目前显示设备都是二维平面的,所以需要投影矩阵来转换物体。投影矩阵通常分为平行投影和透视投影。   | 名称 | 变量 | | ------------ | ------------ | | 投影矩阵 | projectionMatrix | | 视图矩阵 | viewMatrix | | 模型矩阵 | modelMatrix | 完整代码 ------------ ```javascript import * as THREE from "three"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; import gsap from "gsap"; import * as dat from "dat.gui"; // 目标:认识shader //创建gui对象 const gui = new dat.GUI(); // console.log(THREE); // 初始化场景 const scene = new THREE.Scene(); // 创建透视相机 const camera = new THREE.PerspectiveCamera( 90, window.innerHeight / window.innerHeight, 0.1, 1000 ); // 设置相机位置 // object3d具有position,属性是1个3维的向量 camera.position.set(0, 0, 2); // 更新摄像头 camera.aspect = window.innerWidth / window.innerHeight; // 更新摄像机的投影矩阵 camera.updateProjectionMatrix(); scene.add(camera); // 加入辅助轴,帮助我们查看3维坐标轴 const axesHelper = new THREE.AxesHelper(5); scene.add(axesHelper); // 加载纹理 // 创建纹理加载器对象 const textureLoader = new THREE.TextureLoader(); const texture = textureLoader.load("./texture/ca.jpeg"); const params = { uFrequency: 10, uScale: 0.1, }; // const material = new THREE.MeshBasicMaterial({ color: "#00ff00" }); // 创建着色器材质 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.PlaneGeometry(1, 1, 64, 64), shaderMaterial ); console.log(floor); scene.add(floor); // 初始化渲染器 const renderer = new THREE.WebGLRenderer({ alpha: true }); // renderer.shadowMap.enabled = true; // renderer.shadowMap.type = THREE.BasicShadowMap; // renderer.shadowMap.type = THREE.VSMShadowMap; // 设置渲染尺寸大小 renderer.setSize(window.innerWidth, window.innerHeight); // 监听屏幕大小改变的变化,设置渲染的尺寸 window.addEventListener("resize", () => { // console.log("resize"); // 更新摄像头 camera.aspect = window.innerWidth / window.innerHeight; // 更新摄像机的投影矩阵 camera.updateProjectionMatrix(); // 更新渲染器 renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器的像素比例 renderer.setPixelRatio(window.devicePixelRatio); }); // 将渲染器添加到body document.body.appendChild(renderer.domElement); // 初始化控制器 const controls = new OrbitControls(camera, renderer.domElement); // 设置控制器阻尼 controls.enableDamping = true; // 设置自动旋转 // controls.autoRotate = true; const clock = new THREE.Clock(); function animate(t) { const elapsedTime = clock.getElapsedTime(); // console.log(elapsedTime); requestAnimationFrame(animate); // 使用渲染器渲染相机看这个场景的内容渲染出来 renderer.render(scene, camera); } animate(); ``` tn2>index.html ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <link rel="stylesheet" href="./assets/css/style.css" /> </head> <body> <script src="./main/main.js" type="module"></script> </body> </html> ``` tn2>style.css ```css * { margin: 0; padding: 0; } body { background-color: #1e1a20; } ::-webkit-scrollbar { display: none; } .page { display: flex; justify-content: center; height: 100vh; padding: 0 10%; color: #fff; flex-direction: column; } .page h1 { margin: 60px 0; font-size: 40px; } .page h3 { font-size: 30px; } ``` 着色器插件安装与导入 ------------ tn2>由于OpenGL是一个单独的语言,所以我们更希望它能够单独弄成一个`.glsl`文件,在需要的时候进行导入。 首先我们创建`shader/basic`文件夹,并创建相关的文件分别存放顶点着色器和片元着色器的代码。  tn2>fragment.glsl ```javascript void main(){ gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); } ``` tn2>vertex.glsl ```javascript void main(){ gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ; } ``` tn2>在main.js中我们对其进行导入,修改成如下代码。 ```javascript // 顶点着色器 import basicVertexShader from "../shader/basic/vertex.glsl"; // 片元着色器 import basicFragmentShader from "../shader/basic/fragment.glsl"; ... const shaderMaterial = new THREE.ShaderMaterial({ // 顶点着色器 vertexShader: basicVertexShader, // 片元着色器 fragmentShader: basicFragmentShader, }); ``` tn2>效果与前面运行一样。 如果想对gl的代码有所高亮显示,可以安装该插件。   原始着色器材质(RawShaderMaterial) ------------ tn2>此类的工作方式与ShaderMaterial类似,不同之处在于内置的uniforms和attributes的定义不会自动添加到GLSL shader代码中。 (修改成如下代码) ```javascript // 顶点着色器 import basicVertexShader from "../shader/raw/vertex.glsl"; // 片元着色器 import basicFragmentShader from "../shader/raw/fragment.glsl"; ... const rawshaderMaterial = new THREE.RawShaderMaterial({ // 顶点着色器 vertexShader: basicVertexShader, // 片元着色器 fragmentShader: basicFragmentShader, }); // 创建平面 const floor = new THREE.Mesh( new THREE.PlaneGeometry(1, 1, 64, 64), rawshaderMaterial ); ``` tn2>注意这里我更改了glsl的路径,并在相应的路径下创建了文件,需要注意的是需要自行定义变量。 ```javascript // fragment.glsl void main(){ gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); } ``` ```javascript attribute vec3 position; uniform mat4 modelMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; void main(){ gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ; } ```  ### 着色器材质的变量 tn2>每个着色器材质都可以指定两种不同类型的shaders,他们是顶点着色器和片元着色器(Vertex shaders and fragment shaders)。 ● 顶点着色器首先运行; 它接收attributes, 计算/操纵每个单独顶点的位置,并将其他数据(varyings)传递给片元着色器。 ● 片元(或像素)着色器后运行; 它设置渲染到屏幕的每个单独的“片元”(像素)的颜色。 shader中有三种类型的变量: uniforms, attributes, 和 varyings ● Uniforms是所有顶点都具有相同的值的变量。 比如灯光,雾,和阴影贴图就是被储存在uniforms中的数据。 uniforms可以通过顶点着色器和片元着色器来访问。 ● Attributes 与每个顶点关联的变量。例如,顶点位置,法线和顶点颜色都是存储在attributes中的数据。attributes 只 可以在顶点着色器中访问。 ● Varyings 是从顶点着色器传递到片元着色器的变量。对于每一个片元,每一个varying的值将是相邻顶点值的平滑插值。 ### 着色器传值 tn2>如何从顶点着色器的值传入到片元着色器中? 举例我们可以通过定义uv变量来进行传值。 ```javascript // vertex.glsl // 设置绘制的高低精度 precision lowp float; attribute vec3 position; attribute vec2 uv; uniform mat4 modelMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; varying vec2 vUv; // 设置精度 // highp -2^16 ~ 2^16 // mediump -2^10 ~ 2^10 // lowp -2^8 ~ 2^8 void main(){ vUv = uv; gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ; } ``` ```javascript // fragment.glsl // 设置绘制的高低精度 precision lowp float; varying vec2 vUv; void main(){ gl_FragColor = vec4(vUv, 0.0, 1.0); } ```  ### 设置线框 tn2>通过设置原始着色器的`wireframe`属性为true可以显示图像的像框。 ```javascript // 创建着色器材质 const rawshaderMaterial = new THREE.RawShaderMaterial({ // 顶点着色器 vertexShader: basicVertexShader, // 片元着色器 fragmentShader: basicFragmentShader, // 设置线框模式 wireframe: true, }); // 创建平面 const floor = new THREE.Mesh( new THREE.PlaneGeometry(1, 1), rawshaderMaterial ); ```  ### 设置图像绘制的坐标 tn2>设置Position位置x为1,z为1的情况下如何进行显示。 修改vertex.glsl ```javascript // 设置绘制的高低精度 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 ); modelPosition.x += 1.0; modelPosition.z += 1.0; gl_Position = projectionMatrix * viewMatrix * modelPosition ; } ```  ### 设置旋转 ```javascript void main(){ vUv = uv; vec4 modelPosition = modelMatrix * vec4( position, 1.0 ); // modelPosition.x += 1.0; // modelPosition.z += 1.0; modelPosition.z += modelPosition.x; gl_Position = projectionMatrix * viewMatrix * modelPosition ; } ```  tn2>或者我们可以使用一些数学函数,例如:`sin()` ```javascript // 或数学函数 modelPosition.z += sin(modelPosition.x* 10.0); ``` ### 绘制水平弯曲波浪形状 ```javascript void main(){ vUv = uv; vec4 modelPosition = modelMatrix * vec4( position, 1.0 ); // 或数学函数 modelPosition.z += sin(modelPosition.x * 100.0)*0.1; gl_Position = projectionMatrix * viewMatrix * modelPosition ; } ``` ```javascript const rawshaderMaterial = new THREE.RawShaderMaterial({ // 顶点着色器 vertexShader: basicVertexShader, // 片元着色器 fragmentShader: basicFragmentShader, // 设置线框模式 // wireframe: true, // 传递纹理 side: THREE.DoubleSide, }); ```  ### 绘制y轴的玻璃形状 ```javascript void main(){ vUv = uv; vec4 modelPosition = modelMatrix * vec4( position, 1.0 ); // 或数学函数 modelPosition.z += sin(modelPosition.x * 10.0)*0.05; // 加上y轴的波动 modelPosition.z += sin(modelPosition.y * 10.0)*0.05; gl_Position = projectionMatrix * viewMatrix * modelPosition ; } ```  tn2>通过传入z的位置来设置颜色。 ```javascript // 设置绘制的高低精度 precision lowp float; attribute vec3 position; attribute vec2 uv; uniform mat4 modelMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; varying vec2 vUv; varying float vElevation; // 设置精度 // highp -2^16 ~ 2^16 // mediump -2^10 ~ 2^10 // lowp -2^8 ~ 2^8 void main(){ vUv = uv; vec4 modelPosition = modelMatrix * vec4( position, 1.0 ); // 或数学函数 modelPosition.z += sin(modelPosition.x * 10.0)*0.05; // 加上y轴的波动 modelPosition.z += sin(modelPosition.y * 10.0)*0.05; vElevation = modelPosition.z; gl_Position = projectionMatrix * viewMatrix * modelPosition ; } ``` ```javascript // 设置绘制的高低精度 precision lowp float; varying vec2 vUv; varying float vElevation; void main(){ // gl_FragColor = vec4(vUv, 0.0, 1.0); float letvElevation = vElevation + 0.05 * 10.0; gl_FragColor = vec4(1.0*letvElevation,0.0, 0.0, 1.0); } ```  让图像动起来! ------------ tn2>通过在传入时间变量,并更具时间变量的不同的移动它的z轴,让其产生波动的效果。 ```javascript const rawshaderMaterial = new THREE.RawShaderMaterial({ // 顶点着色器 vertexShader: basicVertexShader, // 片元着色器 fragmentShader: basicFragmentShader, // 设置线框模式 // wireframe: true, // 传递纹理 side: THREE.DoubleSide, // 传递参数 uniforms: { uTime: { value: 0 }, }, }); ... function animate(t) { const elapsedTime = clock.getElapsedTime(); // 传入时间 rawshaderMaterial.uniforms.uTime.value = elapsedTime; requestAnimationFrame(animate); // 使用渲染器渲染相机看这个场景的内容渲染出来 renderer.render(scene, camera); } ``` ```javascript // 获取时间 uniform float uTime; ... // 或数学函数 modelPosition.z += sin((modelPosition.x + uTime) * 10.0)*0.05; // 加上y轴的波动 modelPosition.z += sin((modelPosition.y + uTime) * 10.0)*0.05; ```  tn2>我们再添加2022残奥会的图片,在`fragment.glsl`文件中进行修改并添加纹理。  ```javascript import image from '../assets/ca.jpeg' ... const texture = textureLoader.load(image); ... const rawshaderMaterial = new THREE.RawShaderMaterial({ ... // 传递参数 uniforms: { uTime: { value: 0 }, uTexture: { value: texture, }, }, }); ``` ```javascript // 设置绘制的高低精度 precision lowp float; varying vec2 vUv; varying float vElevation; uniform sampler2D uTexture; void main(){ // 根据UV,取出对应的颜色 vec4 textureColor = texture2D(uTexture, vUv); float letvElevation = vElevation + 0.05 * 10.0; textureColor.rgb *= letvElevation; gl_FragColor = textureColor; //vec4(textureColor.rgb, 1.0); } ``` tn>需要注意的是,在进行build后图片的路径可能会有所改变导致加载不了图片。 
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739
👈{{preArticle.title}}
👉{{nextArticle.title}}
评价
{{titleitem}}
{{titleitem}}
{{item.content}}
{{titleitem}}
{{titleitem}}
{{item.content}}
尘叶心繁
这一世以无限游戏为使命!
博主信息
排名
6
文章
6
粉丝
16
评论
8
文章类别
.net后台框架
164篇
linux
17篇
linux中cve
1篇
windows中cve
0篇
资源分享
10篇
Win32
3篇
前端
28篇
传说中的c
4篇
Xamarin
9篇
docker
15篇
容器编排
101篇
grpc
4篇
Go
15篇
yaml模板
1篇
理论
2篇
更多
Sqlserver
4篇
云产品
39篇
git
3篇
Unity
1篇
考证
2篇
RabbitMq
23篇
Harbor
1篇
Ansible
8篇
Jenkins
17篇
Vue
1篇
Ids4
18篇
istio
1篇
架构
2篇
网络
7篇
windbg
4篇
AI
17篇
threejs
2篇
人物
1篇
嵌入式
2篇
python
10篇
HuggingFace
8篇
pytorch
1篇
最新文章
最新评价
{{item.articleTitle}}
{{item.blogName}}
:
{{item.content}}
关于我们
ICP备案 :
渝ICP备18016597号-1
网站信息:
2018-2023
TNBLOG.NET
技术交流:
群号656732739
联系我们:
contact@tnblog.net
欢迎加群
欢迎加群交流技术