【Gdoot】从 The Book of Shader 网站模仿学习 Godot 中的 Shader

从 The Book of Shader 网站学习 Shader

我当前初学 Shader,难免有误,不吝赐教,还希望能够指明原因,那就非常感谢了

我从这个网站的链接学习,所以我不会做什么解释,请参照这个网站的链接,配合以下 Godot 的着色器代码进行学习网站的链接:https://thebookofshaders.com/05/?lan=ch (这个网站有英文版,如果想切为英文版,在这个网页的顶部有语言选项)

如果没有 Godot 的基础,建议先学习了解一些基础

  • Godot 着色器:着色器

  • Godot 的 shader 数据类型,运算符,内建函数等:着色语言

  • Godot 三种 shader 类型:Shaders

  • 迁移到 Godot 着色器语言:Migrating to Godot’s shading language

以下这几条内容是我自己的看法,且并不是很准确的,只是想给没学过 Shader 的初学者,提供一下从其他网站学习 Shader 转换到 Godot 的 Shader 的思路:(以下这几条看法其实并不准确,但是可以先暂时这样,先实现其中效果,等到兴趣高了,再去深入学习 Shader 去了解其中的差别)

  • Godot 的着色器代码开头需要先写入 Shader 的类型,比如 2D 类型开头需要写为:shader_type canvas_item;
  • glsl 中的 main() 函数当下可以看作是 Godot 中的 fragment() 函数,并需要在开头先写一条语句 COLOR = texture(TEXTURE, UV); 然后写接下来的内容。
  • 定义的 u_time 变量代表 Godot 中内置的 TIME ,无需定义
  • gl_FragCoord 可看作是 Godot 中内置的 UV ,无需定义
  • 定义的 u_resolution 代表输入的值,需要自己在 Inspect 面板中设置参数,我将其设置默认值为 vec2(1, 1)
  • gl_FragColor 类似 Godot 中内置的 COLOR ,无需定义

开始之前,我们需要现在场景中添加一个 Node2D 根节点,然后,在下面拖拽文件系统中的小 Godot 图标到场景中,节点树会是如下所示:
【Gdoot】从 The Book of Shader 网站模仿学习 Godot 中的 Shader_第1张图片
点击展开属性面板中的 Matrial 属性,给他添加一个 ShaderMatrial 材质,然后添加一个 Shader,如下所示:
【Gdoot】从 The Book of Shader 网站模仿学习 Godot 中的 Shader_第2张图片
OK,环境设置完成!开始我们的学习。


第一个(绘制一条绿色的线)

这个内容需要两个步骤:

  1. 从黑色到白色的渐变
  2. 顶层绘制一条绿色的线

不用过分在意绘制的函数,我们马上会更加详细地解释它。

// Author: Apprentice Zhang

shader_type canvas_item;

uniform vec2 u_resolution = vec2(1, 1);

float plot(vec2 st) {
         
    return smoothstep(0.02, 0.0, abs(st.y - st.x));
}

void fragment(){
     
	COLOR = texture(TEXTURE, UV);
	
	vec2 st = UV.xy / u_resolution;

    float y = st.x;

    vec3 color = vec3(y);

    // Plot a line
    float pct = plot(st);
    color = (1.0-pct)*color + pct*vec3(0.0,1.0,0.0);

	COLOR = vec4(color,1.0);
}

上面这些代码就是你的基本功;遵守和理解它非常重要。你将会一遍又一遍地回到 0.01.0 这个区间。你将会掌握融合与构建这些代码的艺术。

这个 xy 之间一对一的关系称作 线性插值

第二个(绘制一条曲线)

我们接下来用一些数学函数来改造这些代码行 。比如做一个求 x 的 2 次幂的曲线。

// Author: Apprentice Zhang

shader_type canvas_item;

uniform vec2 u_resolution = vec2(2, 2);

float plot(vec2 st, float pct) {
         
    return smoothstep(pct-0.02, pct, st.y) - 
			smoothstep(pct, pct+0.02, st.y);
}

void fragment(){
     
	COLOR = texture(TEXTURE, UV);
	
	vec2 st = UV.xy / u_resolution;

    // pow() (求x的y次幂)是 GLSL 的一个原生函数,GLSL 有很多原生函数。大多数原生函数都是硬件加速的,也就是说如果你正确使用这些函数,你的代码就会跑得更快。
    // 1. 可以修改 2.0 这个值看看效果
    // 2. 将 pow 改为 sqrt(st.x) 看看效果,然后改为 exp 和 log 都看看效果
    float y = pow(st.x, 2.0);

    vec3 color = vec3(y);
	
	float pct = plot(st, y);
	color = (1.0-pct) * color + pct * vec3(0.0, 1.0, 0.0);

	COLOR = vec4(color, 1.0);
}

第三个(step 函数)

// Author: Apprentice Zhang

shader_type canvas_item;

uniform vec2 u_resolution = vec2(2, 2);

float plot(vec2 st, float pct) {
         
    return smoothstep(pct-0.02, pct, st.y) - 
			smoothstep(pct, pct+0.02, st.y);
}

void fragment(){
     
	COLOR = texture(TEXTURE, UV);
	
	vec2 st = UV.xy / u_resolution;

    // step 函数对任何小于阈值的值,返回 0.0,大于阈值,则返回 1.0
    // 可以修改下面的 0.5 这个值看看
    float y = step(0.5, st.x);

    vec3 color = vec3(y);
	
	float pct = plot(st, y);
	color = (1.0-pct) * color + pct * vec3(0.0, 1.0, 0.0);

	COLOR = vec4(color, 1.0);
}

第四个(smoothstep )

// Author: Apprentice Zhang

shader_type canvas_item;

uniform vec2 u_resolution = vec2(2, 2);

float plot(vec2 st, float pct) {
         
    return smoothstep(pct-0.02, pct, st.y) - 
			smoothstep(pct, pct+0.02, st.y);
}

void fragment(){
     
	COLOR = texture(TEXTURE, UV);
	
	vec2 st = UV.xy / u_resolution;

	// 0.1 到 0.9 之间平滑的插值
    float y = smoothstep(0.1, 0.9, st.x);

    vec3 color = vec3(y);
	
	float pct = plot(st, y);
	color = (1.0-pct) * color + pct * vec3(0.0, 1.0, 0.0);

	COLOR = vec4(color, 1.0);
}

在之前的例子中,注意第 8 行,我们用到 smoothstepplot() 函数中画了一条绿色的线。这个函数会对给出的 x 轴上的每个值,在特定的 y 值处制造一个凹凸形变。如何做到呢?通过把两个 smoothstep() 连接到一起。来看看下面这个函数,用它替换上面的第 18 行,把它想成是一个垂直切割。背景看起来很像一条线,不是吗?(注意我的代码和原文代码不一样,这里的每个行号都对应我自己的代码的行号,而不是原链接文章的)

// 将上面“第四个”代码中的第 18 行代码,改为如下代码
float y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);

正弦和余弦函数

(文章内容略,可自行去原文链接中查看。网站的链接:https://thebookofshaders.com/05/?lan=ch )

将上面“第四个”代码中的第 18 行代码,分别试试改为如下每一行代码,看看效果。

float y = mod(st.x,0.5); // 返回 x 对 0.5 取模的值
//float y = fract(st.x); // 仅仅返回数的小数部分
//float y = ceil(st.x);  // 向正无穷取整
//float y = floor(st.x); // 向负无穷取整
//float y = sign(st.x);  // 提取 x 的正负号
//float y = abs(st.x);   // 返回 x 的绝对值
//float y = clamp(st.x,0.0,1.0); // 把 x 的值限制在 0.0 到 1.0
//float y = min(0.0,st.x);   // 返回 x 和 0.0 中的较小值
//float y = max(0.0,st.x);   // 返回 x 和 0.0 中的较大值  

我自己试的效果

float y = mod(st.x * 2.0, 0.5);
//float y = sin(st.x * mod(TIME, 10.0)) * 0.25;

试试下面的小练习(如下的每行代码也是将上面的第18行替换掉)

  • sin 里让 x 加上时间(u_time)。让sin 曲线随 x 轴动起来

    float y = sin(st.x + TIME);
    
  • sin 里用 PI 乘以 x。注意 sin 曲线上下波动的两部分如何收缩了,现在 sin 曲线每两个整数循环一次。

    const float PI = 3.14159265359;	// 先在顶部的 shader_type canvas_item; 语句下边添加这行代码
    
    float y = sin(st.x * PI);
    
  • sin 里用时间( u_time)乘以 x。观察各阶段的循环如何变得越来越频繁。注意 u_time 可能已经变得非常大,使得图像难以辨认。

    float y = sin(st.x * TIME);
    
  • sin(x)(注意不是 sin 里的 x)加 0.3。观察曲线是如何向上移动的,并逐步调整看看效果,也可以减去看看效果。

    float y = sin(st.x) + 0.3;
    
  • sin(x) 乘以 2.0。观察曲线大小如何增大两倍。

    float y = sin(st.x) * 2.0;
    
  • 计算 sin(x) 的绝对值(abs())。现在它看起来就像一个弹力球的轨迹。

    float y = abs(sin(st.x * TIME));
    
  • 只选取 sin(x) 的小数部分(fract())。

    float y = fract(sin(st.x * TIME));
    
  • 使用向正无穷取整(ceil())和向负无穷取整(floor()),使得 sin 曲线变成只有 1 和 -1 的电子波。

    float y = ceil(sin(st.x * TIME));
    
    float y = floor(sin(st.x * TIME));
    

好,循着这个方法,你也可以慢慢跟着学习 Shader,祝学习成功,之后分享出来,为 Godot 贡献一份力

你可能感兴趣的:(Godot,Godot,Shader)