着色器简介
计算机图形学领域中,着色器(Shader)是一种计算机程序。原本用于进行图像的浓淡处理(计算图像中的光照、亮度、颜色等),但近来,也被用于完成很多不同领域的工作,比如处理CG特效、进行与浓淡处理无关的影片后期处理、甚至用于一些与计算机图形学无关的其它领域。
使用着色器在图形硬件上计算渲染效果有很高的自由度。尽管不是硬性要求,但目前大多数着色器是针对GPU开发的。GPU的可编程绘图管线已经全面取代传统的固定管线,可以使用着色器语言对其编程。构成最终图像的像素、顶点、纹理,它们的位置、色相、饱和度、亮度、对比度也都可以利用着色器中定义的算法进行动态调整。调用着色器的外部程序,也可以利用它向着色器提供的外部变量、纹理来修改这些着色器中的参数。
在电影后期处理、计算机成像、电子游戏等领域,着色器常被用来制作各种特效。除了普通的光照模型,着色器还可以调整图像的色相、饱和度、亮度、对比度,生成模糊、高光、有体积光源、失焦、卡通渲染、色调分离、畸变、凹凸贴图、色键(即所谓的蓝幕、绿幕抠像效果)、边缘检测等效果。
因为天生就是并行的,所以着色器处理信息的方式与普通的程序有所不同。着色器代码是单独针对顶点或像素执行的。你也无法在帧与帧之间存储数据。因此,使用着色器时,你需要使用与其他编程语言不同的编码和思考方式。
假设你想要把纹理中的所有像素点都设置成某个给定的颜色。使用IVRScript,你的代码需要使用 for
循环:
for x in range(width):
for y in range(height):
set_color(x, y, some_color)
而在着色器中,代码相对于每个像素并行执行,所以对应代码如下:
void fragment():
COLOR = some_color;
IdeaXR中的着色器使用
IdeaXR 中的着色语言是基于流行的 OpenGL 着色语言(GLSL)的简化。引擎会为你处理一些底层的初始化工作,让编写复杂着色器更为简单。
着色器由三个主要函数组成:vertex()、fragment()、light()。
1. vertex()
函数运行在网格的所有顶点上,可以对顶点的变量进行修改设置。
2. fragment()
函数运行在网格所覆盖的每一个像素上。它会使用 vertex() 函数所输出的值,这些值会在顶点之间进行插值。
3. light()
函数运行在每一个像素以及每一盏灯光上。它的变量是从 fragment() 函数以及之前的运行中获取的。
关于在 IdeaXR 中的着色器添加,我们首先给网格实例节点添加着色器材质:
之后在着色器一栏中新建着色器即可完成着色器的添加:
你可以选择创建着色器和可视化着色器。其中着色器只能用着色器语言编写,而可视化着色器则把着色器中的内置方法封装成了一个个模块,类似于IdeaXR中的交互编辑器的形式。你也可以在可视化着色器中使用着色器语言,以及将其转化为着色器代码。
着色器类型
在IdeaXR中,你所编写的着色器必须指定类型(spatial,canvas_item,particles),不存在所有场景都可以使用的通用配置。不同的类型支持不同的渲染模式、内置变量、处理函数。
编写着色器时必须在第一行指定类型,具体的添加方式类似于下面这样:
shader_type spatial;
可以使用的类型如下:
- 用于 3D 渲染的 spatial。
- 用于 2D 渲染的 canvas_item。
- 用于粒子系统的 particles。
渲染模式
渲染模式需要在第二行指定,也就是在着色器类型之后,类似于下面这样:
shader_type spatial;
render_mode unshaded;
渲染模式会修改 IdeaXR 应用着色器的方式。例如, unshaded
模式会让引擎跳过内置的光线处理器函数。
每种着色器类型都有不同的渲染模式。每种着色器类型的完整渲染模式列表请参阅spatial 渲染模式、canvas_item 渲染模式、particles 渲染模式着色器具体介绍。
处理器函数
根据着色器类型的不同,你可以覆盖不同的处理器函数。在 spatial
和 canvas_item
中,你可以使用 vertex()
、fragment()
、light()
。而在 particles
中则只能使用 vertex()
。
顶点处理器
在 spatial
和 canvas_item
着色器中,会为每一个顶点调用 vertex()
处理函数。在 particles
着色器中则会为每一个粒子调用一次。
你的世界中的几何体上,每一个顶点都有位置、颜色等属性。该函数会修改这些值,并将其传入片段函数。
对于顶点位置进行操作的的能力具有广泛的应该场合:织物模拟、粒子系统的点尺寸处理,顶点动画,计算光照,UV计算等等。
片段处理器
fragment()
处理函数的作用是对每个像素进行修改设置,计算像素最后的颜色输出。这里的代码会在绘制的对象或图元的每一个可见像素上执行。只能在 spatial
和 canvas_item
着色器中使用。
片段函数的标准用途是设置用于计算光照的材质属性。你可以为 ROUGHNESS
、RIM
、TRNASMISSION
等属性设置值,告诉光照函数光照应该如何处理对应的片段,这样就可以控制复杂的着色管线,而不必让用户编写过多的代码。这等效于修改 SpatialMaterial
中的对应属性,而在着色器中可以更为自由的设置修改这些属性,以达到
SpatialMaterial
所不能表现出的效果。
光照处理器
light()
处理器也会在每一个像素上运行,并且同时还会在每一个影响该对象的灯光上运行。如果没有灯光影响该对象则不会运行。它会被用于 fragment()
处理器,一般会在 fragment()
函数中进行材质属性设置时执行。