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

news2024/10/5 21:21:49

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来研究和学习

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux——高级IO

目录 IO 五种IO模型 阻塞式IO 非阻塞式IO 信号驱动IO 多路转接 异步IO 阻塞IO VS 非阻塞IO IO 网络的知识我们已经介绍完了&#xff0c;网络通信的本质就是IO&#xff0c;一方要发送数据&#xff0c;还要接收数据&#xff0c;这就是一次IO&#xff0c;所以我们原来说过…

解决VSCode中导入PyTorch时报错的HTTP错误与Channel冲突

问题描述与解释 在Anaconda中成功安装PyTorch&#xff0c;并进行了验证&#xff1a; (base) C:\Users\Hui>conda activate pytorch(pytorch) C:\Users\\Hui>python Python 3.8.19 (default, Mar 20 2024, 19:55:45) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on …

安装windows服务,细节

1、选中服务代码&#xff0c;右键添加安装程序。 2、安装程序的权限一定改为local,否则安装时会提示null错误。 3、安装服务 InstallUtil D:\vs2022work\testFW\testFW\bin\Debug\testFW.exe p:InstallUtil 需要新建环境变量才能直接使用&#xff08;找到InstallUtil 工具所在…

沃尔核材:价值重估

当英伟达这个曾经的GPU行业龙头&#xff0c;伴随AI的发展成为AI芯片架构的供应商时&#xff0c;他就跳出了原本行业的竞争格局&#xff0c;曾经还能与之一战的超威半导体被远远甩在身后&#xff0c;成为宇宙第一公司。 这说的就是一家公司价值的重估。今天给大家聊的也是这样一…

【C++】相机标定源码笔记- RGB 相机与 ToF 深度传感器校准类

类的设计目标是为了实现 RGB 相机与 ToF 深度传感器之间的高精度校准&#xff0c;从而使两种类型的数据能够在同一个坐标框架内被整合使用。这在很多场景下都是非常有用的&#xff0c;比如在3D重建、增强现实、机器人导航等应用中&#xff0c;能够提供更丰富的场景信息。 -----…

学习笔记(linux高级编程)11

进程间通信 》信号通信 应用&#xff1a;异步通信。 中断&#xff0c;&#xff0c; 1~64&#xff1b;32应用编程。 如何响应&#xff1a; Term Default action is to terminate the process. Ign Default action is to ignore the signal. wait Core Default action is …

Eclipse运行main函数报 launch error

右键run as java application&#xff0c;运行main函数的时候报launch error 解决方式&#xff1a;文件右键run configurations 旧的是Project JRE&#xff0c;改成下图这个样子

视频去水印在线工具,视频去水印网站在线使用

在数字媒体时代&#xff0c;视频创作已成为越来越多人热衷的活动。然而&#xff0c;当我们想要对下载的视频进行二次创作或分享时&#xff0c;视频上的水印常常成为一个头疼的问题。本文将为您介绍几种免费且高效的去水印方法&#xff0c;让您在视频制作中游刃有余&#xff01;…

Studying-代码随想录训练营day28| 122.买卖股票的最佳时机II、55. 跳跃游戏、45.跳跃游戏II、1005.K次取反后最大化的数组和

第28天&#xff0c;贪心算法part02&#xff0c;题目难度在增加&#xff0c;要锻炼贪心思维能力(ง •_•)ง&#xff0c;编程语言&#xff1a;C 目录 122.买卖股票的最佳时机II 55. 跳跃游戏 45.跳跃游戏II 1005.K次取反后最大化的数组和 总结&#xff1a; 122.买卖股票的…

npm install puppeteer 报错 npm ERR! PUPPETEER_DOWNLOAD_HOST is deprecated解决办法

npm install puppeteer 报错如下&#xff1a; npm ERR! PUPPETEER_DOWNLOAD_HOST is deprecated. Use PUPPETEER_DOWNLOAD_BASE_URL instead. npm ERR! Error: ERROR: Failed to set up Chrome v126.0.6478.126! Set "PUPPETEER_SKIP_DOWNLOAD" env variable to sk…

绝区零国际服下载 一键下载绝区零国际服教程

绝区零是一款米哈游倾情打造的全新都市幻想动作角色扮演游戏。在游戏中&#xff0c;我们将扮演一名绳匠&#xff0c;这是为出于各种原因需要进入危险空洞的人提供指引的专业人士。您将与独特的角色一起踏上冒险之旅&#xff0c;携手探索空洞&#xff0c;对战强大敌人&#xff0…

每周题解:最大半连通子图

题目链接 最大半连通子图 题目描述 一个有向图 G ( V , E ) G\left(V,E\right) G(V,E) 称为半连通的 (Semi-Connected)&#xff0c;如果满足&#xff1a; ∀ u , v ∈ V \forall u,v\in V ∀u,v∈V&#xff0c;满足 u → v u\to v u→v 或 v → u v\to u v→u&#xff0…

番外篇 | 斯坦福提出即插即用二阶优化器Sophia :相比Adam实现2倍加速,显著节省大语言模型训练成本

前言:Hello大家好,我是小哥谈。大模型的预训练成本巨大,优化算法的改进可以加快模型的训练时间并减少训练开销。目前大模型的训练优化器基本上都采用Adam及其变体,并且Adam的应用已经有9个年头了,在模型优化方面相当于霸主的地位。但是能否够在优化器方面提高模型预训练效…

【每日刷题】Day79

【每日刷题】Day79 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 1619. 删除某些元素后的数组均值 - 力扣&#xff08;LeetCode&#xff09; 2. 1365. 有多少小于当前…

python3.8安装详细教程

python3.8下载及安装详细教程 Python 3.8 是一个重要的Python版本&#xff0c;它引入了一系列新功能和改进。以下是对Python 3.8的详细概述&#xff0c;包括其关键特性、安装方法以及版本状态等信息。 Python 3.8的关键特性 海象运算符&#xff08;Walrus Operator&#xff09…

保障性住房数字化运营平台助力租赁住房智能化管理

保障性住房能提供合理的价格、良好的配套设施和优越的租住体验&#xff0c;租赁将不是问题。 一、发力租赁型保障房建设 随着城镇化进程的加速和流动人口规模的扩大&#xff0c;进城务工人员、新就业大学生等新市民、青年人的住房困难问题日益凸显。加快发展租赁型保障性住房…

深圳技术大学oj C : 生成r子集

Description 输出给定序列按字典序的 &#xfffd; 组合&#xff0c;按照所有 &#xfffd; 个元素出现与否的 01 标记串 &#xfffd;&#xfffd;&#xfffd;&#xfffd;−1,...,&#xfffd;1 的字典序输出. 此处01串的字典序指&#xff1a;先输入的数字对应低位&#x…

使用高斯混合模型识别餐厅热点

使用 GMM 识别加拿大多伦多的直观餐厅集群&#xff08;附 Python 代码&#xff09; 聚类算法&#xff08;例如 GMM&#xff09;是一种有用的工具&#xff0c;可帮助识别数据中的模式。它们使我们能够识别数据集中的子组&#xff0c;从而提高你的理解或增强预测模型。在本文中&a…

中国国产AI芯片的崛起

一、CUDA的垄断 当讨论半导体行业面临的挑战时&#xff0c;你首先想到的是什么&#xff1f;光刻机&#xff1f;3纳米或者5纳米技术&#xff1f;我们无法生产的完美方形芯片&#xff1f;是的&#xff0c;但也不完全是。 人们经常把半导体芯片归类为硬件产业&#xff0c;但实际上…

mmcv安装失败及解决方案

假如想安装的版本是mmcv1.4.0, 但是pip install mmcv1.4.0总是失败&#xff0c;若是直接pip install mmcv会安装成功&#xff0c;但是安装的就是最新版本&#xff0c;后面代码跑起来还会报错&#xff0c;怎么办呢&#xff1f; 接下来分享一个mmcv指定版本安装的方式。 网页&a…