你的第一个画布项着色器

简介

着色器是在GPU上运行的,用来渲染图像的一种特殊的程序.现代渲染都是通过着色器实现的.若想了解关于着色器更详细的说明,请查看 着色器是什么.

本教程将重点介绍实际编写着色器程序的各个方面,引导您走过使用顶点和片段函数编写着色器的整个流程.本教程面向着色器.

注解

如果你在着色器方面有一定的经验,只是想知道着色器在Godot中是如何运作的,请参阅 着色器参考.

场景布置

CanvasItem 着色器用于绘制Godot中的所有2D对象,而 Spatial 着色器用于绘制所有3D对象.

要使用着色器,它必须要被附加到一个 材质.若要使用同一材质绘制多个对象,该材质必须附加到每个对象上.

所有继承自 CanvasItem, 精灵, 方块地图, :ref:`2D网格实例<class_meshinstance2d>`等等.它们同时也可以选择性地继承其父类的材质. 如果你有要使用同一材质的很多节点,这个特性就可以派上用场.

首先,创建一个精灵节点.你其实可以用任意的画布项,但是在这个教程中我们用精灵.

在属性面板里,点击”材质”旁边写着”[空]“的地方然后选择”读取”,接着选中”Icon.png”.对于新的项目,这个就是Godot的图标.你现在就会在视区中看到这个图标.

接下来,在属性面板下的画布项部分中,点击”材质”并选择”新着色器材质”.这会创建一个新的材质资源.然后点击新出现的球体.Godot目前还不知道你是要写画布项着色器还是空间着色器,它显示空间着色器的输出预览,所以你看到的是默认的空间着色器的输出.

点击”着色器”并选中”新着色器”.最后,点击新创建的着色器资源,着色器编辑器就会打开.你现在就已经准备好开始写你的第一个着色器了.

你的第一个画布项着色器

在Godot中,所有的着色器第一行都是指定着色器类型的,格式如下:

  1. shader_type canvas_item;

因为我们正在编写CanvasItem着色器,所以我们在第一行中指定了``canvas_item``.我们所有的代码都会在这个声明下面.

这一行告诉游戏引擎要提供你哪些内置变量以及函数.

在Godot中,你可以重写三个函数来控制着色器的运作,它们是``vertex``(顶点函数),``fragment``(片段函数)和``light``(光照函数).本教程会引导你写出一个包含顶点和片段函数的着色器.因为光照函数比另外两个函数要复杂非常多,所以在这里不会进行讲解.

您的第一个片段函数

片段函数对精灵中的每个像素进行操作,并且决定这个像素应该是什么颜色的.

它们限制在精灵覆盖的那些像素中,这也就意味着,你无法用片段函数来实现例如在精灵周围加边框的事情.

最基础的片段函数仅仅给每个像素赋予一个颜色.

我们向内置变量``COLOR``中写入一个``vec4``来做到这点.``vec4``是创建一个四维向量的简写形式.若想进一步了解向量,请参阅 向量数学教程.``COLOR``变量既是片段函数的一个输入,同时也是它的最终输出.

  1. void fragment(){
  2. COLOR = vec4(0.4, 0.6, 0.9, 1.0);
  3. }

../../../_images/blue-box.png

恭喜你!你成功在Godot中写出了你的第一个着色器.

接着,我们来讨论更复杂的事情.

你可以使用片段函数中的很多输入来计算``COLOR``,``UV``就是其中的一个.你的精灵指定了UV坐标(在你不知情的情况下!),而它们告诉着色器,对于网格的每个部分从纹理的何处读取信息.

在片段函数中你只能从``UV``中读取,但是你可以在其他函数中使用,或者直接对``COLOR``赋值.

``UV``取值在0-1之间,从左到右,由上到下.

../../../_images/iconuv.png

  1. void fragment() {
  2. COLOR = vec4(UV, 0.5, 1.0);
  3. }

../../../_images/UV.png

使用内置变量``TEXTURE``

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

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

默认的片段函数从纹理中读取并显示它.当你覆盖了默认的片段函数,你就失去了这个功能,所以你必须自己实现它.你使用”纹理”函数从纹理中读取.某些节点,比如精灵,有一个专用的纹理变量,可以在着色器中使用”纹理”访问.使用它与”UV”和”纹理”一起绘制精灵.

  1. void fragment(){
  2. COLOR = texture(TEXTURE, UV); //read from texture
  3. COLOR.b = 1.0; //set blue channel to 1.0
  4. }

../../../_images/blue-tex.png

Uniform输入

Uniform输入是用来向着色器传递数据的,这些数据在整个着色器中是相同的.

你可以通过在着色器顶部定义来使用Uniform值,如:

  1. uniform float size;

用法的更多详情请参见 着色语言文档 .

添加一个Uniform值来改变精灵中蓝色量.

  1. uniform float blue = 1.0; // you can assign a default value to uniforms
  2. void fragment(){
  3. COLOR = texture(TEXTURE, UV); //read from texture
  4. COLOR.b = blue;
  5. }

现在你可以在编辑器中改变Sprite的蓝色量.回头看看你创建着色器的地方的属性面板.你应该看到一个叫做 “Shader Param” 的部分.展开这个部分,你会看到你刚刚声明的Uniform.如果你在编辑器中改变这个值,它将覆盖你在着色器中提供的默认值.

代码与着色器的交互

你可以使用在节点的材质资源上调用的函数 set_shader_param() ,从代码中改变Uniform.对于一个Sprite节点,可以使用下面的代码来设置 blue Uniform.

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

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

你的第一个顶点函数

现在我们有了一个片段函数,我们再写一个顶点函数.

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

顶点函数中最重要的变量是顶点.最初,它指定模型中的顶点坐标,但你也要写进去决定在哪里画那些顶点.”顶点”是一个”二维向量”,它最初出现在局部空间中(即与摄像机、视图或父节点无关).

您可以通过直接添加到”顶点”来偏移顶点.

  1. void vertex() {
  2. VERTEX += vec2(10.0, 0.0);
  3. }

与内置变量”时间”相结合,可用于简单的动画.

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

总结

着色器的核心,如你所见,是计算 VERTEXCOLOR.你可以制定更复杂的数学策略来给这些变量赋值.

一些更高级的着色器教程可以给你启发,如 Shadertoy着色器之书 .