godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.75k stars 21.12k forks source link

Texture sampling don't work on vertex shader 2D #38605

Closed ghsoares closed 4 years ago

ghsoares commented 4 years ago

Godot version: v3.2.stable.official

OS/device including version: Windows 10

Issue description: When I try to texture sample in Vertex function in a shader to manipulate the vertex from a noise, it behaves like the UV was constant. I don't know if this is a excepted behaviour, but I would like to be able to displace the 2D vertex using a OpenSimplexNoise texture, but I can't.

Steps to reproduce: Create a simple shader with a uniform texture and try to sample the noise texture from vertex shader and use the result to manipulate somehow the VERTEX

Minimal reproduction project: VertexIssue.zip

clayjohn commented 4 years ago

Your example project works fine on my device. Windows 10, NVidia GTX 1050

Keep in mind, the vertex shader is only run once for each vertex, so the noise texture is only sampled 4 times and then blended across your sprite.

The following code is probably more in line with what you expected, try running it in your project.

shader_type canvas_item;

uniform sampler2D noise_texture;

varying float noise;

void vertex() {
    noise = texture(noise_texture, UV*0.9 + vec2(TIME * 0.1, 0)).r;
    VERTEX *= noise;
}

void fragment() {
    COLOR.rgb = vec3(noise);
    //That works on fragment function:
    float noise_value = texture(noise_texture, UV).r;
    COLOR.rgb = vec3(noise_value);

}
lawnjelly commented 4 years ago

Also note that reading a texture in a vertex shader isn't supported in all hardware, and it isn't mandated in GLES2 / WebGL.

If you want to look up the caps of a device, the caps is GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS.

ghsoares commented 4 years ago

Your example project works fine on my device. Windows 10, NVidia GTX 1050

Keep in mind, the vertex shader is only run once for each vertex, so the noise texture is only sampled 4 times and then blended across your sprite.

The following code is probably more in line with what you expected, try running it in your project.

shader_type canvas_item;

uniform sampler2D noise_texture;

varying float noise;

void vertex() {
  noise = texture(noise_texture, UV*0.9 + vec2(TIME * 0.1, 0)).r;
  VERTEX *= noise;
}

void fragment() {
  COLOR.rgb = vec3(noise);
  //That works on fragment function:
  float noise_value = texture(noise_texture, UV).r;
  COLOR.rgb = vec3(noise_value);

}

That's a interesting fact to know, so this function is graphic-card dependant, so for a real project, isn't a way to create shaders that every device supports. That's a sad thing to know.

As for the way you showed in the code, shows a bit different result, but it's far from what I want to achieve.

Also note that reading a texture in a vertex shader isn't supported in all hardware, and it isn't mandated in GLES2 / WebGL.

If you want to look up the caps of a device, the caps is GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS.

That's a sad thing to know, so if isn't supported in all devices, that means that I need to manually create a custom noise function inside my shader? That's a thing that a can do, but I would like to use the godot built-in OpenSimplexNoise.

ghsoares commented 4 years ago

Other thing I can do that's more easier is to use UV offset instead of the VERTEX displacement, but again, it would be interesting to do with vertex shader

clayjohn commented 4 years ago

What are you trying to achieve? I have a strong feeling you are misunderstanding what vertex shaders are rather than there being any bug.

The code I posted was just a guess at what you are trying to do. Because the code you have right now is doing exactly what it's supposed to.

ghsoares commented 4 years ago

What are you trying to achieve? I have a strong feeling you are misunderstanding what vertex shaders are rather than there being any bug.

The code I posted was just a guess at what you are trying to do. Because the code you have right now is doing exactly what it's supposed to.

Yes, I may be misunderstanding vertex shaders because this is one of my first experiences, where I have a ghost sprite, and I would like to use a scrolling noise texture to offset the vertexes of the sprite like a ghost.

And yet, even with your code and the initial code that I created, that result isn't achieved maybe because of my Graphics Card (integrated chipset). So, I don't have so much experience in vertex shaders and I was thinking if I could sample the texture in vertex shader, I could give a noisy sprite displacement.

But, I really appreciate yours patience to respond this issue.

clayjohn commented 4 years ago

Maybe your issue is not having enough vertices? A sprite only has 4 vertices, so only for samples will be taken, one at each corner which causes the sprite to stretch and twist. It sounds like you are hoping to displace the sprite continuously? In which case you need to use a subdivided mesh instead of a sprite.

However, I am still unclear on what you are trying to do. "Offsetting the vertices like a ghost" is not a common effect. Please describe what you think that looks like.

ghsoares commented 4 years ago

Maybe your issue is not having enough vertices? A sprite only has 4 vertices, so only for samples will be taken, one at each corner which causes the sprite to stretch and twist. It sounds like you are hoping to displace the sprite continuously? In which case you need to use a subdivided mesh instead of a sprite.

Thank you! Finally discovered my error, I was treating the vertex shader like it was continous from [0, 0] (top-left corner of the texture) to [1, 1] (bottom-right corner of the texture) like the UV, but as you said, the vertex is dependant from the mesh vertices, and as a sprite node only has four, the texture is sampled only in the corners and by consequence, only offsets the corner vertices.

So in teory, to fix that, I just need to create a MeshInstance2D with a plane mesh, subdivide it to be able to have a good amount of vertices, and then create the shader. I created a concept in 3D, where I displace the vertex in x and z axis:

Temp1

However, I am still unclear on what you are trying to do. "Offsetting the vertices like a ghost" is not a common effect. Please describe what you think that looks like.

The ghost-like effect I'm trying to achieve is this "wobbly" motion, I don't know how to explain exactly, but I would like to have something similar to what the youtuber Brackeys made for her game in Ludum Dare 46 (aka "Party Killer") in this timestamp: https://youtu.be/AlCJc05nkwc?t=387 But in 2D.

clayjohn commented 4 years ago

That sounds like it! I'm closing this issue now. Good luck on your project!

In the future, please ask your question in one of the community channels first before opening an issue here. Answering beginner questions takes contributor time away from improving the engine so we like to reserve this space for bug reports only.

ghsoares commented 4 years ago

Thank you @clayjohn and @lawnjelly to help me and sorry for this beginner question.