godotengine / godot

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

Material's billboard property doesn't work as expected #19249

Closed mysticfall closed 2 years ago

mysticfall commented 6 years ago

Godot version: master / 2ff31bada

OS/device including version: Manjaro Linux 17.1

Issue description: I'm not entirely certain if I understood the concept of billboard properly, but if I'm not mistaken it's supposed to be used to make something always face the camera.

If you set the billboard property to enabled, it'd make a plane mesh's thin side to face the camera so it can't be used to make an UI panel or item label to display properly.

On the other hand, the y-billboard option makes such a plane like meshes to show its broad side to the camera, but this time the texture is flipped horizontally (tested with a viewport texture) and I couldn't find a way to correct its orientation.

And even without the flipping problem, the mesh doesn't align property with respect to the current camera. Instead of facing the camera it tries to match its rotation, and sometimes the surface entirely disappears if you turn the camera to a certain angle.

Steps to reproduce: Try to make the UI panel in gui_in_3d scene from the official demo to always face the camera.

Zireael07 commented 6 years ago

This is only the case with plane meshes, quad meshes work as expected.

spongeboburu commented 6 years ago

I can confirm this bug for both 3.0.5 and master, although they produce different results.

In 3.0.5 using billboard on a plane will result in a plane that is slightly skewed when rotating the camera around the plane, this looks like it's just a bug in the shader.

In master (built just now) the plane isn't rendered at all, but I might have a clue as to why.

I'm speculating that the cube works because the shader is using positive Z as a normal, a cube does have a face with Z normal whereas the plane is facing positive Y (up). Using a prism you get the "front" of the prism (the triangular face with positive Z normal). A plane does not have any Z (or X) faces.

Adding an option to set the normal for the plane mesh would be a nice feature to have and might solve this problem. And/or add it as an option for SpatialMaterial and/or a render_mode option for ShaderMaterial.

Of course, all speculation on my part

spongeboburu commented 6 years ago

Can you try this shader for a shader material on a cube and plane? (note: all the different axis matrices are for flipping what face is rendered towards the camera, you can remove the flip stuff entirely to get the pos Z, which is default)

EDIT: after experimenting this isn't optimal. Gotta find a better way.

shader_type spatial;
render_mode unshaded;

uniform sampler2D image;
uniform bool keep_scale = true;
varying vec4 test;

void vertex()
{
    mat4 axis_x_pos = mat4(vec4(0, 0, 1, 0), vec4(0, 1, 0, 0), vec4(-1, 0, 0, 0), vec4(0, 0, 0, 1));
    mat4 axis_x_neg = mat4(vec4(0, 0, -1, 0), vec4(0, 1, 0, 0), vec4(1, 0, 0, 0), vec4(0, 0, 0, 1));
    mat4 axis_y_pos = mat4(vec4(1, 0, 0, 0), vec4(0, 0, 1, 0), vec4(0, -1, 0, 0), vec4(0, 0, 0, 1));
    axis_y_pos *= mat4(vec4(-1, 0, 0, 0), vec4(0, 1, 0, 0), vec4(0, 0, -1, 0), vec4(0, 0, 0, 1));
    mat4 axis_y_neg = mat4(vec4(1, 0, 0, 0), vec4(0, 0, -1, 0), vec4(0, 1, 0, 0), vec4(0, 0, 0, 1));
    mat4 axis_z_pos = mat4(vec4(1, 0, 0, 0), vec4(0, 1, 0, 0), vec4(0, 0, 1, 0), vec4(0, 0, 0, 1));
    mat4 axis_z_neg = mat4(vec4(1, 0, 0, 0), vec4(0, -1, 0, 0), vec4(0, 0, -1, 0), vec4(0, 0, 0, 1));
    axis_z_neg *= mat4(vec4(-1, 0, 0, 0), vec4(0, -1, 0, 0), vec4(0, 0, 1, 0), vec4(0, 0, 0, 1));
    mat4 flip = axis_y_pos; // CHANGE ME TO CHANGE WHAT FACE IS SHOWING
    MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0], CAMERA_MATRIX[1], CAMERA_MATRIX[2], WORLD_MATRIX[3]);
    if (keep_scale)
        MODELVIEW_MATRIX *= mat4(vec4(length(WORLD_MATRIX[0].xyz),0,0,0),vec4(0,length(WORLD_MATRIX[1].xyz),0,0),vec4(0,0,length(WORLD_MATRIX[2].xyz),0),vec4(0,0,0,1));
    MODELVIEW_MATRIX[2][2] = 0.0;   
    MODELVIEW_MATRIX *= flip;
}

void fragment()
{
    ALBEDO = texture(image, UV).rgb;
}
spongeboburu commented 6 years ago

Made a small experiment on the upstream master, added translation and rotation for primitive meshes. Setting X rotation to 90 will face the plane to Z+ and that seems to work.

https://github.com/spongeboburu/godot/commit/fd474e232f8c6a500ed4439463987162c77fabf0

Try it out. If this is considered useful by enough users maybe I'll ask the devs if a pull request is in order or if they have a cleaner way of fixing it.

KoBeWi commented 4 years ago

Seems to still happen in 3.2.2-rc1 although not sure why not just use quads instead of planes if they work correctly... I mean, they serve different purpose, so just pick the one that's fit better.

Calinou commented 2 years ago

Closing in favor of https://github.com/godotengine/godot-proposals/issues/4516, which would resolve the original issue here (it can't be done without breaking compatibility).

The QuadMesh vs PlaneMesh differences are now documented in the class reference.