第一个空间着色:第2部分

从高层来看,godot所做的就是为用户提供一系列可以随意设置的参数。 (AOSSS_StrengthRIM 等等)。这些参数对应于不同的复杂效果(环境遮挡、次表面散射、边缘照明等)。当不写入时,代码在编译之前就会被抛出,因此着色器不会产生额外功能的成本。这使得用户很容易获得复杂的PBR正确的着色,而不需要编写复杂的着色。当然,godot还允许您忽略所有这些参数,并编写一个完全定制的明暗器。

有关这些参数的完整列表,请参阅 spatial shader 参考文件

顶点函数和片段函数的区别在于,顶点函数按顶点运行并设置属性,例如 VERTEX (位置)和 NORMAL ,而片段明暗器按像素运行,最重要的是,设置 ALBEDO 的颜色 Mesh .

你的第一个空间碎片函数

如本教程上一部分所述。godot中fragment函数的标准用法是设置不同的材质属性,让godot处理其余的属性。为了提供更大的灵活性,Godot还提供了称为渲染模式的东西。渲染模式设置在明暗器的顶部,正下方 shader_type 它们指定了您希望着色器的内置方面具有何种功能。

例如,如果不希望灯光影响对象,请将渲染模式设置为 unshaded

render_mode unshaded;

也可以将多个渲染模式堆叠在一起。例如,如果要使用“Toon着色”而不是更逼真的PBR着色,请将“漫反射”模式和“镜面反射”模式设置为“Toon:

render_mode diffuse_toon, specular_toon;

此内置功能模型允许您只更改几个参数来编写复杂的自定义明暗器。

有关渲染模式的完整列表,请参见 Spatial shader reference .

在本部分教程中,我们将介绍如何从上一部分中获取崎岖不平的地形,并将其转化为海洋。

首先让我们设定水的颜色。我们通过设置 ALBEDO .

ALBEDO 是一个 vec3 包含对象颜色的。

让我们把它设置成一个很好的蓝色阴影。

void fragment() {
  ALBEDO = vec3(0.1, 0.3, 0.5);
}
../../../_images/albedo.png

我们把它设置成一个非常深的蓝色阴影,因为水的蓝色大部分来自天空的反射。

Godot使用的PBR模型依赖于两个主要参数: METALLICROUGHNESS .

ROUGHNESS 指定材质表面的平滑度/粗糙度。低 ROUGHNESS 会使材料看起来像一个闪亮的塑料,而高粗糙度使材料在颜色上看起来更坚固。

METALLIC 指定对象与金属的相似程度。最好靠近 01 . 想想 METALLIC 改变反射和 ALBEDO 颜色。高 METALLIC 几乎忽略 ALBEDO 总而言之,看起来像一面天空的镜子。当一个低 METALLIC 有一个更平等的天空颜色表示和 ALBEDO 颜色。

ROUGHNESS 增加自 01 从左到右同时 METALLIC 增加自 01 从上到下。

../../../_images/PBR.png

注解

METALLIC 应该接近 01 适当的PBR阴影。仅在它们之间设置,以便在材质之间混合。

水不是金属,所以我们要把它 METALLIC 属性到 0.0 . 水也是高度反光的,所以我们将 ROUGHNESS 财产也相当低。

void fragment() {
  METALLIC = 0.0;
  ROUGHNESS = 0.01;
  ALBEDO = vec3(0.1, 0.3, 0.5);
}
../../../_images/plastic.png

现在我们有一个光滑的塑料表面。现在是时候考虑一些我们想要模拟的水的特殊性质了。有两个主要的将这个从一个奇怪的塑料表面到漂亮的风格化的水。第一种是镜面反射。镜面反射是指从太阳直接反射到眼睛的地方看到的那些亮点。第二个是菲涅耳反射。菲涅耳反射是物体在较浅的角度上变得更具反射性的特性。这就是为什么你可以看到你下面的水,但在更远的地方它反射天空的原因。

为了增加镜面反射,我们要做两件事。首先,我们将“镜面反射”的渲染模式更改为“Toon”,因为“Toon渲染”模式具有较大的镜面高光。

render_mode specular_toon;
../../../_images/specular-toon.png

其次,我们将增加边缘照明。边缘照明增加了光线在斜角的效果。通常它被用来模拟光线通过物体边缘织物的方式,但我们将在这里使用它来帮助实现一个很好的水样效果。

void fragment() {
  RIM = 0.2;
  METALLIC = 0.0;
  ROUGHNESS = 0.01;
  ALBEDO = vec3(0.1, 0.3, 0.5);
}
../../../_images/rim.png

为了增加菲涅尔反射,我们将在片段着色中计算菲涅尔项。我们不打算使用真正的菲涅耳项,而是使用 NORMALVIEW 向量。这个 NORMAL 矢量指向远离的曲面,而 VIEW 矢量是你的眼睛和表面上那个点之间的方向。它们之间的点积是一个很方便的方法,可以告诉你什么时候你正以一个角度看表面。

float fresnel = sqrt(1.0 - dot(NORMAL, VIEW));

把它们混合在一起 ROUGHNESSALBEDO . 这是明暗器材质相对于空间材质的好处。对于空间材质,我们可以使用纹理或平面数设置这些属性。但是有了明暗器,我们可以根据我们能想到的任何数学函数来设置它们。

void fragment() {
  float fresnel = sqrt(1.0 - dot(NORMAL, VIEW));
  RIM = 0.2;
  METALLIC = 0.0;
  ROUGHNESS = 0.01 * (1.0 - fresnel);
  ALBEDO = vec3(0.1, 0.3, 0.5) + (0.1 * fresnel);
}
../../../_images/fresnel.png

现在,只有5行代码,您就可以拥有复杂的外观。既然我们有了灯光,这水看起来太亮了。让我们把它变暗。这很容易通过减少 vec3 我们进入 ALBEDO . 让我们把它们设置为 vec3(0.01, 0.03, 0.05) .

../../../_images/dark-water.png

使用设置动画 TIME

回到顶点函数,我们可以使用内置变量为波设置动画。 TIME .

TIME 是一个内置变量,可从顶点和片段函数访问。

在上一个教程中,我们通过读取高度图来计算高度。对于本教程,我们将进行同样的操作。将高度图代码放入名为 height() .

float height(vec2 position) {
  return texture(noise, position / 10.0).x; //scaling factor is based on mesh size (This PlanMesh is 10x10)
}

为了使用 TIMEheight() 我们需要传递函数。

float height(vec2 position, float time) {
}

确保在顶点函数中正确传递。

void vertex() {
  vec2 pos = VERTEX.xz;
  float k = height(pos, TIME);
  VERTEX.y = k;
}

而不是使用法线贴图来计算法线。我们将在 vertex() 功能。为此,请使用以下代码行。

NORMAL = normalize(vec3(k - height(pos + vec2(0.1, 0.0), TIME), 0.1, k - height(pos + vec2(0.0, 0.1), TIME)));

我们需要计算 NORMAL 手工操作,因为在下一节中,我们将使用数学来创建复杂的波。

现在,我们要 height() 通过偏移使功能更加复杂 position 通过的余弦 TIME .

float height(vec2 position, float time) {
  vec2 offset = 0.01 * cos(position + time);
  return texture(noise, (position / 10.0) - offset).x;
}

这会导致波浪移动缓慢,但不是以非常自然的方式。下一节将通过添加更多的数学函数,更深入地探讨如何使用材质球来创建更复杂的效果(在本例中是真实的波)。

高级效果:波浪

使明暗器如此强大的是,您可以通过使用数学实现复杂的效果。为了说明这一点,我们将通过修改 height() 通过引入一个新的函数 wave() .

wave() 有一个参数, position ,与中的相同 height() .

我们要打电话 wave() 在中多次 height() 为了伪装海浪的样子。

float wave(vec2 position){
  position += texture(noise, position / 10.0).x * 2.0 - 1.0;
  vec2 wv = 1.0 - abs(sin(position));
  return pow(1.0 - pow(wv.x * wv.y, 0.65), 4.0);
}

起初,这看起来很复杂。所以让我们一行一行地通过它。

position += texture(noise, position / 10.0).x * 2.0 - 1.0;

将位置偏移 noise 纹理。这将使波成为曲线,因此它们不是与网格完全对齐的直线。

vec2 wv = 1.0 - abs(sin(position));

使用定义类波函数 sin()position . 正常情况下 sin() 波浪很圆。我们使用 abs() 到绝对值,以给它们一个尖锐的脊线并将它们限制在0-1范围内。然后我们从中减去 1.0 把山顶放在上面。

return pow(1.0 - pow(wv.x * wv.y, 0.65), 4.0);

将x方向波乘以y方向波,并将其提高到一个功率,以锐化峰值。然后从中减去 1.0 使山脊成为山峰,并将其提升到锐化山脊的力量。

我们现在可以替换 height() 功能 wave() .

float height(vec2 position, float time) {
  float h = wave(position);
}

使用这个你可以得到:

../../../_images/wave1.png

sin波的形状太明显了。所以让我们把波浪展开一点。我们通过缩放 position .

float height(vec2 position, float time) {
  float h = wave(position*0.4);
}

现在看起来好多了。

../../../_images/wave2.png

如果我们在不同的频率和振幅下将多个波叠加在一起,我们可以做得更好。这意味着我们要按比例调整每一个波的位置,使波变薄或变宽(频率)。我们要乘以波的输出,使它们变短或变高(振幅)。

下面是一个例子,说明如何将四个波分层以获得更好的波。

float height(vec2 position, float time) {
  float d = wave((position + time) * 0.4, 8.0) * 0.3;
  d += wave((position - time) * 0.3, 8.0) * 0.3;
  d += wave((position + time) * 0.5, 4.0) * 0.2;
  d += wave((position - time) * 0.6, 4.0) * 0.2;
  return d;
}

注意,我们把时间加在2上,然后从另外两个减去它。这使得波向不同的方向移动,产生了复杂的效果。还要注意,振幅(结果乘以的数字)加起来等于 1.0 . 这将使波保持在0-1范围内。

有了这段代码,您将得到更复杂的波形,您所要做的就是添加一点数学知识!

../../../_images/wave3.png

有关空间明暗器的详细信息,请阅读 Shading Language 医生和 Spatial Shaders 博士。还可以在 Shading section 以及 3D 部分。