什么是着色器?

简介

你已经决定尝试编写着色器了. 你可能已经听说过, 它们可用来快速创造非常有趣的效果. 可能也听说过它们很可怕. 以上两种说法都对.

着色器可以用来创建广泛的效果(事实上, 现代渲染引擎中绘制的所有东西都是用着色器绘制的).

对于不熟悉着色器的人来说, 编写着色器也是非常困难的.Godot试图通过公开许多有用的内置特性, 并为您处理一些较低级别的初始化工作, 使编写着色器变得更容易一些. 然而,GLSL (OpenGL着色语言, 也是Godot使用的)仍然是不直观和受限制的, 特别是对于习惯使用GDScript的用户.

可是它是什么?

着色器是一种在图形处理单元(GPU)上运行的特殊程序. 大多数电脑都有GPU, 一个集成到他们的CPU或离散的(这意味着它是一个单独的硬件组件, 例如, 标准显卡).GPU对于呈现特别有用, 因为它们被优化为并行运行数千条指令.

着色器的输出通常是绘制到视图端口的对象的彩色像素. 但是一些着色器允许特殊的输出(对于像Vulkan这样的应用程序接口尤其如此). 着色器在着色器管道中操作. 标准流程是顶点->片段着色器管道. 顶点着色器用于决定每个顶点的位置(3D模型中的点, 或者精灵的一角)去和片段着色器决定什么颜色个别像素接收.

假设你想要将纹理中的所有像素更新为给定的颜色, 你可以在CPU上这样写:

for x in range(width):
  for y in range(height):
    set_color(x, y, some_color)

在着色器中, 你只能访问循环的内部, 所以你写的是这样的:

// function called for each pixel
void fragment() {
  COLOR = some_color;
}

您无法控制如何调用此函数. 您必须设计不同于在中央处理器上设计程序的着色器.

着色器管道的一个后果是, 您无法访问着色器上次运行的结果, 您无法从正在绘制的像素中访问其他像素, 而且不能在当前绘制的像素之外编写. 这使得GPU能够并行地为不同的像素执行着色器, 因为他们彼此不耦合. 这种灵活性的缺乏是为GPU的工作设计, 使着色器难以置信的快.

它能做什么

  • 快速定位顶点

  • 快速计算颜色

  • 快速计算光照

  • 进行大量的数学计算

它不能做什么

  • 绘制外部网格

  • 从当前像素(或顶点)访问其他像素

  • 储存之前的迭代

  • 动态更新(可以, 但是需要编译)

着色器的结构

在Godot中, 着色器由3个主要函数组成:"vertex()" 函数,"fragment()" 和 "light()" 函数.

"vertex()" 函数运行在网格中的所有顶点上, 并设置它们的位置以及部分其他每个顶点的变量.

函数的作用是: 为网格所覆盖的每个像素运行 "fragment()" 函数. 它使用 "vertex()" 函数中的变量来运行."vertex()" 函数中的变量在顶点之间进行插值, 以提供 "fragment()" 函数的值.

"light()" 函数用于每个像素和每束光照. 它从 "fragment()" 函数和以前的运行中获取变量.

更多关于着色器在Godot中具体操作的信息, 请参见 Shaders 文档.

警告

如果启用了 vertex_lighting 渲染模式, 或者在项目设置中启用了 Rendering渲染>Quality质量>Shading着色>强制顶点着色 , 则不会运行 light() 函数.(在移动平台上默认启用.)

技术概述

GPU渲染图形的速度要比CPU快得多, 原因如下: 但最值得注意的是, 它们能够大规模并行运行计算.CPU通常有4或8个内核, 而GPU通常有数千个. 这意味着GPU可以一次完成数百项任务.GPU架构师已经利用了这种方式, 允许快速地进行许多计算, 但只有当多个或所有核心同时进行相同的计算时, 但数据不同.

这就是着色器的作用.GPU会同时调用着色器, 然后对不同的数据位(顶点或像素)进行操作. 这些成束的数据通常被称为wavefront. 着色器将对wavefront中的每一个线程运行相同的内容. 例如, 如果一个给定的GPU可以处理每个wavefront的100个线程, 一个wavefront将一起运行在10×10的像素块上. 它将继续为该wavefront的所有像素运行, 直到它们完成. 相应地, 如果你有一个像素比其他像素慢(由于过多的分支), 整个块将被减慢, 导致渲染时间大量减慢.

这与基于CPU的操作不同. 在CPU上, 如果您可以将速度提高哪怕只有一个像素, 整个渲染时间就会减少. 在GPU上, 您必须加快整个wavefront的速度才能加快渲染速度.