着色器驻留在 GPU 中,它们的输出显示在屏幕上。为了保存图像,CPU 必须查看 GPU 输出,但这通常不会发生。而且由于不经过CPU,所以性能很好。通常。好吧,至少比 CPU 一直这样做要好。
另外,您确定不想通过其他方式获得像素艺术外观吗?例如从纹理中删除过滤器,更改拉伸模式并在小分辨率下工作,也许还可以启用像素捕捉?不?手表如何在 Godot 中为像素艺术游戏制作丝般光滑的相机 https://www.youtube.com/watch?v=zxVQsi9wnw8。仍然没有?好的...
不管怎样,为了你想要的,你需要一个Viewport
.
视口设置
你需要的是创建一个Viewport
. 不要忘记设置它size
。还可能想设置render_target_v_flip
to true
,这会垂直翻转图像。如果你发现输出图像是颠倒的这是因为你需要切换render_target_v_flip
.
然后放置为Viewport
你想要渲染什么。
渲染
接下来,您可以从Viewport
,将其转换为图像,并将其保存为 png。我正在使用附加到的工具脚本执行此操作Viewport
,所以我将有一个解决方法来从检查器面板触发代码。我的代码如下所示:
tool
extends Viewport
export var save:bool setget do_save
func do_save(new_value) -> void:
var image := get_texture().get_data()
var error := image.save_png("res://output.png")
if error != OK:
push_error("failed to save output image.")
当然,你可以export
a FILE
path String
以便在检查器面板中轻松更改它。我在这里给出常见的边缘情况:
tool
extends Viewport
export(String, FILE) var path:String
export var save:bool setget do_save
func do_save(_new_value) -> void:
var target_path := path.strip_edges()
var folder := target_path.get_base_dir()
var file_name := target_path.get_file()
var extension := target_path.get_extension()
if file_name == "":
push_error("empty file name.")
return
if not (Directory.new()).dir_exists(folder):
push_error("output folder does not exist.")
return
if extension != "png":
target_path += "png" if target_path.ends_with(".") else ".png"
var image := get_texture().get_data()
var error := image.save_png(target_path)
if error != OK:
push_error("failed to save output image.")
return
print("image saved to ", target_path)
另一种选择是使用ResourceSaver
:
tool
extends Viewport
export var save:bool setget do_save
func do_save(new_value) -> void:
var image := get_texture().get_data()
var error := ResourceSaver.save("res://image.res", image)
if error != OK:
push_error("failed to save output image.")
这仅适用于 Godot 编辑器,并且仅适用于 Godot,因为您获得了 Godot 资源文件。尽管我发现使用 Godot 生成图像的想法很有趣。我建议去ResourceSaver
如果你想为 Godot 自动生成它们。
关于从工具脚本保存资源
在上面的示例中,我假设您正在保存到资源路径。这是因为目的是将输出图像用作Godot 中的资源。使用资源路径有几个含义:
- 这可能不适用于导出的游戏(因为目标是改进工作流程,所以这是可以的)。
- Godot 需要重新导入资源,但不会注意到它发生了变化。
我们可以处理第二点EditorPlugin
,如果这就是你正在做的事情,你可以这样做来告诉 Godot 扫描更改:
get_editor_interface().get_resource_filesystem().scan()
如果你不是,你可以通过创建一个空的来作弊EditorPlugin
。我们的想法是这样做:
var ep = EditorPlugin.new()
ep.get_editor_interface().get_resource_filesystem().scan()
ep.free()
顺便说一下,你会想要缓存EditorPlugin
而不是每次都制作一个新的。或者更好的是,缓存EditorFileSystem
你从get_resource_filesystem
.
自动化
现在,我意识到必须将东西放在里面可能很麻烦Viewport
。如果您不需要一直这样做,那么这对于您的工作流程来说可能没问题。
但是自动化呢?好吧,无论采用哪种方法,您都需要一个tool
隐藏的脚本Viewport
,需要一个Node
,检查它是否有着色器,如果有,则将其暂时移动到Viewport
,获取渲染纹理(get_texture()
)将其设置为纹理Node
,删除着色器,并返回Node
到其在场景中的原始位置。或者不是在中寻找着色器Node,始终将着色器应用于任何内容Node
,也许作为资源加载而不是硬编码。
Note:我相信你需要在添加之间让一个空闲帧通过Node
to the Viewport
并获取纹理,以便纹理更新。或者是两个空闲帧?好吧,如果其中一个不起作用,请尝试添加另一个。
关于制作一个EditorPlugin
如您所知,您可以从项目设置创建插件。这将创建一个EditorPlugin
给你的脚本。在那里,您可以向工具菜单添加一个选项(使用add_tool_menu_item
),或者将其添加到编辑器的工具栏(使用add_control_to_container
)。并让它作用于编辑场景中的当前选择(您可以使用get_selection
,或覆盖edit
and handles
方法)。您可能还想为此创建一个撤消条目,请参阅get_undo_redo
.
或者,您也可以让它跟踪(或查找)Node
它必须采取行动,然后致力于build
虚拟方法,当项目即将运行时运行。我没有与build
虚拟方法,所以我不知道它是否有任何需要注意的怪癖。