你的第一个CanvasItem明暗器

介绍

明暗器是在GPU上执行的特殊程序,用于渲染图形。所有的现代渲染都是使用明暗器完成的。有关什么是明暗器的更详细描述,请参见 What are shaders .

本教程将重点介绍编写着色程序的实际方面,方法是引导您完成使用顶点和片段函数编写着色程序的过程。本教程针对绝对初学者进行着色。

注解

如果您有编写明暗器的经验,并且只是想了解明暗器在Godot中的工作方式,请参见 Shading Reference .

安装程序

CanvasItem 着色器用于绘制godot中的所有二维对象,而 Spatial 材质球用于绘制所有三维对象。

要使用明暗器,必须将其附加到 Material 必须附加到对象上。材料是一种 Resource . 要使用相同的材质绘制多个对象,必须将材质附加到每个对象。

所有从 CanvasItem 具有材料属性。这包括所有 GUI elementsSpritesTileMapsMeshInstance2Ds 等等。他们也可以选择继承父母的材料。如果您有大量想要使用同一材质的节点,则此功能非常有用。

首先,创建一个sprite节点。您可以使用任何canvasitem,但对于本教程,我们将使用sprite。

在Inspector中,单击“纹理”旁边的“纹理”。 [空的] 选择“加载”,然后选择“icon.png”。对于新项目,这是Godot图标。现在应该可以在视区中看到图标。

接下来,在Inspector中的CanvasItem部分下,单击“Material”旁边并选择“New ShaderMaterial”。这将创建新的材料资源。单击出现的球体。Godot目前不知道您是在编写CanvasItem明暗器还是空间明暗器,它预览空间明暗器的输出。所以您看到的是默认空间明暗器的输出。

单击“shader”旁边并选择“new shader”。最后,单击新的明暗器资源,明暗器编辑器将打开。现在可以开始编写第一个着色程序了。

你的第一个CanvasItem明暗器

在godot中,所有明暗器都从一条指定明暗器类型的线开始。它使用以下格式:

shader_type canvas_item;

因为我们正在编写CanvasItem明暗器,所以我们指定 canvas_item 在第一行。我们所有的代码都将在这个声明之下。

这一行告诉引擎要向您提供哪些内置变量和功能。

在godot中,可以覆盖三个函数来控制明暗器的操作方式; vertexfragmentlight . 本教程将指导您编写具有顶点和片段功能的着色程序。光函数比顶点和碎片函数要复杂得多,所以这里不介绍。

你的第一个片段函数

fragment函数为sprite中的每个像素运行,并确定该像素应该是什么颜色。

它们被限制在sprite覆盖的像素上,这意味着您不能使用它来创建sprite周围的轮廓。

最基本的片段函数除了为每个像素指定一种颜色外,什么也不做。

我们通过写一个 vec4 到内置变量 COLOR . vec4 是用4个数字构造向量的简写。有关向量的更多信息,请参见 Vector math tutorial COLOR 既是片段函数的输入变量,也是片段函数的最终输出。

void fragment(){
  COLOR = vec4(0.4, 0.6, 0.9, 1.0);
}
../../../_images/blue-box.png

祝贺你!你完了。您已经成功地在Godot中编写了第一个着色器。

现在让我们让事情变得更复杂。

fragment函数有许多输入可以用于计算 COLOR . UV 是其中之一。紫外线坐标是在你的精灵中指定的(你不知道!)它们告诉材质球在哪里可以从网格的每个部分的纹理中读取。

在fragment函数中,只能从 UV ,但您可以在其他函数中使用它或将值赋给 COLOR 直接。

UV 从左到右和从上到下变化为0-1。

../../../_images/iconuv.png
void fragment() {
  COLOR = vec4(UV, 0.5, 1.0);
}
../../../_images/UV.png

使用 TEXTURE 内置

当你想调整精灵中的颜色时,你不能像下面的代码一样,手动调整纹理中的颜色。

void fragment(){
  //this shader will result in an all white rectangle
  COLOR.b = 1.0;
}

默认的fragment函数从纹理中读取并显示它。当覆盖默认的片段函数时,您将失去该功能,因此必须自己实现它。从纹理中读取 texture 功能。某些节点(如sprites)具有专用的纹理变量,可以使用 TEXTURE . 与一起使用 UVtexture 画雪碧。

void fragment(){
  COLOR = texture(TEXTURE, UV); //read from texture
  COLOR.b = 1.0; //set blue channel to 1.0
}
../../../_images/blue-tex.png

均匀输入

统一输入用于将数据传递到整个着色器中相同的着色器。

通过在材质球顶部定义制服,可以使用制服,如下所示:

uniform float size;

有关用法的详细信息,请参阅 Shading Language doc .

加入一种制服来改变雪碧中蓝色的含量。

uniform float blue = 1.0; // you can assign a default value to uniforms

void fragment(){
  COLOR = texture(TEXTURE, UV); //read from texture
  COLOR.b = blue;
}

现在您可以从编辑器中更改精灵中蓝色的数量。回顾一下创建材质球的检查器。您应该看到一个名为“shader param”的部分。打开这一部分,你会看到你刚刚宣布的制服。如果您在编辑器中更改该值,它将覆盖您在明暗器中提供的默认值。

从代码与着色器交互

您可以使用此功能从代码更改制服 set_shader_param() 在节点的材料资源上调用。使用sprite节点,可以使用以下代码设置 blue 制服。

var blue_value = 1.0
material.set_shader_param("blue", blue_value)

注意,制服的名称是一个字符串。字符串必须与它在着色器中的写入方式完全匹配,包括拼写和大小写。

你的第一个顶点函数

现在我们有了一个片段函数,让我们编写一个顶点函数。

使用顶点函数计算每个顶点在屏幕上的结束位置。

顶点函数中最重要的变量是 VERTEX . 最初,它指定模型中的顶点坐标,但也会写入该坐标以确定实际绘制这些顶点的位置。 VERTEX 是一个 vec2 最初显示在本地空间中(即不相对于相机、视区或父节点)。

可以通过直接添加到 VERTEX .

void vertex() {
  VERTEX += vec2(10.0, 0.0);
}

TIME 内置变量,这可以用于简单的动画。

void vertex() {
  // Animate Sprite moving in big circle around its location
  VERTEX += vec2(cos(TIME)*100.0, sin(TIME)*100.0);
}

结论

在它们的核心,明暗器做你到目前为止看到的事情,它们计算 VERTEXCOLOR . 这取决于你想出更复杂的数学策略来给这些变量赋值。

要获得灵感,请查看一些更高级的着色教程,并查看其他网站,如 ShadertoyThe Book of Shaders .