简单模式 7 公式/示例?
Simple mode 7 formula / example?
我最近发现了利用 SNES 模式 7 的伪 3d 效果,并想尝试在 Godot 引擎中复制它。我尝试在网上四处寻找,但所有内容要么以我无法理解的方式解释,要么以我不知道的编程语言解释。我还需要学习如何旋转该区域,以及如何将精灵作为角色或敌人放入,但我没有在这些上找到任何东西。谁能解释一下这个公式,以及我如何实现它?
好的,我明白了。我将解释两种设置。
在开始之前,让我解释一下我们将使用的着色器代码:
shader_type canvas_item;
uniform mat3 matrix;
void fragment()
{
vec3 uv = matrix * vec3(UV, 1.0);
COLOR = texture(TEXTURE, uv.xy / uv.z);
}
这是一个 canvas_item
着色器,因此它旨在在 2D 中工作。我们正在做的是将变换矩阵(作为 uniform
传递)应用到纹理坐标 (UV
)。我们存储在 uv
变量中的结果。我们将使用它来对使用此着色器的任何节点的纹理进行采样……但是我们需要使用 uv
的 z
来制作透视效果。为此,我们将 uv.xy
除以 uv.z
。
但是,我想将它应用到纹理中心。所以,让我在开始处减去 0.5
,并在末尾添加 0.5
:
shader_type canvas_item;
uniform mat3 matrix;
void fragment()
{
vec3 uv = matrix * vec3(UV - 0.5, 1.0);
COLOR = texture(TEXTURE, (uv.xy / uv.z) + 0.5);
}
还有一件事。我不喜欢在极端值上我们看到相反的图像。因此,我会这样处理:
shader_type canvas_item;
uniform mat3 matrix;
void fragment()
{
vec3 uv = matrix * vec3(UV - 0.5, 1.0);
if (uv.z < 0.0) discard;
COLOR = texture(TEXTURE, (uv.xy / uv.z) + 0.5);
}
这里给无分支爱好者一个替代方案(不知道是不是更好):
shader_type canvas_item;
uniform mat3 matrix;
void fragment()
{
vec3 uv = matrix * vec3(UV - 0.5, 1.0);
COLOR = texture(TEXTURE, (uv.xy / uv.z) + 0.5);
COLOR.a *= sign(sign(uv.z) + 1.0);
}
此处 sign(uv.z)
将是 -1.0
、0.0
或 1.0
。
然后 sign(uv.z) + 1.0
它将是 0.0
、1.0
或 2.0
。
最后 sign(sign(uv.z) + 1.0)
将是 0.0
或 1.0
(如果您愿意,可以使用 clamp(sign(uv.z), 0.0, 1.0)
)。因此 COLOR.a *= sign(sign(uv.z) + 1.0)
将 alpha 乘以 0.0
任何地方 uv.z
为负。
注意:我之所以在片段着色器中操作UV坐标,而不是在顶点着色器中操作,是因为Godot正在为2D做仿射纹理映射。这会导致失真。这是一个解决方法。
第一个设置只是一个精灵。用你想要的任何纹理设置 Sprite
,并将 material 设置为新的着色器 material,并在着色器中使用我在开始时显示的代码。
Godot 将为您提供编辑 material 资源中 Shader Param
下的 uniform mat3 matrix
的选项。默认情况下它将是单位矩阵,在编辑器中看起来像这样:
x 1 y 0 z 0
x 0 y 1 z 0
x 0 y 0 z 1
您可以使用它来应用旋转、缩放、剪切或透视变换。 *我建议从更改z列(最右边的一个)的零开始,控制视角:
x 1 y 0 z 3d_rotate_horizontal
x 0 y 1 z 3d_rotate_vertical
x 0 y 0 z scale
示例结果:
一条建议:不要使用一直延伸到边缘的纹理。当您应用透视时,着色器将读取超出边缘的内容,但默认情况下它被夹紧,这会导致拉伸纹理边缘的任何像素。
顺便说一句,如果您将图像导入为 Image
(而不是默认的 Texture
),您可以将 Sprite 纹理设置为 ImageTexture
,这会给您一些对纹理显示方式的额外控制,包括启用 mipmap、抗锯齿过滤器和重复超出其边缘的纹理(镜像和非镜像)。
第二个更复杂的设置适用于多个对象。这也是适用于 TileMap
的设置。您将需要此树结构:
- Sprite2D
+- Viewport
+- Camera2D
+- target
将 Sprite2D
放置在我们想要看到的位置,使用我在开始时显示的着色器代码为其提供一个着色器 material。 顺便说一句,这也应该与 TextureRect
一起使用,以防您在 UI 中需要它。
不要为Sprite2D
(或TextureRect
)设置纹理。您将附加一个如下所示的脚本:
extends Sprite
func _ready():
var viewport = $Viewport
yield(get_tree(), "idle_frame")
yield(get_tree(), "idle_frame")
texture = viewport.get_texture()
如果需要,将 Sprite
更改为 TextureRect
。
此代码引用 Viewport
节点,等待两帧(以确保 Viewport
纹理可用)然后获取纹理并将其分配给自身。
您需要为视口指定您想要的大小。另外我建议设置 Transparent Bg
和 V Flip
。 Camera2D
可以保留其默认值。
最后“目标”就是你想展示的任何东西。它可以是一个或多个二维节点。我建议将它变成另一个场景,这样就可以很容易地独立于此设置对其进行编辑(Viewport
的任何子项都不会在编辑器中显示)。
示例结果:
是的,我们可以在 Godot 中用实际 3D 存档同样的效果,没问题。但我们没有。我们选择用2D工具来实现这个效果,然后做其他的事情,不是因为容易,而是因为很难。
此答案中使用的纹理是 public 域 (CC0),来自 Kenney。
我最近发现了利用 SNES 模式 7 的伪 3d 效果,并想尝试在 Godot 引擎中复制它。我尝试在网上四处寻找,但所有内容要么以我无法理解的方式解释,要么以我不知道的编程语言解释。我还需要学习如何旋转该区域,以及如何将精灵作为角色或敌人放入,但我没有在这些上找到任何东西。谁能解释一下这个公式,以及我如何实现它?
好的,我明白了。我将解释两种设置。
在开始之前,让我解释一下我们将使用的着色器代码:
shader_type canvas_item;
uniform mat3 matrix;
void fragment()
{
vec3 uv = matrix * vec3(UV, 1.0);
COLOR = texture(TEXTURE, uv.xy / uv.z);
}
这是一个 canvas_item
着色器,因此它旨在在 2D 中工作。我们正在做的是将变换矩阵(作为 uniform
传递)应用到纹理坐标 (UV
)。我们存储在 uv
变量中的结果。我们将使用它来对使用此着色器的任何节点的纹理进行采样……但是我们需要使用 uv
的 z
来制作透视效果。为此,我们将 uv.xy
除以 uv.z
。
但是,我想将它应用到纹理中心。所以,让我在开始处减去 0.5
,并在末尾添加 0.5
:
shader_type canvas_item;
uniform mat3 matrix;
void fragment()
{
vec3 uv = matrix * vec3(UV - 0.5, 1.0);
COLOR = texture(TEXTURE, (uv.xy / uv.z) + 0.5);
}
还有一件事。我不喜欢在极端值上我们看到相反的图像。因此,我会这样处理:
shader_type canvas_item;
uniform mat3 matrix;
void fragment()
{
vec3 uv = matrix * vec3(UV - 0.5, 1.0);
if (uv.z < 0.0) discard;
COLOR = texture(TEXTURE, (uv.xy / uv.z) + 0.5);
}
这里给无分支爱好者一个替代方案(不知道是不是更好):
shader_type canvas_item;
uniform mat3 matrix;
void fragment()
{
vec3 uv = matrix * vec3(UV - 0.5, 1.0);
COLOR = texture(TEXTURE, (uv.xy / uv.z) + 0.5);
COLOR.a *= sign(sign(uv.z) + 1.0);
}
此处 sign(uv.z)
将是 -1.0
、0.0
或 1.0
。
然后 sign(uv.z) + 1.0
它将是 0.0
、1.0
或 2.0
。
最后 sign(sign(uv.z) + 1.0)
将是 0.0
或 1.0
(如果您愿意,可以使用 clamp(sign(uv.z), 0.0, 1.0)
)。因此 COLOR.a *= sign(sign(uv.z) + 1.0)
将 alpha 乘以 0.0
任何地方 uv.z
为负。
注意:我之所以在片段着色器中操作UV坐标,而不是在顶点着色器中操作,是因为Godot正在为2D做仿射纹理映射。这会导致失真。这是一个解决方法。
第一个设置只是一个精灵。用你想要的任何纹理设置 Sprite
,并将 material 设置为新的着色器 material,并在着色器中使用我在开始时显示的代码。
Godot 将为您提供编辑 material 资源中 Shader Param
下的 uniform mat3 matrix
的选项。默认情况下它将是单位矩阵,在编辑器中看起来像这样:
x 1 y 0 z 0
x 0 y 1 z 0
x 0 y 0 z 1
您可以使用它来应用旋转、缩放、剪切或透视变换。 *我建议从更改z列(最右边的一个)的零开始,控制视角:
x 1 y 0 z 3d_rotate_horizontal
x 0 y 1 z 3d_rotate_vertical
x 0 y 0 z scale
示例结果:
一条建议:不要使用一直延伸到边缘的纹理。当您应用透视时,着色器将读取超出边缘的内容,但默认情况下它被夹紧,这会导致拉伸纹理边缘的任何像素。
顺便说一句,如果您将图像导入为 Image
(而不是默认的 Texture
),您可以将 Sprite 纹理设置为 ImageTexture
,这会给您一些对纹理显示方式的额外控制,包括启用 mipmap、抗锯齿过滤器和重复超出其边缘的纹理(镜像和非镜像)。
第二个更复杂的设置适用于多个对象。这也是适用于 TileMap
的设置。您将需要此树结构:
- Sprite2D
+- Viewport
+- Camera2D
+- target
将 Sprite2D
放置在我们想要看到的位置,使用我在开始时显示的着色器代码为其提供一个着色器 material。 顺便说一句,这也应该与 TextureRect
一起使用,以防您在 UI 中需要它。
不要为Sprite2D
(或TextureRect
)设置纹理。您将附加一个如下所示的脚本:
extends Sprite
func _ready():
var viewport = $Viewport
yield(get_tree(), "idle_frame")
yield(get_tree(), "idle_frame")
texture = viewport.get_texture()
如果需要,将 Sprite
更改为 TextureRect
。
此代码引用 Viewport
节点,等待两帧(以确保 Viewport
纹理可用)然后获取纹理并将其分配给自身。
您需要为视口指定您想要的大小。另外我建议设置 Transparent Bg
和 V Flip
。 Camera2D
可以保留其默认值。
最后“目标”就是你想展示的任何东西。它可以是一个或多个二维节点。我建议将它变成另一个场景,这样就可以很容易地独立于此设置对其进行编辑(Viewport
的任何子项都不会在编辑器中显示)。
示例结果:
是的,我们可以在 Godot 中用实际 3D 存档同样的效果,没问题。但我们没有。我们选择用2D工具来实现这个效果,然后做其他的事情,不是因为容易,而是因为很难。
此答案中使用的纹理是 public 域 (CC0),来自 Kenney。