【问题标题】:Simple mode 7 formula / example?简单模式7公式/例子?
【发布时间】:2021-07-20 02:38:06
【问题描述】:

我最近发现了使用 SNES 模式 7 的伪 3d 效果,并想尝试在 Godot 引擎中复制它。我尝试在网上四处查看,但所有内容要么以我无法理解的方式进行解释,要么以我不知道的编程语言进行解释。我还需要学习如何旋转该区域,并将精灵作为角色或敌人放入,但我没有找到任何东西。谁能解释一下这个公式,以及我如何实现它?

【问题讨论】:

  • 1. Godot可以做3D,为什么不做3D?使用 3D 版本的 TileMap(GridMap 除外),您可以使用 3D 工具在 Godot 中制作“2D”游戏,然后给出 3D 矩阵变换。 2. 您可以渲染到纹理(视口纹理,参见3d in 2d2d in 3d 演示)。 3. 我们现在有了着色器。
  • @Theraot 我的主要目标是学习如何编写这种脚本。我正在使用 Godot,但希望能够在很多地方使用我的知识,而不仅仅是 Godot。此外,为了让我的游戏获得真实的感觉,我认为最好的方法是复制原版。
  • 如果复制原件并不意味着使用实际的硬件或仿真器,它必须意味着我提到的事情之一。顺便说一句,gamedev.stackexchange.com 上欢迎“如何做这个或那个效果”形式的问题。但是,您提到了一个脚本,您是否要复制特定的代码?
  • 我认为着色器可能更容易实现。我会努力让它发挥作用。
  • @Theraot 我指定“模式 7”的原因是因为 SNES 模式 7 是让您以特定方式操纵背景的模式。用于 3d 的一个著名例子是最初的超级马里奥赛车。这种效果可以创造出本质上是让您的角色在其上四处走动的地板。

标签: 3d 2d godot gdscript


【解决方案1】:

好的,我想通了。我将解释两种设置。

在开始之前,让我解释一下我们将使用的着色器代码:

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 变量中的结果。我们将使用它来采样使用此着色器的任何节点的纹理……但是我们需要使用uvz 来做透视效果。为此,我们将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.00.01.0

然后sign(uv.z) + 1.0 将是0.01.02.0

最后sign(sign(uv.z) + 1.0) 将是0.01.0(如果您愿意,可以使用clamp(sign(uv.z), 0.0, 1.0))。因此,COLOR.a *= sign(sign(uv.z) + 1.0) 将 alpha 乘以 0.0uv.z 为负数。


注意:我之所以在片段着色器中操作 UV 坐标而不是在顶点着色器中进行操作,是因为 Godot 正在为 2D 进行仿射纹理映射。这会导致失真。这是一种解决方法。


第一个设置只是一个精灵。使用您想要的任何纹理设置Sprite,并将材质设置为新的着色器材质,并在着色器中使用我在开始时显示的代码。

Godot 将为您提供编辑材料资源中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、antialias 过滤器和重复纹理超出其边缘(镜像和非镜像)。


第二种更复杂的设置是针对多个对象。这也是适用于TileMap 的设置。你将需要这个树形结构:

- Sprite2D
  +- Viewport
     +- Camera2D
     +- target

Sprite2D 定位在我们希望看到的位置,给它一个着色器材质,使用我在开始时显示的着色器代码。 顺便说一句,如果您在 UI 中需要它,这也应该与 TextureRect 一起使用。

不要为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 BgV FlipCamera2D 可以保留其默认值。

最后,“目标”就是你想要展示的任何东西。它可以是一个或多个二维节点。我建议把它做成另一个场景,这样就可以很容易地独立于这个设置来编辑它(Viewport 的任何子元素都不会在编辑器中可见)。

示例结果:

是的,我们可以在 Godot 中使用实际 3D 存档同样的效果,没问题。但我们没有。我们选择用 2D 工具来实现这个效果并做其他事情,不是因为它们容易,而是因为它们很难。


此答案中使用的纹理是公共领域 (CC0),来自 Kenney

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多