【Threejs进阶教程-着色器篇】1. Shader入门(ShadertoyShader和ThreejsShader入门)

ThreejsShader入门

  • 关于本Shader教程
  • 认识Shader
    • Shader和Threejs的关系
    • WebGLShader
    • ThreejsShader
    • ShadertoyShader
    • 其他Shader
  • 再次劝退数学不好的人
  • 从ShaderToy开始
    • Shader的代码是强类型
    • glsl的类型,变量,内置函数,关键字
    • 关于uv
    • 基于UV的颜色处理
  • 最基本的ThreeJS Shader
    • ShaderMaterial介绍
    • 代码拆解
      • uniforms(参数集)(本篇不讲)
      • vertexShader(顶点着色器)(现阶段不讲)
      • fragmentShader(片元着色器)代码讲解
    • 现阶段的效果进阶
  • ThreejsShader和ShadertoyShader的巧妙使用
  • 本系列教程更新速度可能较慢,还请耐心等待

关于本Shader教程

  1. 本教程着重讲解Shadertoy的shader和Threejs的Shader,与原生WebGLShader略有不同,如果需要学习原生WebGL的shader,请参考《WebGL编程指南》
  2. 本人的shader水平也比较基础,文章中所写代码,不一定是最佳的代码,思路也不一定是最好的思路,所以一切本人的Shader教程下,所有的代码及思路以及学习建议均仅供参考,且目前本教程可能不适用于WebGPU,如果有大佬路过看到本人文章,觉得有可以指点之处,可以在下面留言,我们一起进步
  3. 数学水平不行的人,尤其是高中数学都及格不了的,不建议入坑Shader
  4. 本教程会在讲解片元着色器时,使用Shadertoy来编写demo,所以教程中会出现一部分Shadertoy的代码
  5. 本段内容将会出现在本人所有的【进阶教程-着色器篇】的文章中

认识Shader

能百度到的我这里就不废话了,自行百度 WebGL,OpenGL,OpenGL ES,GLSL,这些可以帮助你了解shader的历史和发展以及版本关系

Shader和Threejs的关系

shader是Threejs的底层,在前后关系上,Shader要更底层,Threejs的渲染器部分,主要基于Shader来开发

WebGLShader

最原始版本的Shader,在学习《WebGL编程指南》时会经常接触

ThreejsShader

ThreejsShader,特指 THREE.ShaderMaterial和THREE.RawShaderMaterial,这个是Threejs最核心的内容之一,基本上所有的特效物体,特效粒子,都可以使用Shader来制作,在threejs的ShaderMaterial中,内置了很多方便的常用的变量,提供给你访问,如果你不想使用Threejs内置的变量,那么用THREE.RawShaderMaterial即可

ShadertoyShader

基于Shadertoy网站的Shader,由Shadertoy网站提供了大量的内置变量,以编写片元着色器为主
(但是shadertoy上的大佬,早就不满足于只写片元着色器了,在里面加入了各种各样牛逼的写法,什么光线追踪,3d化的各种效果,还有人在里面做游戏的,也有人就纯靠代码写出来超级逼真的场景,比如说iq大佬,而且全部开源,是个非常适合学Shader的地方)

本系列教程,以ShadertoyShader和ThreejsShader为主,可能部分写法不太适用于其他的shader

其他Shader

cesium,babylon,pixi均内置了自己的shader系统,但是所有的shader,基本语法都是一样的,只有内置变量的区别,所以学会了原生Shader基本上可以通吃整个webgl圈子的shader

所以如果你想要学习原生的Shader,建议从《WebGL开发指南》开始学起

再次劝退数学不好的人

Shader难度很高,对数学能力要求很高,如果你已经做好了心理准备,那么我们接下来,就准备开始吧

从ShaderToy开始

ShaderToy的入门案例

但是,这里本人要改一下,改的更简单一点,方便入门
在这里插入图片描述

	void mainImage( out vec4 fragColor, in vec2 fragCoord )
	{
	    vec2 uv = fragCoord/iResolution.xy;
	
	    fragColor = vec4(uv.x,0.0,0.0,1.0);
	}

把这一段代码,替换到Shadertoy中,然后点击编辑窗左下角的播放键,即可得到左边的渐变红色效果

我们来对上述代码做一个分析

Shader的代码是强类型

很多人直接就反应了,这个代码根本看不懂,因为根本就不是 JS语言
首先,我们看到第一行,代码是==void mainImage( out vec4 fragColor, in vec2 fragCoord ) ==
有没有发现,这个代码很像是我们学习的C语言的HelloWorld

glsl是强类型语言,所以我们无论什么时候,都要遵循强类型语言的规则

现阶段,我们只需要死记硬背,第一行是Shadertoy必须要写的内容即可

然后我们开始分析第二行代码,vec2 uv = fragCoord/iResolution.xy;
这里声明了一个变量,vec2 uv

glsl的类型,变量,内置函数,关键字

这里本人就懒得手敲了,《WebGL编程指南》187页~215页,非常多,这里本人仅截图出来第一张,后面的请自行去群里下载电子书,或者买一本实体书来阅读
什么群?基本上所有的Threejs/WebGL的qq群文件里都有相关电子书,非常好找,随便加一个就行了

内置函数在 427~436页,内置函数表在后续的开发中会经常查阅,所以建议学习Shader的人自行购买一本实体书
还有,内置变量啊,内置函数啊,现阶段就看一遍就行了,知道哪些是关键字,避开就行了
在这里插入图片描述

实在懒得看书的就看我这里的解释吧。。。
这里仅介绍本篇教程中用到的变量和对象

vec2,其实是一个缩写,完整单词是Vector2,二维向量,这里的uv实际上就是一个二维向量,一个二维向量由 x,y两个数据构成

然后后面的 fragCoord/iResolution.xy,这里,现阶段不做讲解,只需要记住,shadertory第二行必须是这个就行了,后面在threejs的开发中,这一行也不会发生大的改变,如果你需要更详细的了解和学习Shadertoy,再去考虑研究 fragCoord和iResolution的作用,最终的计算结果,就是uv

注意:shadertoy,以及后续所有的Shader代码每一行结尾必须加分号!!!!!!!!

关于uv

uv这个概念,在前面介绍纹理篇已经简单介绍过了,所以还不懂uv概念的,回去补补课了

在Shadertoy中,已经通过内置的计算,把uv给你端上来了,但是shadertoy的uv和我们传统意义上的uv的直观感受不太一样,一般我们在讲解uv的时候,长宽是一样长的,但是shadertoy中不同

在这里插入图片描述

基于UV的颜色处理

接下来进入最重要的一行了 fragColor = vec4(uv.x,0.0,0.0,1.0);

首先,在Shadertoyu中,fragColor 代表最终输出,也就是说,无论你怎么编写这里的代码,都必须要有一个最终输出,这个最终输出需要你传入一个 vec4类型的变量,就是最终颜色,对应颜色的rgba

vec4在用作最终变量的时候,4个值分别对应 红色百分比,绿色百分比,蓝色百分比,透明度

我们从代码中可以得到,我们把uv的x值,作为红色的百分比值,传入到了最终输出中,然后绿色和蓝色保持为0,透明度为1,所以整个效果都呈现红色,只有强弱的区别

但是,这个效果并不是纯红色,因为uv的值是在变的,画面最左侧的uv的u值(代码中是uv.x),是0,左右侧的u值为1,所以最终呈现出了这样的一个渐变红色的效果

我们把uv.x放到第二个参数上,看看效果
在这里插入图片描述
我们把uv.y 作为参数传进去,看看效果
在这里插入图片描述

这样我们就完成了Shader的入门级案例,基于uv的颜色渐变

最基本的ThreeJS Shader

Shader最最基本的案例,我们已经完成了,那么现在我们进入下一阶段,将这个效果应用于Threejs
首先,我们依然要准备一个demo

<!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;
    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(vUv.x,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";

    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(0,0,15);
        scene.add(camera);

        orbit = new OrbitControls(camera,renderer.domElement);
        orbit.enableDamping = true;
        scene.add(new THREE.GridHelper(10,10));
    }

    let uniforms = {

    }

    function addMesh() {
        let geometry = new THREE.PlaneGeometry(10,10);
        let material = new THREE.ShaderMaterial({
            uniforms,
            vertexShader:document.getElementById('vertexShader').textContent,
            fragmentShader:document.getElementById('fragmentShader').textContent,
        })
        let mesh = new THREE.Mesh(geometry,material);
        scene.add(mesh);
    }


    function render() {
        renderer.render(scene,camera);
        orbit.update();
        requestAnimationFrame(render);
    }

</script>
</body>
</html>

demo效果如下
在这里插入图片描述
中间的那条线,是在代码中创建的网格辅助线

ShaderMaterial介绍

ShaderMaterial官方文档
RawShaderMaterial官方文档

大多数情况下都用Shadermaterial,内置的东西已经足够用了,RawShaderMaterial只是用了更原生的方式,来编写Shader

现阶段给你文档也不一定看得懂,只需要记得,下面提到的,现阶段死记硬背的东西,就死记硬背下来,按照这个格式写就行了

代码拆解

创建ShaderMaterial,至少要有三个要点

uniforms(参数集)(本篇不讲)

uniforms是一个对象,而且,threejs对uniforms有严格要求,比如说下面的写法:

let uniforms = {
opacity:{ value: 1.0 },
aPosition:{value: new THREE.Vector3()}
}

uniforms的参数,必须是 [key] : { value : [value] },后续会详细讲解uniforms的用法用途

vertexShader(顶点着色器)(现阶段不讲)

threejs这里需要的是一个字符串,这个字符串,就是glsl的代码
现阶段,默认顶点着色器代码固定 ,且只需要知道,创建ShaderMaterial必须要有一个顶点着色器即可,通过document.getElementById(‘vertexShader’).textContent
来获取上面id为 vertexShader的dom,并通过.textContent,来获取到内部编写的文本代码

<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>

fragmentShader(片元着色器)代码讲解

现阶段主要编写的代码,通过
document.getElementById(‘fragmentShader’).textContent
来获取上面id为 fragmentShader的dom,并通过.textContent,来获取到内部编写的文本代码

<script type="x-shader/x-fragment" id="fragmentShader">
    varying vec2 vUv;
    void main(){
        gl_FragColor = vec4(vUv.x,0.0,0.0,1.0);
    }
</script>

这里我们先介绍片元着色器的代码,等后续进入到顶点着色器的篇章中,再详细介绍顶点着色器

首先,varying vec2 vUv; ,现阶段视为片元着色器的固定代码,只需要关注下面的即可

我们对比一下和ShaderToy的代码结构

在这里插入图片描述

首先,这里我们依然是需要一个main,但是在Threejs的Shader中,写法变更为 main(),而且不需要传入参数(为什么不需要传入参数,这个后续讲解ShadertoyShader时会讲)
然后同样的,threejs中,也需要一个最终输出,只不过,这个最终输出写为 gl_FragColor,参数与Shadertoy一致

我们的代码,采用在Shadertoy的代码的第一版,依然是用vUv.x去控制红色

在Threejs中,uv是内置变量,所以我们为了避免冲突,在这里写成vUv,实际作用与在Shadertoy里面的uv是一致的

现阶段的效果进阶

我们现在改一下代码,改动一点点即可

<script type="x-shader/x-fragment" id="fragmentShader">
    varying vec2 vUv;
    void main(){
        gl_FragColor = vec4(1.0 - vUv.y,0.0,0.0,1.0 - vUv.y);
    }
</script>

然后改动一下下面的ShaderMaterial

        let material = new THREE.ShaderMaterial({
            uniforms,
            vertexShader:document.getElementById('vertexShader').textContent,
            fragmentShader:document.getElementById('fragmentShader').textContent,
            transparent:true,
            side:THREE.DoubleSide
        });

这样,我们就得出了一个这样的效果

在这里插入图片描述
在这里插入图片描述
然后,我们在代码中,创建4个一样的平面,然后做一下旋转,拼成一个围墙

    function addMesh() {
        let geometry = new THREE.PlaneGeometry(10,10);
        let material = new THREE.ShaderMaterial({
            uniforms,
            vertexShader:document.getElementById('vertexShader').textContent,
            fragmentShader:document.getElementById('fragmentShader').textContent,
            transparent:true,
            side:THREE.DoubleSide
        })
        let mesh = new THREE.Mesh(geometry,material);

        let mesh1 = mesh.clone();
        mesh1.position.set(5,5,0);
        mesh1.rotation.y = Math.PI/2;
        scene.add(mesh1);

        let mesh2 = mesh.clone();
        mesh2.position.set(-5,5,0);
        mesh2.rotation.y = Math.PI/2;
        scene.add(mesh2);

        let mesh3 = mesh.clone();
        mesh3.position.set(0,5,-5);
        scene.add(mesh3);

        let mesh4 = mesh.clone();
        mesh4.position.set(0,5,5);
        scene.add(mesh4);
    }

在这里插入图片描述
这个就是最基础的光栅栏效果,各位可以自行修改在片元着色器中的颜色,来修改颜色

但是,这个光栅栏不是最终版本,请阅读后续教程来继续优化效果

ThreejsShader和ShadertoyShader的巧妙使用

上面我们展示了,如何编写最基本的shader,不难看出,我们在编辑片元着色器的时候,是可以借助Shadertoy来编辑的,很多shadertoy的2d效果,也可以被搬到Threejs中现用,但是仅限于你要看懂,且以uv为基准的那些,所以,如果你想要编辑一些2D的,以及动态贴图效果,都可以借助Shadertoy来研究和学习

本系列教程更新速度可能较慢,还请耐心等待

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PCL 点云FPFH特征描述子

点云FPFH特征描述子 一、概述1.1 FPFH概念1.2 基本原理1.3 PFH和FPFH的区别二、代码实现三、结果示例一、概述 1.1 FPFH概念 快速点特征直方图(FPFH)描述子:计算 PFH 特征的效率其实是十分低的,这样的算法复杂度无法实现实时或接近实时的应用。因此,这篇文章将介绍 PFH 的简…

【java web 01】3小时快速学习前端知识(收藏备用)

3小时快速学习前端知识【全栈专用】 一、教程简介1.1 Java 开发为何学Web技术1.2 课程设计1.3 课前准备 二、HTML2.1 Html简介2.1.1 HTML、CSS、JS分别有什么作用2.1.2 什么是HTML2.1.3 什么是标记语言 2.2 Hello&#xff0c;Html2.2.1 HTML基础结构2.2.2 专业词汇2.2.3 语法细…

面试经典150题

合并两个有序数组 两个按非递减顺序排列的整数数组nums1和nums&#xff0c;另有两个整数m和n&#xff0c;分别表示nums1和nums2中的元素数组。 请合并nums2到nums1中&#xff0c;使合并后的数组同样按非递减顺序排列。 直接合并后排序 class Solution { public:void merge(…

解码Python字符串:‘r‘、‘b‘、‘u‘和‘f‘前缀的全面指南

&#x1f4d6; 正文 1 字符串前加’r’ 表示原始字符串&#xff0c;消除转义 print(abc\nde) # abc # deprint(rabc\nde) # abc\nde在下面这个列子中&#xff0c;如果不在路径字符串前面加r那么&#xff0c;路径中的空格就会出现问题 print(rD:\01 programming\09python\py…

【ARM系列】GIC600AE功能安全

GIC600AE功能安全 1.GIC600AE主要安全机制分布图&#xff1a;2.Fault Management Unit1.GIC block的错误如何上报到FMU&#xff1f;2.汇总到FMU的错误如何上报&#xff1f;3.Error Record format4.Safety Mechanism GIC600AE在原GIC600版本基础上增加了FuSa功能&#xff0c;所增…

RIP环境下的MGRE网络

首先将LSP的IP地址进行配置 其他端口也进行同样的配置 将serial3/0/1配置25.0.0.2 24 将serial4/0/0配置35.0.0.2 24 将GE0/0/0配置45.0.0.2 24 进行第二步 R1与R5之间使用ppp的pap认证 在R5中进行配置 在aaa空间中创建账号和密码 将这个账号和密码使用在ppp协议中 然后…

zdppy+onlyoffice+vue3解决文档加载和文档强制保存时弹出警告的问题

解决过程 第一次排查 最开始排查的是官方文档说的 https://api.onlyoffice.com/editors/troubleshooting#key 解决方案。参考的是官方的 https://github.com/ONLYOFFICE/document-server-integration/releases/latest/download/Python.Example.zip 基于Django的Python代码。 …

使用 Hugging Face 模型时遇到的问题

题意&#xff1a; I load a float32 Hugging Face model, cast it to float16, and save it. How can I load it as float16? 我加载了一个float32的Hugging Face模型&#xff0c;将其转换为float16&#xff0c;并保存了。我该如何以float16的形式加载它呢&#xff1f; 问题…

2.硬盘和内存区别

2.2 磁盘比内存慢几万倍&#xff1f; 存储器方面的设备&#xff0c;分类比较多&#xff0c;那我们肯定不能只买一种存储器&#xff0c;比如你除了要买内存&#xff0c;还要买硬盘&#xff0c;而针对硬盘我们还可以选择是固态硬盘还是机械硬盘。 相信大家都知道内存和硬盘都属…

【大模型LLM面试合集】大语言模型架构_attention

1.attention 1.Attention 1.1 讲讲对Attention的理解&#xff1f; Attention机制是一种在处理时序相关问题的时候常用的技术&#xff0c;主要用于处理序列数据。 核心思想是在处理序列数据时&#xff0c;网络应该更关注输入中的重要部分&#xff0c;而忽略不重要的部分&…

java webservice 根据wsdl文件生成客户端代码;webservice可视化测试工具SOAPUI;

背景 最近要对接HIS系统&#xff0c;对方提供的接口是webservice的&#xff08;有点古老&#xff09;&#xff0c;对方是webservice的提供方&#xff0c;提供了wsdl文件&#xff0c;我方需要根据wsdl文件生成java代码&#xff0c;intellij idea生成webservice客户端代码支持的…

复分析——第10章——Θ函数应用(E.M. Stein R. Shakarchi)

第10章 Θ函数的应用 (Applications of Theta Functions) The problem of the representation of an integer n as the sum of a given number k of integral squares is one of the most celebrated in the theory of numbers. Its history may be traced back to Diopha…

列表渲染 v-for

列表渲染v-for 使用v-for指令基于数组渲染一个列表&#xff0c;v-for指令的值需要使用item in/of items形式的特殊语法&#xff0c;其中items是源数据的数组&#xff0c;而item是迭代的别名。 代码实例&#xff1a; <template> <div><p v-for"item in na…

Java基础概念

1.注释和关键字 &#xff08;1&#xff09;注释 什么是注释&#xff1f;注释就是对代码进行解释说明的文字 注释的分类&#xff1f;单行注释&#xff0c;多行注释&#xff0c;文档注释 注释的使用细节&#xff1f; 注释的内容不会参与编译和运行&#xff0c;仅仅是对代码的…

使用vllm部署大语言模型

vLLM是一个快速且易于使用的库&#xff0c;用于LLM&#xff08;大型语言模型&#xff09;推理和服务。通过PagedAttention技术&#xff0c;vLLM可以有效地管理注意力键和值内存&#xff0c;降低内存占用和提高计算效率。vLLM能够将多个传入的请求进行连续批处理&#xff0c;从而…

智能视频监控如何助力体育场馆安全管理:安防监控EasyCVR视频综合管理方案

近期有新闻报道&#xff0c;6月30日&#xff0c;17岁的中国国家羽毛球运动员在亚洲青年羽毛球锦标赛中&#xff0c;突然晕倒并抽搐&#xff0c;尽管被送往医院抢救&#xff0c;该运动员仍在当晚不幸离世。运动猝死不仅发生于职业运动员身上&#xff0c;在普通健身者中也时有发生…

nodejs + vue3 模拟 fetchEventSouce进行sse流式请求

先上效果图: 前言: 在GPT爆发的时候,各项目都想给自己的产品加上AI,蹭上AI的风口,因此在最近的一个需求,就想要给项目加入Ai的功能,原本要求的效果是,查询到对应的数据后,完全展示出来,也就是常规的post请求,后来这种效果遇到了一个很现实的问题:长时间的等待。我…

4个方法帮助你解决RAR解压文件时提示密码错误问题

在日常工作和学习中&#xff0c;我们经常需要处理各种压缩文件&#xff0c;这些文件有时为了保护内容安全&#xff0c;会被设置密码。然而&#xff0c;在解压这些文件时&#xff0c;如果遇到“密码错误”的提示&#xff0c;可能会让人感到十分棘手。今天&#xff0c;我们就来探…

[ICS] Modbus未授权攻击S7协议漏洞利用

工业控制系统历史 在可编程逻辑控制器(plc)成为标准之前&#xff0c;工厂车间自动化是通过机架和机架的工业继电器&#xff0c;气动柱塞计时器和电磁计数器来控制电机的启动和停止&#xff0c;阀门的打开以及其他与控制相关的过程交互。运行这种设置的控制程序根本不是程序&am…