原文地址:Docs » Shading » Your first shader » Your first Spatial shader
和光交互
首先,将线框模式关闭。再次点击左上角的视窗选项,当前它应该显示透视(Perspective) ,然后选择显示法线(Display Normal)。
我们发现模型的颜色变得扁平了。这是因为模型上的光是扁平的(注原文:This is because the lighting on it is flat)。让我们来添加些光吧。
首先,我们在场景里添加一个全向光(OmniLight)。
你可以看到光对地形产生了影响,但是看起来很奇怪。问题在于,光依然是把这个地形当作一个平面来影响的。因为光着色器(light shader
)依然是使用模型中的法线来计算光照。
法线存储在模型里,但是我们在shader中“改变”了模型的形状,所以原来的法线不再是正确的了。要修正这个问题,我们可以重新计算shader中的法线或者使用一个和我们的噪声相符的法线纹理。在Godot中,这两种方式实现其来都很简单。
你可以在顶点函数中手动计算新的法线,然后把它赋值给NORMAL
,设置完NORMAL
以后,Godot可以为我们处理所有复杂的光照计算。我们将在本教程的下个部分介绍这个方法。现在我们使用从一个纹理中读取法线的方法。
我们通过传入第2张噪声纹理的方式,再次依靠NoiseTexture`来计算法线。
uniform sampler2D normalmap;
再次使用OpenSimplexNoise
生成一个NoiseTexture
设置给这个uniform
变量。这一次勾选上As Normalmap
选项。
因为这是一个法线图(Normalmap
)而不是逐顶点法线(per-vertex normal
),我们将在片元函数中指定它。我们将在本教程的下一部分对片元函数做更详细的介绍。
void fragment() {
}
如果我们有一些对应顶点的法线,我们应当设置NORMAL
属性,而如果我们有一个由纹理生成的法线图,我们应当设置NORMALMAP
属性。这样,Godot会自动将纹理包裹到模型上。
最后,为了确保我们从噪声纹理和法线图纹理的相同位置读取,我们将使用varying
变量从顶点函数中传递VERTEX.xz
到片元函数中。
在顶点函数的上面定义一个名为vertex_position
的vec2
的变量。在顶点函数中将VERTEX.xz
赋值给vertex_position
。
varying vec2 vertex_position;
void vertex() {
...
vertex_position = VERTEX.xz / 2.0;
}
当法线就位以后,光线可以与模型的高度动态地作用了。
我们可以左右拖拽一下光源,光照会自动更新。
以下是本教程的全部代码。你可以看到,它并不是很长,因为Godot已经为你解决了绝大多数繁琐的工作。
shader_type spatial;
uniform float height_scale = 0.5;
uniform sampler2D noise;
uniform sampler2D normalmap;
varying vec2 vertex_position;
void vertex() {
vertex_position = VERTEX.xz / 2.0;
float height = texture(noise, vertex_position).x * height_scale;
}
void fragment() {
NORMALMAP = texture(normalmap, vertex_position).xyz;
}
以上就是本部分的全部内容了。希望你已经理解了Godot中顶点着色器的基础知识。在本教程的下一个部分,我们将写一个片元函数来配合这个顶点函数,并且我们还会介绍一些高级技术,甚至可以把这个地形变成海洋中运动的波浪。