Accessing a bitmap created by a shader
Asked Answered
S

10

0

Well, this is kind of complex, so I will try to explain it the best way I can.
I want to create a bitmap that could then be used inside other scripts. Since the bitmap is a bit big (2048px x 1535px), the fastest way to create it is through a shader.
I already created a TextureRect , set its Texture to ExternalTexture, created a ShaderMaterial for it, and created a new shader, that I coded to create some sort of image.
Now, I would like to be able to access the generated bitmap from within a GDScript.
By "access", I mean, be able to access the pixel values, and even be able to generate a new bitmap, by changing the Uniform variables that are defined in the Shader.
Is this possible?
In a nutshell:
1) create a bitmap using a Shader (since it is fast) that can be accessed from a GDScript?
2) from within a GCScript, force the Shader to recreate a new bitmap with new parameters?

Sycosis answered 20/6, 2022 at 11:57 Comment(0)
F
0

I think you will need to have the TextureRect as a child node of a Viewport with the same size as the bitmap. Then you can get a ViewportTexture and access the pixels from there.

You might be able to get the pixels from the TextureRect texture directly, but I am not sure. I know the Viewport solution should work.

To get the pixels themselves, you’ll need to lock the image and then call the get_pixel function: https://godotengine.org/qa/77048/getting-texture-pixel-data

Edit: for telling the script to make a new texture, you are going to want to define some shader uniforms that are the parameters for the shader. Then you can set those uniforms via GDScript: https://godotengine.org/qa/57814/how-to-modify-a-shaders-parameters-in-runtime

Flotation answered 20/6, 2022 at 12:42 Comment(0)
C
0

Yes, the Viewport Texture would be the way to go. It's based on pointers, so you don't have to "get" the data. It should be in some sort of shared memory, so once it is connected, they are the same object.

Conny answered 20/6, 2022 at 16:26 Comment(0)
S
0

Thank you for the quick answer.
I will try it out as soon as possible. My wife got sick today and I was busy all day and I will get back to my job tomorrow. But I will try your solution as soon as possible.

Sycosis answered 20/6, 2022 at 20:47 Comment(0)
S
0

Ok, got a little time before going to bed.
I tried:

var vpimg = get_node("Viewport").get_texture().get_data()

but got the error:

(Node not found: "Viewport" (relative to "/root/EditorNode/@@592/@@593/@@601/@@603/@@607/@@611/@@612/@@613/@@629/@@630/@@639/@@640/@@6275/@@6109/@@6110/@@6111/@@6112/@@6113/Spatial/MeshInstance_GDScript").)
res://Global_Scripts/Globe2.gd:53 - Attempt to call function 'get_texture' in base 'null instance' on a null instance.

The Viewport node exists and it is called "Viewport". I even tried changing its name to something else (adjusting the get_node parameter, of course), but got the same error.

Sycosis answered 20/6, 2022 at 21:23 Comment(0)
C
0

Is this in a plug-in or tool script? You have limited access to the rest of the game in editor scripts.

Conny answered 20/6, 2022 at 21:52 Comment(0)
S
0

Made it work by making the Viewport a child of the object that has the GDScript.
But now I can't seem to force the shader to update.
I tried to change one of its parameters, named "s_seed" with:

var mat = get_node("Viewport/TextureRect")
mat.material.set_shader_param("s_seed",sh_seed)

but nothing happens.

Sycosis answered 20/6, 2022 at 21:52 Comment(0)
C
0

The function get_node() is relative. So if you call:

get_node("Object")

That implies that the node with name "Object" is a direct child of the node which the script is attached.

You can also go one layer up (a sibling of the parent) with:

get_node("../Object")

Or use absolute paths:

get_node("/root/Game/Object")

When the node "Game" is your root object and "Object" is a direct child of "Game"

Conny answered 20/6, 2022 at 21:57 Comment(0)
C
0

In your last code, I think mat is a copy of the material, not the original reference. You can do this:

get_node("Viewport/TextureRect").material.set_shader_param("s_seed", sh_seed)
Conny answered 20/6, 2022 at 22:1 Comment(0)
S
0

Thank you so much for the fast replay.
Indeed, I can change the shader parameters now.
But, the Viewport is not updated as I change the shader parameters in code.
I notice that, if I change those parameters, the image created by the shaders does update only when I click the Viewport.
Is there any way to force a recalculation of the Viewport image, in code?

Sycosis answered 20/6, 2022 at 22:13 Comment(0)
S
0

After much fiddling, turning the "Update Mode" to Always did the trick :-)

Sycosis answered 20/6, 2022 at 22:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.