tnblog
首页
视频
资源
登录

Three js 着色器材质 shader

6569人阅读 2023/7/20 11:33 总访问:3455820 评论:0 收藏:0 手机
分类: 前端

Three js 着色器材质 shader

什么是 GLSL?


GLSL 代表 openGL Shading Language,它是着色器程序的特定标准,您将在接下来的章节中看到。根据硬件和操作系统,还有其他类型的着色器。在这里,我们将使用由Khronos Group监管的 openGL 规范。了解 OpenGL 的历史有助于理解其大部分奇怪的约定,为此我建议您查看:openglbook.com/chapter-0-preface-what-is-opengl.html

着色器材质(ShaderMaterial)


使用自定义shader渲染的材质。 shader是一个用GLSL编写的小程序 ,在GPU上运行。 您可能需要使用自定义shader,如果你要:
——要实现内置 materials 之外的效果。
——将许多对象组合成单个BufferGeometry以提高性能。

ShaderMaterial 只有使用 WebGLRenderer 才可以绘制正常, 因为 vertexShaderfragmentShader 属性中GLSL代码必须使用WebGL来编译并运行在GPU中。

顶点着色器和片元着色器

什么是顶点着色器(vertexShader)


它接收attributes, 计算/操纵每个单独顶点的位置,并将其他数据(varyings)传递给片元着色器。

什么是片元着色器(fragmentShader)


它设置渲染到屏幕的每个单独的片元(像素)的颜色。

举例


我们通过自定义的着色器渲染一个平面材质。

变量 描述
gl_Position OpenGL中的一个内置变量,用于表示顶点在裁剪空间中的位置。
vec4 表示一个四维向量,由x、y、z和w四个分量组成 。
position 表示当前的位置
gl_FragColor 是OpenGL中的一个内置变量,用于设置片元的颜色 。它是一个vec4类型的变量,由x、y、z和w四个分量组成 。其中,x、y、z分量表示颜色的红、绿、蓝分量,w分量表示透明度 。


这里的gl_FragColor我设置的是黄色的面板。

  1. // 创建着色器材质
  2. const shaderMaterial = new THREE.ShaderMaterial({
  3. // 顶点着色器
  4. vertexShader: `
  5. void main(){
  6. gl_Position = vec4( position, 1.0 ) ;
  7. }
  8. `,
  9. // 片元着色器
  10. fragmentShader: `
  11. void main(){
  12. gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
  13. }
  14. `,
  15. });
  16. // 创建平面
  17. const floor = new THREE.Mesh(
  18. new THREE.PlaneGeometry(1, 1, 64, 64),
  19. shaderMaterial
  20. );
  21. console.log(floor);
  22. // 添加
  23. scene.add(floor);


但这样会发现个问题,由于坐标是一成不变的所以不论你如何移动旋转它将都这样显示,所以的我们在旋转移动鼠标的同时还需要乘以如下几个矩阵:<投影矩阵>*<视图矩阵>*<模型矩阵>*<顶点坐标>
代码改成如下所示,再次运行

  1. gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;


相乘的顺序不能改变。物体本身拥有一个坐标系,叫本地坐标系。把物体放到世界坐标系中,采用了模型矩阵,就是执行缩放、平移、旋转操作的过程。此时物体就具有了世界坐标系。
再加入上帝之眼,就是视图矩阵,包括视点坐标,观察点坐标,和上方向。现在只差最后一步–投影矩阵,物体就可以呈现出来了。
目前显示设备都是二维平面的,所以需要投影矩阵来转换物体。投影矩阵通常分为平行投影和透视投影。

名称 变量
投影矩阵 projectionMatrix
视图矩阵 viewMatrix
模型矩阵 modelMatrix

完整代码

  1. import * as THREE from "three";
  2. import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
  3. import gsap from "gsap";
  4. import * as dat from "dat.gui";
  5. // 目标:认识shader
  6. //创建gui对象
  7. const gui = new dat.GUI();
  8. // console.log(THREE);
  9. // 初始化场景
  10. const scene = new THREE.Scene();
  11. // 创建透视相机
  12. const camera = new THREE.PerspectiveCamera(
  13. 90,
  14. window.innerHeight / window.innerHeight,
  15. 0.1,
  16. 1000
  17. );
  18. // 设置相机位置
  19. // object3d具有position,属性是1个3维的向量
  20. camera.position.set(0, 0, 2);
  21. // 更新摄像头
  22. camera.aspect = window.innerWidth / window.innerHeight;
  23. // 更新摄像机的投影矩阵
  24. camera.updateProjectionMatrix();
  25. scene.add(camera);
  26. // 加入辅助轴,帮助我们查看3维坐标轴
  27. const axesHelper = new THREE.AxesHelper(5);
  28. scene.add(axesHelper);
  29. // 加载纹理
  30. // 创建纹理加载器对象
  31. const textureLoader = new THREE.TextureLoader();
  32. const texture = textureLoader.load("./texture/ca.jpeg");
  33. const params = {
  34. uFrequency: 10,
  35. uScale: 0.1,
  36. };
  37. // const material = new THREE.MeshBasicMaterial({ color: "#00ff00" });
  38. // 创建着色器材质
  39. const shaderMaterial = new THREE.ShaderMaterial({
  40. // 顶点着色器
  41. vertexShader: `
  42. void main(){
  43. gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;
  44. }
  45. `,
  46. // 片元着色器
  47. fragmentShader: `
  48. void main(){
  49. gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
  50. }
  51. `,
  52. });
  53. //
  54. // 创建平面
  55. const floor = new THREE.Mesh(
  56. new THREE.PlaneGeometry(1, 1, 64, 64),
  57. shaderMaterial
  58. );
  59. console.log(floor);
  60. scene.add(floor);
  61. // 初始化渲染器
  62. const renderer = new THREE.WebGLRenderer({ alpha: true });
  63. // renderer.shadowMap.enabled = true;
  64. // renderer.shadowMap.type = THREE.BasicShadowMap;
  65. // renderer.shadowMap.type = THREE.VSMShadowMap;
  66. // 设置渲染尺寸大小
  67. renderer.setSize(window.innerWidth, window.innerHeight);
  68. // 监听屏幕大小改变的变化,设置渲染的尺寸
  69. window.addEventListener("resize", () => {
  70. // console.log("resize");
  71. // 更新摄像头
  72. camera.aspect = window.innerWidth / window.innerHeight;
  73. // 更新摄像机的投影矩阵
  74. camera.updateProjectionMatrix();
  75. // 更新渲染器
  76. renderer.setSize(window.innerWidth, window.innerHeight);
  77. // 设置渲染器的像素比例
  78. renderer.setPixelRatio(window.devicePixelRatio);
  79. });
  80. // 将渲染器添加到body
  81. document.body.appendChild(renderer.domElement);
  82. // 初始化控制器
  83. const controls = new OrbitControls(camera, renderer.domElement);
  84. // 设置控制器阻尼
  85. controls.enableDamping = true;
  86. // 设置自动旋转
  87. // controls.autoRotate = true;
  88. const clock = new THREE.Clock();
  89. function animate(t) {
  90. const elapsedTime = clock.getElapsedTime();
  91. // console.log(elapsedTime);
  92. requestAnimationFrame(animate);
  93. // 使用渲染器渲染相机看这个场景的内容渲染出来
  94. renderer.render(scene, camera);
  95. }
  96. animate();


index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7. <title>Document</title>
  8. <link rel="stylesheet" href="./assets/css/style.css" />
  9. </head>
  10. <body>
  11. <script src="./main/main.js" type="module"></script>
  12. </body>
  13. </html>


style.css

  1. * {
  2. margin: 0;
  3. padding: 0;
  4. }
  5. body {
  6. background-color: #1e1a20;
  7. }
  8. ::-webkit-scrollbar {
  9. display: none;
  10. }
  11. .page {
  12. display: flex;
  13. justify-content: center;
  14. height: 100vh;
  15. padding: 0 10%;
  16. color: #fff;
  17. flex-direction: column;
  18. }
  19. .page h1 {
  20. margin: 60px 0;
  21. font-size: 40px;
  22. }
  23. .page h3 {
  24. font-size: 30px;
  25. }

着色器插件安装与导入


由于OpenGL是一个单独的语言,所以我们更希望它能够单独弄成一个.glsl文件,在需要的时候进行导入。
首先我们创建shader/basic文件夹,并创建相关的文件分别存放顶点着色器和片元着色器的代码。


fragment.glsl

  1. void main(){
  2. gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
  3. }


vertex.glsl

  1. void main(){
  2. gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;
  3. }


在main.js中我们对其进行导入,修改成如下代码。

  1. // 顶点着色器
  2. import basicVertexShader from "../shader/basic/vertex.glsl";
  3. // 片元着色器
  4. import basicFragmentShader from "../shader/basic/fragment.glsl";
  5. ...
  6. const shaderMaterial = new THREE.ShaderMaterial({
  7. // 顶点着色器
  8. vertexShader: basicVertexShader,
  9. // 片元着色器
  10. fragmentShader: basicFragmentShader,
  11. });


效果与前面运行一样。
如果想对gl的代码有所高亮显示,可以安装该插件。

原始着色器材质(RawShaderMaterial)


此类的工作方式与ShaderMaterial类似,不同之处在于内置的uniforms和attributes的定义不会自动添加到GLSL shader代码中。
(修改成如下代码)

  1. // 顶点着色器
  2. import basicVertexShader from "../shader/raw/vertex.glsl";
  3. // 片元着色器
  4. import basicFragmentShader from "../shader/raw/fragment.glsl";
  5. ...
  6. const rawshaderMaterial = new THREE.RawShaderMaterial({
  7. // 顶点着色器
  8. vertexShader: basicVertexShader,
  9. // 片元着色器
  10. fragmentShader: basicFragmentShader,
  11. });
  12. // 创建平面
  13. const floor = new THREE.Mesh(
  14. new THREE.PlaneGeometry(1, 1, 64, 64),
  15. rawshaderMaterial
  16. );


注意这里我更改了glsl的路径,并在相应的路径下创建了文件,需要注意的是需要自行定义变量。

  1. // fragment.glsl
  2. void main(){
  3. gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
  4. }
  1. attribute vec3 position;
  2. uniform mat4 modelMatrix;
  3. uniform mat4 viewMatrix;
  4. uniform mat4 projectionMatrix;
  5. void main(){
  6. gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;
  7. }

着色器材质的变量


每个着色器材质都可以指定两种不同类型的shaders,他们是顶点着色器和片元着色器(Vertex shaders and fragment shaders)。
● 顶点着色器首先运行; 它接收attributes, 计算/操纵每个单独顶点的位置,并将其他数据(varyings)传递给片元着色器。
● 片元(或像素)着色器后运行; 它设置渲染到屏幕的每个单独的“片元”(像素)的颜色。
shader中有三种类型的变量: uniforms, attributes, 和 varyings
● Uniforms是所有顶点都具有相同的值的变量。 比如灯光,雾,和阴影贴图就是被储存在uniforms中的数据。 uniforms可以通过顶点着色器和片元着色器来访问。
● Attributes 与每个顶点关联的变量。例如,顶点位置,法线和顶点颜色都是存储在attributes中的数据。attributes 只 可以在顶点着色器中访问。
● Varyings 是从顶点着色器传递到片元着色器的变量。对于每一个片元,每一个varying的值将是相邻顶点值的平滑插值。

着色器传值


如何从顶点着色器的值传入到片元着色器中?
举例我们可以通过定义uv变量来进行传值。

  1. // vertex.glsl
  2. // 设置绘制的高低精度
  3. precision lowp float;
  4. attribute vec3 position;
  5. attribute vec2 uv;
  6. uniform mat4 modelMatrix;
  7. uniform mat4 viewMatrix;
  8. uniform mat4 projectionMatrix;
  9. varying vec2 vUv;
  10. // 设置精度
  11. // highp -2^16 ~ 2^16
  12. // mediump -2^10 ~ 2^10
  13. // lowp -2^8 ~ 2^8
  14. void main(){
  15. vUv = uv;
  16. gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;
  17. }
  1. // fragment.glsl
  2. // 设置绘制的高低精度
  3. precision lowp float;
  4. varying vec2 vUv;
  5. void main(){
  6. gl_FragColor = vec4(vUv, 0.0, 1.0);
  7. }

设置线框


通过设置原始着色器的wireframe属性为true可以显示图像的像框。

  1. // 创建着色器材质
  2. const rawshaderMaterial = new THREE.RawShaderMaterial({
  3. // 顶点着色器
  4. vertexShader: basicVertexShader,
  5. // 片元着色器
  6. fragmentShader: basicFragmentShader,
  7. // 设置线框模式
  8. wireframe: true,
  9. });
  10. // 创建平面
  11. const floor = new THREE.Mesh(
  12. new THREE.PlaneGeometry(1, 1),
  13. rawshaderMaterial
  14. );

设置图像绘制的坐标


设置Position位置x为1,z为1的情况下如何进行显示。
修改vertex.glsl

  1. // 设置绘制的高低精度
  2. precision lowp float;
  3. attribute vec3 position;
  4. attribute vec2 uv;
  5. uniform mat4 modelMatrix;
  6. uniform mat4 viewMatrix;
  7. uniform mat4 projectionMatrix;
  8. varying vec2 vUv;
  9. void main(){
  10. vUv = uv;
  11. vec4 modelPosition = modelMatrix * vec4( position, 1.0 );
  12. modelPosition.x += 1.0;
  13. modelPosition.z += 1.0;
  14. gl_Position = projectionMatrix * viewMatrix * modelPosition ;
  15. }

设置旋转

  1. void main(){
  2. vUv = uv;
  3. vec4 modelPosition = modelMatrix * vec4( position, 1.0 );
  4. // modelPosition.x += 1.0;
  5. // modelPosition.z += 1.0;
  6. modelPosition.z += modelPosition.x;
  7. gl_Position = projectionMatrix * viewMatrix * modelPosition ;
  8. }


或者我们可以使用一些数学函数,例如:sin()

  1. // 或数学函数
  2. modelPosition.z += sin(modelPosition.x* 10.0);

绘制水平弯曲波浪形状

  1. void main(){
  2. vUv = uv;
  3. vec4 modelPosition = modelMatrix * vec4( position, 1.0 );
  4. // 或数学函数
  5. modelPosition.z += sin(modelPosition.x * 100.0)*0.1;
  6. gl_Position = projectionMatrix * viewMatrix * modelPosition ;
  7. }
  1. const rawshaderMaterial = new THREE.RawShaderMaterial({
  2. // 顶点着色器
  3. vertexShader: basicVertexShader,
  4. // 片元着色器
  5. fragmentShader: basicFragmentShader,
  6. // 设置线框模式
  7. // wireframe: true,
  8. // 传递纹理
  9. side: THREE.DoubleSide,
  10. });

绘制y轴的玻璃形状

  1. void main(){
  2. vUv = uv;
  3. vec4 modelPosition = modelMatrix * vec4( position, 1.0 );
  4. // 或数学函数
  5. modelPosition.z += sin(modelPosition.x * 10.0)*0.05;
  6. // 加上y轴的波动
  7. modelPosition.z += sin(modelPosition.y * 10.0)*0.05;
  8. gl_Position = projectionMatrix * viewMatrix * modelPosition ;
  9. }


通过传入z的位置来设置颜色。

  1. // 设置绘制的高低精度
  2. precision lowp float;
  3. attribute vec3 position;
  4. attribute vec2 uv;
  5. uniform mat4 modelMatrix;
  6. uniform mat4 viewMatrix;
  7. uniform mat4 projectionMatrix;
  8. varying vec2 vUv;
  9. varying float vElevation;
  10. // 设置精度
  11. // highp -2^16 ~ 2^16
  12. // mediump -2^10 ~ 2^10
  13. // lowp -2^8 ~ 2^8
  14. void main(){
  15. vUv = uv;
  16. vec4 modelPosition = modelMatrix * vec4( position, 1.0 );
  17. // 或数学函数
  18. modelPosition.z += sin(modelPosition.x * 10.0)*0.05;
  19. // 加上y轴的波动
  20. modelPosition.z += sin(modelPosition.y * 10.0)*0.05;
  21. vElevation = modelPosition.z;
  22. gl_Position = projectionMatrix * viewMatrix * modelPosition ;
  23. }
  1. // 设置绘制的高低精度
  2. precision lowp float;
  3. varying vec2 vUv;
  4. varying float vElevation;
  5. void main(){
  6. // gl_FragColor = vec4(vUv, 0.0, 1.0);
  7. float letvElevation = vElevation + 0.05 * 10.0;
  8. gl_FragColor = vec4(1.0*letvElevation,0.0, 0.0, 1.0);
  9. }

让图像动起来!


通过在传入时间变量,并更具时间变量的不同的移动它的z轴,让其产生波动的效果。

  1. const rawshaderMaterial = new THREE.RawShaderMaterial({
  2. // 顶点着色器
  3. vertexShader: basicVertexShader,
  4. // 片元着色器
  5. fragmentShader: basicFragmentShader,
  6. // 设置线框模式
  7. // wireframe: true,
  8. // 传递纹理
  9. side: THREE.DoubleSide,
  10. // 传递参数
  11. uniforms: {
  12. uTime: {
  13. value: 0
  14. },
  15. },
  16. });
  17. ...
  18. function animate(t) {
  19. const elapsedTime = clock.getElapsedTime();
  20. // 传入时间
  21. rawshaderMaterial.uniforms.uTime.value = elapsedTime;
  22. requestAnimationFrame(animate);
  23. // 使用渲染器渲染相机看这个场景的内容渲染出来
  24. renderer.render(scene, camera);
  25. }
  1. // 获取时间
  2. uniform float uTime;
  3. ...
  4. // 或数学函数
  5. modelPosition.z += sin((modelPosition.x + uTime) * 10.0)*0.05;
  6. // 加上y轴的波动
  7. modelPosition.z += sin((modelPosition.y + uTime) * 10.0)*0.05;


我们再添加2022残奥会的图片,在fragment.glsl文件中进行修改并添加纹理。

  1. import image from '../assets/ca.jpeg'
  2. ...
  3. const texture = textureLoader.load(image);
  4. ...
  5. const rawshaderMaterial = new THREE.RawShaderMaterial({
  6. ...
  7. // 传递参数
  8. uniforms: {
  9. uTime: {
  10. value: 0
  11. },
  12. uTexture: {
  13. value: texture,
  14. },
  15. },
  16. });
  1. // 设置绘制的高低精度
  2. precision lowp float;
  3. varying vec2 vUv;
  4. varying float vElevation;
  5. uniform sampler2D uTexture;
  6. void main(){
  7. // 根据UV,取出对应的颜色
  8. vec4 textureColor = texture2D(uTexture, vUv);
  9. float letvElevation = vElevation + 0.05 * 10.0;
  10. textureColor.rgb *= letvElevation;
  11. gl_FragColor = textureColor; //vec4(textureColor.rgb, 1.0);
  12. }

需要注意的是,在进行build后图片的路径可能会有所改变导致加载不了图片。


欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

评价
这一世以无限游戏为使命!
排名
2
文章
633
粉丝
44
评论
93
docker中Sware集群与service
尘叶心繁 : 想学呀!我教你呀
一个bug让程序员走上法庭 索赔金额达400亿日元
叼着奶瓶逛酒吧 : 所以说做程序员也要懂点法律知识
.net core 塑形资源
剑轩 : 收藏收藏
映射AutoMapper
剑轩 : 好是好,这个对效率影响大不大哇,效率高不高
ASP.NET Core 服务注册生命周期
剑轩 : http://www.tnblog.net/aojiancc2/article/details/167
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术