RobLoach / node-raylib

Node.js bindings for Raylib
https://robloach.github.io/node-raylib/
Other
233 stars 20 forks source link

Drawing mirrored billboards via negative width does not work. #174

Closed Triazic closed 1 year ago

Triazic commented 1 year ago

On my googling I've seen that the approach to drawing flipped billboards / textures is to provide a source rectangle with the width (or height) being negative. This is not working for me?

Works (renders billboard normally):

  const source: Rectangle = { x: 0, y: 0, width: tex.width, height: tex.height };
  DrawBillboardPro(camera, tex, source, position, camera.up, { x: 1, y: 1 }, { x: 0, y: 0 }, 0, RAYWHITE);

Does not work (renders nothing - should render billboard with x flipped):

  const source: Rectangle = { x: 0, y: 0, width: -tex.width, height: tex.height };
  DrawBillboardPro(camera, tex, source, position, camera.up, { x: 1, y: 1 }, { x: 0, y: 0 }, 0, RAYWHITE);

Reference: https://www.reddit.com/r/raylib/comments/nvtyqn/how_do_i_flip_a_texture/

konsumer commented 1 year ago

I think this is more of a general raylib question, but billboards are "always facing camera", so might not work the same. Maybe try DrawTextureRec (as in the reddit answer) with a negative-width Rectangle? For 3D, you may have to billboard it yourself (aim it at camera) and reverse the image yourself.

While the syntax is not identical, here is original 2D idea in a codepen (to play around with it):

download The text on my shirt is reversed in original image, so it appears to be working.

The more 3D-way to do this are render-to-texture or portal-rendering. See this SO answer for some nice links. (The question is for java, but the theory still applies here.)

twuky commented 1 year ago

yeah, my guess is that it's disappearing due to opengl 'backface culling', if you're giving it a plane that's effectively facing away from the screen (opengl determines this by looking at the order of vertices of a triangle) it will assume the face is not visible to the camera and skip drawing it, for performance in 3d scenes.

you could check if you are able to disable culling, or if not, use the Image functions to generate mirrored versions of the textures when you load them, and use those instead of a negative x

Triazic commented 1 year ago

I ending up inventing a vertex shader that accepts the source rect as a uniform and translates stuff accordingly.

#version 330

// Input vertex attributes
in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec3 vertexNormal;
in vec4 vertexColor;

// Input uniform values
uniform mat4 mvp;
uniform sampler2D texture0;
uniform vec4 source;

// Output vertex attributes (to fragment shader)
out vec2 fragTexCoord;
out vec4 fragColor;

void main()
{
    // Send vertex attributes to fragment shader
    fragTexCoord = vertexTexCoord;

    // invert the x co-ordinate of the texture/sprite
    float textureWidth = textureSize(texture0, 0).x;
    float sourceWidth = source.z;
    float left = source.x;
    float right = left + sourceWidth;
    float flippedX = (right - (fragTexCoord.x * textureWidth - left)) / textureWidth;
    fragTexCoord.x = flippedX;

    fragColor = vertexColor;

    // Calculate final vertex position
    gl_Position = mvp*vec4(vertexPosition, 1.0);
}
  const shader = flipped ? assets.flippedShader: assets.defaultShader;
  BeginShaderMode(shader);
  if (flipped) {
    // pass the source rect to the flipped shader.. 
    //@ts-ignore
    r.SetShaderVec4(shader, GetShaderLocation(shader, "source"), { x: source.x, y: source.y, z: source.width, w: source.height });
  }
  DrawBillboardPro(camera, tex, source, position, camera.up, defaultSizeVector, noRotationVector, 0, RAYWHITE);
  EndShaderMode();
Triazic commented 1 year ago

Worth noting that in C# this also does not work so it's probably a raylib thing. Needed to use my shader there as well.

twuky commented 1 year ago

that was my suspicion too - if you were interested in fixing it (or requesting for it to be fixed) on raylib's side - the solution would be to have DrawBillboard check for negative width or height objects, and instead of mirroring the vertex positions, mirror the texture sample coordinates

twuky commented 1 year ago

oh actually - @Triazic , you may try a rectangle like this too, it may do what you are thinking of with flipping the UVs: const source: Rectangle = { x: width, y: 0, width: -tex.width, height: tex.height };

Triazic commented 1 year ago

Nah 99% sure I tried that too, didn't work.