第一个空间着色:第2部分¶
从高层来看,godot所做的就是为用户提供一系列可以随意设置的参数。 (AO
, SSS_Strength
, RIM
等等)。这些参数对应于不同的复杂效果(环境遮挡、次表面散射、边缘照明等)。当不写入时,代码在编译之前就会被抛出,因此着色器不会产生额外功能的成本。这使得用户很容易获得复杂的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);
}
我们把它设置成一个非常深的蓝色阴影,因为水的蓝色大部分来自天空的反射。
Godot使用的PBR模型依赖于两个主要参数: METALLIC
和 ROUGHNESS
.
ROUGHNESS
指定材质表面的平滑度/粗糙度。低 ROUGHNESS
会使材料看起来像一个闪亮的塑料,而高粗糙度使材料在颜色上看起来更坚固。
METALLIC
指定对象与金属的相似程度。最好靠近 0
或 1
. 想想 METALLIC
改变反射和 ALBEDO
颜色。高 METALLIC
几乎忽略 ALBEDO
总而言之,看起来像一面天空的镜子。当一个低 METALLIC
有一个更平等的天空颜色表示和 ALBEDO
颜色。
ROUGHNESS
增加自 0
到 1
从左到右同时 METALLIC
增加自 0
到 1
从上到下。
注解
METALLIC
应该接近 0
或 1
适当的PBR阴影。仅在它们之间设置,以便在材质之间混合。
水不是金属,所以我们要把它 METALLIC
属性到 0.0
. 水也是高度反光的,所以我们将 ROUGHNESS
财产也相当低。
void fragment() {
METALLIC = 0.0;
ROUGHNESS = 0.01;
ALBEDO = vec3(0.1, 0.3, 0.5);
}
现在我们有一个光滑的塑料表面。现在是时候考虑一些我们想要模拟的水的特殊性质了。有两个主要的将这个从一个奇怪的塑料表面到漂亮的风格化的水。第一种是镜面反射。镜面反射是指从太阳直接反射到眼睛的地方看到的那些亮点。第二个是菲涅耳反射。菲涅耳反射是物体在较浅的角度上变得更具反射性的特性。这就是为什么你可以看到你下面的水,但在更远的地方它反射天空的原因。
为了增加镜面反射,我们要做两件事。首先,我们将“镜面反射”的渲染模式更改为“Toon”,因为“Toon渲染”模式具有较大的镜面高光。
render_mode specular_toon;
其次,我们将增加边缘照明。边缘照明增加了光线在斜角的效果。通常它被用来模拟光线通过物体边缘织物的方式,但我们将在这里使用它来帮助实现一个很好的水样效果。
void fragment() {
RIM = 0.2;
METALLIC = 0.0;
ROUGHNESS = 0.01;
ALBEDO = vec3(0.1, 0.3, 0.5);
}
为了增加菲涅尔反射,我们将在片段着色中计算菲涅尔项。我们不打算使用真正的菲涅耳项,而是使用 NORMAL
和 VIEW
向量。这个 NORMAL
矢量指向远离的曲面,而 VIEW
矢量是你的眼睛和表面上那个点之间的方向。它们之间的点积是一个很方便的方法,可以告诉你什么时候你正以一个角度看表面。
float fresnel = sqrt(1.0 - dot(NORMAL, VIEW));
把它们混合在一起 ROUGHNESS
和 ALBEDO
. 这是明暗器材质相对于空间材质的好处。对于空间材质,我们可以使用纹理或平面数设置这些属性。但是有了明暗器,我们可以根据我们能想到的任何数学函数来设置它们。
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);
}
现在,只有5行代码,您就可以拥有复杂的外观。既然我们有了灯光,这水看起来太亮了。让我们把它变暗。这很容易通过减少 vec3
我们进入 ALBEDO
. 让我们把它们设置为 vec3(0.01, 0.03, 0.05)
.
使用设置动画 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)
}
为了使用 TIME
在 height()
我们需要传递函数。
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);
}
使用这个你可以得到:
sin波的形状太明显了。所以让我们把波浪展开一点。我们通过缩放 position
.
float height(vec2 position, float time) {
float h = wave(position*0.4);
}
现在看起来好多了。
如果我们在不同的频率和振幅下将多个波叠加在一起,我们可以做得更好。这意味着我们要按比例调整每一个波的位置,使波变薄或变宽(频率)。我们要乘以波的输出,使它们变短或变高(振幅)。
下面是一个例子,说明如何将四个波分层以获得更好的波。
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范围内。
有了这段代码,您将得到更复杂的波形,您所要做的就是添加一点数学知识!
有关空间明暗器的详细信息,请阅读 Shading Language 医生和 Spatial Shaders 博士。还可以在 Shading section 以及 3D 部分。