文章目录

  • 前言
  • 一、GLSL基本使用
    • 1.GLSL中的存储关键字
      • attribute
      • uniform
      • varying
    • 2.精度限定关键字
    • 3.模型转换矩阵
  • 二、使用RawShaderMaterial
    • 1.顶点着色器向片元着色器传递数据
    • 2.着色器变换操作
    • 3.让着色器动起来
  • 总结

前言

看了这么久的threejs了,今天使用着色器RawShaderMaterial绘出了第一个图形,特来记录下,例子比较简单,讲的也比较详细,相信不熟悉着色器编程的小伙伴们也能看懂哦。


一、GLSL基本使用

首先了解编写本程序用到的GLSL基本知识点,webgl中着色器的介绍与流程之前有分享过:传送门

1.GLSL中的存储关键字

attribute

attribute变量只能在顶点着色器中使用,它是一个全局变量,被用来表示顶点信息,举例:

attribute vec3 position; // 存储坐标信息
attribute vec2 uv; // 贴图坐标
attribute vec4 a_Color // 颜色

uniform

uniform变量既可以在顶点着色器中使用,也可以在片元着色器中使用,它也是一个全局变量,可以是除了数组与结构体的任何类型,在顶点着色器和片元着色器定义了同名uniform变量时会被二者共享,即会被所有顶点和片元共用,它可以被用来存储变换矩阵,时间纹理等举例:

uniform mat4 modelMatrix; // 模型转换矩阵
uniform mat4 viewMatrix; // 视图矩阵
uniform mat4 projectionMatrix; // 投影矩阵
uniform float uTime; // 时间

varying

vary它也是一个全局变量,与uniform不同的一点是:他必须同时在顶点着色器和片元着色器中定义同名同类型的varying变量,它的作用是把顶点着色器的数据传递给片元着色器,举例:

在顶点着色器,使用varying定义一个变量v_uv,它是二维向量类型,用它获取每个点的uv坐标

varying vec2 v_uv;
void main() {v_uv = uv;···
}

在片元着色器中接收来自顶点着色器中的uv信息,并把它作为rbga中的前俩个值

varying vec2 v_uv;
void main() {gl_FragColor = vec4(v_uv, 0.0, 1.0);
}

2.精度限定关键字

在GLSL中需要指定精度以提高运行效率,减少内存损耗,可以在开头指定以下三个之一。

precision lowp float;
precision medium float;
precision highp float;

3.模型转换矩阵

将一个(外部引入)模型的最终显示到二维屏幕上,实际经历了一系列模型转换过程,流程如下。后续我会单独记录一下流程原理,其中相机坐标系又叫做视图坐标系;目前只需要知道他们在threejs中是如何代表的:

#mermaid-svg-L8hVM7XndOkmwrhG {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-L8hVM7XndOkmwrhG .error-icon{fill:#552222;}#mermaid-svg-L8hVM7XndOkmwrhG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-L8hVM7XndOkmwrhG .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-L8hVM7XndOkmwrhG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-L8hVM7XndOkmwrhG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-L8hVM7XndOkmwrhG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-L8hVM7XndOkmwrhG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-L8hVM7XndOkmwrhG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-L8hVM7XndOkmwrhG .marker.cross{stroke:#333333;}#mermaid-svg-L8hVM7XndOkmwrhG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-L8hVM7XndOkmwrhG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-L8hVM7XndOkmwrhG .cluster-label text{fill:#333;}#mermaid-svg-L8hVM7XndOkmwrhG .cluster-label span{color:#333;}#mermaid-svg-L8hVM7XndOkmwrhG .label text,#mermaid-svg-L8hVM7XndOkmwrhG span{fill:#333;color:#333;}#mermaid-svg-L8hVM7XndOkmwrhG .node rect,#mermaid-svg-L8hVM7XndOkmwrhG .node circle,#mermaid-svg-L8hVM7XndOkmwrhG .node ellipse,#mermaid-svg-L8hVM7XndOkmwrhG .node polygon,#mermaid-svg-L8hVM7XndOkmwrhG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-L8hVM7XndOkmwrhG .node .label{text-align:center;}#mermaid-svg-L8hVM7XndOkmwrhG .node.clickable{cursor:pointer;}#mermaid-svg-L8hVM7XndOkmwrhG .arrowheadPath{fill:#333333;}#mermaid-svg-L8hVM7XndOkmwrhG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-L8hVM7XndOkmwrhG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-L8hVM7XndOkmwrhG .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-L8hVM7XndOkmwrhG .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-L8hVM7XndOkmwrhG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-L8hVM7XndOkmwrhG .cluster text{fill:#333;}#mermaid-svg-L8hVM7XndOkmwrhG .cluster span{color:#333;}#mermaid-svg-L8hVM7XndOkmwrhG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-L8hVM7XndOkmwrhG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

模型转换矩阵
视图矩阵
投影矩阵
模型坐标系
世界坐标系
相机坐标系
裁剪坐标系

在webgl中它们的类型和名称如下:

uniform mat4 modelMatrix; // 模型转换矩阵
uniform mat4 viewMatrix; // 视图矩阵
uniform mat4 projectionMatrix; // 投影矩阵
position // 模型坐标系中的坐标

二、使用RawShaderMaterial

Threejs中提供了ShaderMaterial和RawShaderMaterial两种材质可进行GLSL编写,ShaderMaterial内置了许多常用的GLSL变量,我们使用RawShaderMaterial从头编写。

1.顶点着色器向片元着色器传递数据

在外部引入写好的着色器,基本场景的构建可以参考这里

 import basicVertexShader from '../shader/vertexShader.glsl'import basicFragmentShader from '../shader/fragmentShader.glsl'···const shaderMaterial = new THREE.RawShaderMaterial({vertexShader: vertexShader,fragmentShader: fragmentShader})

在顶点着色器中,主要做了这么几件事:

  • 获取模型的每个顶点的模型坐标和uv坐标
  • 使用varying定义v_uv,获取顶点的uv值传递给片元着色器
  • 计算每个顶点的裁剪坐标 gl_Position(在RawShaderMaterial中需要手动计算)

顶点着色器内容:

 attribute vec3 position; // 顶点的坐标信息attribute vec2 uv; // 顶点的uv坐标信息uniform mat4 modelMatrix;uniform mat4 viewMatrix;uniform mat4 projectionMatrix;varying vec2 v_uv;  // 要传递给片元着色器的数据precision lowp float;void main() {v_uv = uv; // uv坐标信息// gl_Position为每个点裁剪坐标,在RawShaderMaterial可以使用下式获取gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);

在顶点着色器中,主要做了:

  • 接收来自顶点着色器的信息——顶点坐标的uv
  • 将来自顶点着色器的信息赋给颜色rgba中的rg值(gl_FragColor 表示片元颜色)

片元着色器内容:

 varying vec2 v_uv; // 接收来自顶点着色器的信息precision lowp float;void main() {gl_FragColor = vec4(v_uv, 0.0, 1.0);  // 将来自顶点着色器的信息赋给颜色rg值(gl_FragColor 表示片元颜色)}

最终效果如下:

threejs采用右手空间坐标系,红轴为X轴,绿轴为Y轴,在左下角uv坐标为(0.0,0.0),所以他的rgba就变成了(0.0,0.0,0.0,1.0)的纯黑色,同理右上角的颜色是rgba(1.0,1.0,1.0,1.0)的黄色。

2.着色器变换操作

在顶点着色器中,将模型坐标单独提取出来:

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

modelPosition就是我们说的模型坐标,即右手坐标系,注意它和空间直角坐标系是不同的。(图片来自网络)

右手坐标系

空间直角坐标系

对模型的xyz坐标进行处理,修改顶点着色器,对于每个点顶点:

  • XY坐标整体移动1.0个单位
  • Z坐标随着其X坐标成正弦分布
void main() {v_uv = uv; // uv坐标信息vec4 modelPosition = modelMatrix * vec4(position, 1.0);modelPosition.x += 1.0;modelPosition.y += 1.0;modelPosition.z += 0.1 * sin(modelPosition.x * 10.0);gl_Position = projectionMatrix * viewMatrix * modelPosition;
}

threejs为了提高效率默认只渲染单面,需要把 side:THREE.DoubleSide给设置上

    const shaderMaterial = new THREE.RawShaderMaterial({// 投影矩阵 * 视图矩阵 * 模型矩阵 * 顶点坐标vertexShader: vertexShader,fragmentShader: fragmentShader,side:THREE.DoubleSide})

效果也如预计中的一样:

对模型的片元着色器再进行处理,按照Z坐标的大小为其设置颜色:

  • 顶点着色器中设置varying float f_height, 用来向片元着色器传递模型的Z坐标
  • 片元着色器中声明arying float f_height接收来自顶点着色器的信息,在main函数中接受它,把这个值赋给rgba中的第一位

顶点着色器:

attribute vec3 position;
attribute vec2 uv;uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;varying vec2 v_uv;
varying float f_height;// 传递Zprecision lowp float;void main() {// v_uv = uv; // uv坐标信息vec4 modelPosition = modelMatrix * vec4(position, 1.0);modelPosition.x += 1.0;modelPosition.y += 1.0;modelPosition.z += 0.1 * sin(modelPosition.x * 10.0); f_height = modelPosition.z // 放入传递的数据gl_Position = projectionMatrix * viewMatrix * modelPosition;
}

片元着色器:

// varying vec2 v_uv;
varying float f_height; // 传递来的Z坐标
precision lowp float;void main() {float height = f_height + 1.0;  // 创建变量接收数据// gl_FragColor = vec4(v_uv, 0.0, 1.0); gl_FragColor = vec4(height * 1.0, 0.0, 0.0, 1.0);
}

波峰处R值很高,而波谷Z坐标为0,接近纯黑色。

3.让着色器动起来

在RawSahderMaterial中,需要我们手动将时间传递给着色器,为此我们需要:

  • 定义Three.Clock变量,获取每帧的当前时间
  • 在 RawShaderMaterial中定义uniform,将获取到的时间传递给它
  • 片元着色器中接收时间,并把它传给坐标,实现“动起来”的效果

为此,需要对原有代码进行一些改造,同时也调整一下片元着色器的亮度。

  const clock = useRef(new THREE.Clock()).current // 定义Three.Clockconst shaderMaterial = useRef(null) // 将材质提到全局···// 初始化地板const initGeometry = () => {// 原始着色器材质shaderMaterial.current = new THREE.RawShaderMaterial({// 投影矩阵 * 视图矩阵 * 模型矩阵 * 顶点坐标vertexShader,fragmentShader,uniforms: {uTime: { value: 0 }},side: THREE.DoubleSide})const floor = new THREE.Mesh(new THREE.PlaneBufferGeometry(1, 1, 32, 32),shaderMaterial.current);scence.add(floor)}  ···// 渲染器执行渲染const renderScene = useCallback(() => {console.log('renderScene')timer.current = window.requestAnimationFrame(() => renderScene())controls.update();const curElapsedTime = clock.getElapsedTime()shaderMaterial.current.uniforms.uTime.value = curElapsedTime// console.log(curElapsedTime,'---')render.render(scence, camera);}, [render])

顶点着色器:

     precision lowp float;attribute vec3 position;attribute vec2 uv;uniform mat4 modelMatrix;uniform mat4 viewMatrix;uniform mat4 projectionMatrix;uniform float uTime; // 接收时间varying vec2 v_uv;varying float f_height;void main() {v_uv = uv; // uv坐标信息vec4 modelPosition = modelMatrix * vec4(position, 1.0);modelPosition.x += 1.0;modelPosition.y += 1.0;modelPosition.z = 0.05 * sin((modelPosition.x + uTime)* 10.0);modelPosition.z += 0.05 * sin((modelPosition.y + uTime)* 10.0);f_height = modelPosition.z;gl_Position = projectionMatrix * viewMatrix * modelPosition;}

实现动画效果:

总结

以上就是基本使用了,流程还是比较简单的,有要看完整代码的朋友评论区留一下联系方式我会私发的哈。

threejs-自定义着色器材质相关推荐

  1. Qt Creator使用自定义着色器

    Qt Creator使用自定义着色器 使用自定义着色器 可用的自定义着色器实用程序 可用的自定义着色器命令 使用自定义着色器 您可以使用Qt Quick 3D着色器实用程序和命令来创建自己的效果和材质 ...

  2. unity烘培单个物体_Unity可编程渲染管线(SRP)教程:二、自定义着色器

    本文翻译自Catlike Coding,原作者:Jasper Flick. 本文经原作者授权,转载请说明出处. 原文链接在下: https://catlikecoding.com/unity/tuto ...

  3. 什么是着色器/Threejs如何使用着色器/Threejs使用着色器实现平面网格的动态效果案例

    1,什么是着色器 着色器(Shader)是计算机图形学中的一个重要概念,它是在 GPU 上运行的程序,用于计算三维场景中每个像素的颜色和其他属性. 着色器通常分为两种类型:顶点着色器和片元着色器.顶点 ...

  4. Unreal Engine 4 使用HLSL自定义着色器(Custom Shaders)教程(下)

    本文是<Unreal Engine 4 自定义着色器(Custom Shaders)教程>的下半部分,上半部分请见<Unreal Engine 4 自定义着色器(Custom Sha ...

  5. Three 自定义着色器Shader实例

    基础环境搭建: camera = new THREE.OrthographicCamera(-10, 10, 10, -10, 0, 100)camera.position.set(0, 0, 10) ...

  6. OpenGL shader class自定义着色器的实例

    OpenGL shader class自定义着色器 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <glad/glad.h> #in ...

  7. OpenGL ES之GLSL自定义着色器编程实现粒子效果

    效果展示 实现流程 一.自定义着色器 顶点着色器:YDWPointParticleShader.vsh // 位置 attribute vec3 a_emissionPosition; // 速度 a ...

  8. OpenGL 自定义着色器(Shaders)

    自定义着色器类 声明 #pragma once#include<string>class Shaders {public:Shaders(const char* vertexPath, c ...

  9. 基于Cesium使用自定义着色器的资源总结

    基于Cesium使用自定义着色器的资源总结 二维几何着色器 Cesium.js着色器的简单实现 B站:cesium着色器的介绍及使用 cesium添加自己的着色器(我没复现出来) 基于3DTile着色 ...

  10. 可编程渲染管线2 自定义着色器

    原文:https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/custom-shaders/ 写一个HLSL shad ...

最新文章

  1. Java配置环境变量及其意义
  2. 教你一招“恶意修改主页”的处理办法
  3. W3C宣布成立Web性能工作组
  4. [reference]-Features_in_A-profile
  5. vb连接云mysql数据库_用vb链接mysql数据库
  6. 2017西安交大ACM小学期 美妙音乐[差分KMP匹配]
  7. 图像拼接2 特征匹配
  8. C++中类和对象的一些注意事项 --- 多态
  9. java vm art_Android虚拟机art流程:JavaVM 和 JNIEnv 的初始化 - 神农笔记
  10. 图例放在图的外面_手把手教你绘制多个置信区间的森林图
  11. linux主备dns切换时间,linux下主从DNS配置相关知识(二)
  12. 踩过的坑 vertical-alignline-height
  13. linux超级块编辑,在EXT4 linux系统上模拟丢失的超级块错误
  14. 月薪3k能买到什么样的房子,看到结果后,我裂开了。。。
  15. 微信开发者工具 公众号网页调试的调试器没了?
  16. Debian10 双显卡切换
  17. 如何让域用户安装需要管理员权限的软件
  18. 传奇架设好后,在登录游戏账号界面黑屏,并且中间有个小砖块,是什么情况?
  19. tilemap软件使用_使用Tilemap的等距2D环境
  20. 用PC3000和HDD Unlock解笔记本硬盘密码

热门文章

  1. Supermap Iserver 安装指南
  2. JS数组常用的方法shift,unshift,splice,split,slice
  3. Grid++Report报表开发工具介绍
  4. JAVA基础之单例模式
  5. Elastic Stack 开源的大数据解决方案
  6. Mac如何使用Windows各种老式打印机(P1007为例)
  7. β冲刺第二周第二次例会报告
  8. 【基础知识】9、加州房价预测
  9. 绑定挂载mount --bind介绍
  10. jshop测试分析总览