• Three.js-着色器加工材质及材质着色器详解


    在Three中,我们可以使用着色器对材质进行加工,例如在对物体材质进行设置时,我们可以通过对顶点着色器的更改,从而实现物体的运动或变化。使用着色器加工材质,主要依赖于Material材质基类中的onBeforeCompile方法进行实现。

    目录

    1. onBeforeCompile

    .onBeforeCompile ( shader : Shader, renderer : WebGLRenderer )

    2. Three材质中shader源码分析

    2.1 shader.vertexShader

    2.2 shader.fragmentShader

     3. 使用着色器修改材质实现gltf模型旋转


    1. onBeforeCompile

    .onBeforeCompile ( shader : Shader, renderer : WebGLRenderer ) : undefined

    在编译shader程序之前立即执行的可选回调。此函数使用shader源码作为参数。用于修改内置材质。

    和其他属性不一样的是,这个回调在.clone(),.copy() 和 .toJSON() 中不支持。

    利用该方法实现着色器对材质的更改,本质是通过替换材质底层的着色器设置的参数实现对其的变换。 

    例如我们通过基础材质设置一个绿色的矩形,并使用着色器使其实现运动,

     基础材质设置:

    1. let basicMaterial = new THREE.MeshBasicMaterial({
    2. color: "#00ff00",
    3. side: THREE.DoubleSide,
    4. });

    着色器设置:

    1. const basicUnifrom = {
    2. uTime:{
    3. value:0
    4. }
    5. }
    6. basicMaterial.onBeforeCompile = (shader,renderer)=>{
    7. console.log(shader);
    8. console.log(shader.vertexShader)
    9. console.log(shader.fragmentShader)
    10. // console.log(renderer)
    11. shader.uniforms.uTime = basicUnifrom.uTime;
    12. shader.vertexShader = shader.vertexShader.replace(
    13. '#include ',
    14. `
    15. #include
    16. uniform float uTime;
    17. `
    18. )
    19. shader.vertexShader = shader.vertexShader.replace(
    20. '#include ',
    21. `
    22. #include
    23. transformed.x += sin(uTime)* 2.0;
    24. transformed.z += cos(uTime)* 2.0;
    25. `
    26. )
    27. }

    实现效果:

    2. Three材质中shader源码分析

    可参考如下文章:


    在 onBeforeCompile方法中设置的控制台输出中,我们可以看到其内部的着色器源码如下:

    2.1 shader.vertexShader

    详见:

    ThreeJS 物理材质shader源码分析(顶点着色器) - zzatp - 博客园ThreeJS 物理材质shader源码分析(顶点着色器) Threejs将shader代码分为ShaderLib和ShaderChunk两部分,ShaderLib通过组合ShaderChunk的代码https://www.cnblogs.com/zzatp/p/9253482.html控制台中输出顶点着色器源码如下:

    源码分析: 

    1. #include // 包含着色器公共模块(包含常用的数学工具函数以及一些常量定义什么的)
    2. #include // 包含处理uv所需要的一些定义
    3. #include // 包含处理uv2所需要的一些定义
    4. #include // 包含置换贴图displacementmap所需要的定义
    5. #include // 包含顶点颜色所需要的定义
    6. #include // 包含雾化效果所需要的定义
    7. #include // 包含变形动画所需要的定义
    8. #include // 包含蒙皮动画所需要的定义
    9. #include // 包含阴影计算所需要的定义
    10. #include // 包含深度处理的一些定义
    11. #include // 包含裁剪平面所需要的一些定义
    12. void main() {
    13. #include // uv 数据处理
    14. #include // uv2 数据处理
    15. #include // 颜色 数据处理
    16. #include // 开始法线处理
    17. #include // 变形动画法线处理
    18. #include // 骨骼蒙皮基本运算
    19. #include // 骨骼蒙皮法线运算
    20. #include // 默认法线处理
    21. #ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED
    22. vNormal = normalize( transformedNormal );
    23. #endif
    24. #include // 开始顶点位置处理
    25. #include // 变形动画位置处理
    26. #include // 蒙皮顶点处理
    27. #include // 置换贴图运用顶点处理
    28. #include // 投影顶点运算
    29. #include // logDepth深度运算
    30. #include // 裁剪平面运算
    31. vViewPosition = - mvPosition.xyz;
    32. #include // 世界坐标运算
    33. #include // 阴影所需要的一些运算
    34. #include // 雾化所需要的运算
    35. }

    因此,我们在使用着色器实现对材质的更改时,可以通过替换shader中的参数进行实现。

    例如上述物体随时间运动的案例中,便是通过replace方法实现,其语法是:stringObj.replace(rgExp, replaceText) 其中stringObj是字符串(string),reExp可以是正则表达式对象(RegExp)也可以是字符串(string),replaceText是替代查找到的字符串。

    上述案例中通过替换

      #include 

    为 

    1. #include
    2. uniform float uTime;

    实现时间参数uTime的设置,并通过对  #include 替换为

    1. #include
    2. transformed.x += sin(uTime)* 2.0;
    3. transformed.z += cos(uTime)* 2.0;

    实现该物体的运动 。 

    2.2 shader.fragmentShader

    详见:ThreeJS 物理材质shader源码分析(像素着色器) - zzatp - 博客园像素着色器(meshphysical_frag.glsl) #define PHYSICAL uniform vec3 diffuse; // 漫反射颜色 uniform vec3 emissive;https://www.cnblogs.com/zzatp/p/9274074.html 控制台中输出片元着色器源如下:

    源码分析:

    1. uniform vec3 diffuse; // 漫反射颜色
    2. uniform vec3 emissive; // 自发光颜色
    3. uniform float roughness; // 粗糙度
    4. uniform float metalness; // 金属性
    5. uniform float opacity; // 透明度
    6. #ifndef STANDARD
    7. uniform float clearCoat; //
    8. uniform float clearCoatRoughness;
    9. #endif
    10. varying vec3 vViewPosition; // 摄像机空间的坐标
    11. #ifndef FLAT_SHADED
    12. varying vec3 vNormal; // 摄像机空间的法线
    13. #endif
    14. #include // 包含着色器公共模块(包含常用的数学工具函数以及一些常量定义什么的)
    15. #include // 数据编码解码功能函数
    16. #include // 抖动处理的定义
    17. #include // 颜色处理的定义
    18. #include // uv相关处理的定义
    19. #include // uv2相关处理的定义
    20. #include // map贴图相关处理的定义
    21. #include // alphamap贴图的处理定义
    22. #include // aomap贴图的处理定义
    23. #include // lighmap贴图处理定义
    24. #include // emissivemap贴图处理的定义
    25. #include // envmap贴图处理的定义
    26. #include // 雾化需要的定义
    27. #include // brdf相关的功能函数
    28. #include // cubemap反射相关
    29. #include // 灯光相关定义
    30. #include // 灯光贴图相关
    31. #include // 灯光相关物理运算
    32. #include // shadowmap影子相关运算定义
    33. #include // bumpmap相关运算的定义
    34. #include // normalmap相关运算的定义
    35. #include // roughnessmap相关运算的定义
    36. #include // metalnessmap相关运算的定义
    37. #include // logdepth相关运算的定义
    38. #include // clipplane裁剪平面相关的定义
    39. void main() {
    40. #include // 裁剪平面裁剪
    41. vec4 diffuseColor = vec4( diffuse, opacity );// 合成rgba四通道漫反射颜色
    42. ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
    43. vec3 totalEmissiveRadiance = emissive;
    44. #include // logdepth运算
    45. #include // map通道颜色采样
    46. #include // color参与计算
    47. #include // alphamap通道颜色采样
    48. #include // alpha测试
    49. #include // 粗糙贴图采样
    50. #include // 金属性贴图采样
    51. #include // 法线贴图基本运算
    52. #include // 法线通过法线贴图运算
    53. #include // 自发光贴图采样
    54. // accumulation
    55. #include // 物理光照基础运算
    56. #include // 计算各种灯光入射光和反射光信息
    57. #include // 从环境光和光照贴图获取辐射
    58. #include // 根据辐射光取得反射信息
    59. // modulation
    60. #include // 根据AO贴图调整反射光照强度
    61. // 反射光直接漫反射+间接漫反射+直接高光+间接高光+自发光 = 输出光照颜色
    62. vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;
    63. gl_FragColor = vec4( outgoingLight, diffuseColor.a );
    64. #include // tonemap进行曝光
    65. #include // 颜色编码
    66. #include // 雾化颜色运算
    67. #include // 颜色预乘alpha
    68. #include // 颜色随机抖动
    69. }

     3. 使用着色器修改材质实现gltf模型旋转

     实现原理与上相同,通过替换着色器中的变量及参数等,实现着色器对材质的修改。

    示例代码:

    1. import * as THREE from "three";
    2. import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
    3. import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"
    4. // console.log(THREE);
    5. // 初始化场景
    6. const scene = new THREE.Scene();
    7. // 创建透视相机
    8. const camera = new THREE.PerspectiveCamera(
    9. 75,
    10. window.innerHeight / window.innerHeight,
    11. 1,
    12. 50
    13. );
    14. // 设置相机位置
    15. // object3d具有position,属性是1个3维的向量
    16. camera.position.set(0, 0, 10);
    17. scene.add(camera);
    18. // 加入辅助轴,帮助我们查看3维坐标轴
    19. const axesHelper = new THREE.AxesHelper(5);
    20. scene.add(axesHelper);
    21. // 加载纹理
    22. // 创建纹理加载器对象
    23. const textureLoader = new THREE.TextureLoader();
    24. // 添加环境纹理
    25. const cubeTextureLoader = new THREE.CubeTextureLoader();
    26. const envMapTexture = cubeTextureLoader.load([
    27. "textures/environmentMaps/0/px.jpg",
    28. "textures/environmentMaps/0/nx.jpg",
    29. "textures/environmentMaps/0/py.jpg",
    30. "textures/environmentMaps/0/ny.jpg",
    31. "textures/environmentMaps/0/pz.jpg",
    32. "textures/environmentMaps/0/nz.jpg",
    33. ]);
    34. //添加直线光源
    35. const directionLight = new THREE.DirectionalLight('#ffffff',1);
    36. directionLight.castShadow = true;
    37. directionLight.position.set(0,0,-200)
    38. scene.add(directionLight)
    39. //设置环境纹理
    40. scene.environment = envMapTexture;
    41. scene.background = envMapTexture;
    42. // 加载模型纹理
    43. const modelTexture = textureLoader.load('./models/LeePerrySmith/color.jpg');
    44. // 加载模型的法向纹理
    45. const normalTexture = textureLoader.load('./models/LeePerrySmith/normal.jpg')
    46. //模型材质
    47. const material = new THREE.MeshStandardMaterial({
    48. map:modelTexture,
    49. normalMap:normalTexture
    50. })
    51. //
    52. const customUniforms = {
    53. uTime : {
    54. value:0
    55. }
    56. }
    57. material.onBeforeCompile = (shader)=>{
    58. console.log(shader.vertexShader);
    59. console.log(shader.fragmentShader);
    60. // 传递时间
    61. shader.uniforms.uTime = customUniforms.uTime;
    62. //添加旋转矩阵算法
    63. shader.vertexShader = shader.vertexShader.replace(
    64. '#include ',
    65. `
    66. #include
    67. mat2 rotate2d(float _angle){
    68. return mat2(cos(_angle),-sin(_angle),
    69. sin(_angle),cos(_angle));
    70. }
    71. uniform float uTime;
    72. `
    73. )
    74. shader.vertexShader = shader.vertexShader.replace(
    75. '#include ',
    76. `
    77. #include
    78. // float angle = sin(position.y+uTime) *0.5;
    79. float angle = uTime *0.5;
    80. mat2 rotateMatrix = rotate2d(angle);
    81. objectNormal.xz = rotateMatrix * objectNormal.xz;
    82. `
    83. )
    84. shader.vertexShader = shader.vertexShader.replace(
    85. '#include ',
    86. `
    87. #include
    88. // float angle = transformed.y*0.5;
    89. //设置二维旋转矩阵
    90. // mat2 rotateMatrix = rotate2d(angle);
    91. transformed.xz = rotateMatrix * transformed.xz;
    92. `
    93. )
    94. }
    95. const depthMaterial = new THREE.MeshDepthMaterial({
    96. depthPacking:THREE.RGBADepthPacking
    97. })
    98. depthMaterial.onBeforeCompile = (shader)=>{
    99. shader.vertexShader = shader.vertexShader.replace(
    100. '#include ',
    101. `
    102. #include
    103. mat2 rotate2d(float _angle){
    104. return mat2(cos(_angle),-sin(_angle),
    105. sin(_angle),cos(_angle));
    106. }
    107. uniform float uTime;
    108. `
    109. );
    110. shader.vertexShader = shader.vertexShader.replace(
    111. '#include ',
    112. `
    113. #include
    114. // float angle = sin(position.y+uTime) *0.5;
    115. float angle = uTime *0.5;
    116. mat2 rotateMatrix = rotate2d(angle);
    117. transformed.xz = rotateMatrix * transformed.xz;
    118. `
    119. )
    120. }
    121. // 模型加载
    122. const gltfLoader = new GLTFLoader();
    123. gltfLoader.load('./models/LeePerrySmith/LeePerrySmith.glb',(gltf)=>{
    124. // console.log(gltf)
    125. const mesh = gltf.scene.children[0];
    126. console.log(mesh)
    127. mesh.material = material;
    128. mesh.castShadow = true;
    129. // 设定自定义的深度材质
    130. mesh.customDepthMaterial = depthMaterial;
    131. scene.add(mesh);
    132. })
    133. // 初始化渲染器
    134. const renderer = new THREE.WebGLRenderer();
    135. // 设置渲染尺寸大小
    136. renderer.setSize(window.innerWidth, window.innerHeight);
    137. renderer.shadowMap.enabled = true;
    138. // 监听屏幕大小改变的变化,设置渲染的尺寸
    139. window.addEventListener("resize", () => {
    140. // console.log("resize");
    141. // 更新摄像头
    142. camera.aspect = window.innerWidth / window.innerHeight;
    143. // 更新摄像机的投影矩阵
    144. camera.updateProjectionMatrix();
    145. // 更新渲染器
    146. renderer.setSize(window.innerWidth, window.innerHeight);
    147. // 设置渲染器的像素比例
    148. renderer.setPixelRatio(window.devicePixelRatio);
    149. });
    150. // 将渲染器添加到body
    151. document.body.appendChild(renderer.domElement);
    152. // 初始化控制器
    153. const controls = new OrbitControls(camera, renderer.domElement);
    154. // 设置控制器阻尼
    155. controls.enableDamping = true;
    156. // 设置自动旋转
    157. // controls.autoRotate = true;
    158. const clock = new THREE.Clock()
    159. function animate(t) {
    160. controls.update();
    161. const time = clock.getElapsedTime();
    162. customUniforms.uTime.value = time;
    163. requestAnimationFrame(animate);
    164. // 使用渲染器渲染相机看这个场景的内容渲染出来
    165. renderer.render(scene, camera);
    166. }
    167. animate();

    实现效果:

  • 相关阅读:
    SpringBoot集成SpringSecurity从0到1搭建权限管理详细过程(认证+授权)
    Flink的基于两阶段提交协议的事务数据汇实现
    移植freertos到qemu上运行
    390. 消除游戏
    用友BIP开发者生态亮相华为全联接大会
    vivado产生报告阅读分析-Report Power4
    11-Vue基础之组件通信(二)
    国产音频放大器工作原理以及应用领域
    【离散数学】图的随机生成和欧拉(回)路的确定(c语言实现)
    【714. 买卖股票的最佳时机含手续费】
  • 原文地址:https://blog.csdn.net/damadashen/article/details/125953918