2


I have been working on projecting decals on to anything that the decals bounding box encapsulates. After reading and trying numerous code snippets (usually in HLSL) I have a some what working method in GLSL for projecting the decals.

Let me start with trying to explain what I'm doing and how this works (so far).
The code below is now fixed and works!

This all is while in the perspective view mode.
I send 2 uniforms to the fragment shader "tr" and "bl". These are the 2 corners of the bounding box. I can and will replace these with hard coded sizes because they are the size of the decals original bounding box. tr = vec3(.5, .5, .5) and br = vec3(-.5, -.5, -.5). I'd prefer to find a way to do the position tests in the decals transformed state. (more about this at the end).

Adding this for clarity. The vertex emitted from the vertex program is the bounding box multiplied by the decals matrix and than by the model view projection matrix.. I use this for the next step:
With that vertex, I get the depth value from the depth texture and with it, calculate the position in world space using the inverse of the projection matrix.

Next, I translate this position using the Inverse of the Decals matrix. (The matrix that scales, rotates and translates the 1,1,1 cube to its world location. I thought that by using the inverse of the decals transform matrix, the correct size and rotation of the screen point would be handled correctly but it is not.

Vertex Program:

//Decals color pass.

#version 330 compatibility 

out mat4 matPrjInv;

out vec4 positionSS;
out vec4 positionWS;
out mat4 invd_mat;

uniform mat4 decal_matrix;

void main(void)
{

    gl_Position = decal_matrix * gl_Vertex;
    gl_Position = gl_ModelViewProjectionMatrix * gl_Position;

    positionWS =  (decal_matrix * gl_Vertex);;
    positionSS = gl_Position;

    matPrjInv = inverse(gl_ModelViewProjectionMatrix);

    invd_mat = inverse(decal_matrix);

}

Fragment Program:

#version 330 compatibility

layout (location = 0) out vec4 gPosition;
layout (location = 1) out vec4 gNormal;
layout (location = 2) out vec4 gColor;


uniform sampler2D depthMap;
uniform sampler2D colorMap;
uniform sampler2D normalMap;
uniform mat4 matrix;

uniform vec3 tr;
uniform vec3 bl;
in vec2 TexCoords;
in vec4 positionSS; // screen space
in vec4 positionWS; // world space

in mat4 invd_mat; // inverse decal matrix

in mat4 matPrjInv; // inverse projection matrix

void clip(vec3 v){
   if (v.x > tr.x || v.x < bl.x ) { discard; }
   if (v.y > tr.y || v.y < bl.y ) { discard; }
   if (v.z > tr.z || v.z < bl.z ) { discard; }
}


vec2 postProjToScreen(vec4 position)
{
    vec2 screenPos = position.xy / position.w;
    return 0.5 * (vec2(screenPos.x, screenPos.y) + 1);
}
void main(){

    // Calculate UVs
    vec2 UV = postProjToScreen(positionSS);

    // sample the Depth from the Depthsampler
    float Depth = texture2D(depthMap, UV).x * 2.0 - 1.0;

    // Calculate Worldposition by recreating it out of the coordinates and depth-sample
    vec4 ScreenPosition;
    ScreenPosition.xy = UV * 2.0 - 1.0;
    ScreenPosition.z = (Depth);
    ScreenPosition.w = 1.0f;

    // Transform position from screen space to world space
    vec4 WorldPosition = matPrjInv * ScreenPosition ;
    WorldPosition.xyz /= WorldPosition.w;
    WorldPosition.w = 1.0f;

    // transform to decal original position and size.
    // 1 x 1 x 1
    WorldPosition = invd_mat * WorldPosition;
    clip (WorldPosition.xyz);

    // Get UV for textures;
    WorldPosition.xy += 0.5;
    WorldPosition.y *= -1.0;

    vec4 bump = texture2D(normalMap, WorldPosition.xy);
    gColor = texture2D(colorMap, WorldPosition.xy);

    //Going to have to do decals in 2 passes..
    //Blend doesn't work with GBUFFER.
    //Lots more to sort out.
    gNormal.xyz = bump;
    gPosition = positionWS;

    }

And here are a couple of Images showing whats wrong. What I get for the projection: enter image description here

And this is the actual size of the decals.. Much larger than what my shader is creating! enter image description here

I have tried creating a new matrix using the decals and the projection matrix to construct a sort of "lookat" matrix and translate the screen position in to the decals post transformed state.. I have not been able to get this working. Some where I am missing something but where? I thought that translating using the inverse of the decals matrix would deal with the transform and put the screen position in the proper transformed state. Ideas?

Updated the code for the texture UVs.. You may have to fiddle with the y and x depending on if your texture is flipped on x or y. I also fixed the clip sub so it works correctly. As it is, this code now works. I will update this more if needed so others don't have to go through the pain I did to get it working. Some issues to resolve are decals laying over each other. The one on top over writes the one below. I think I will have to accumulated the colors and normals in to the default FBO and then blend(Add) them to the GBUFFER textures before or during the lighting pass. Adding more screen size textures is not a great idea so I will need to be creative and recycle any textures I can.

I found the solution to decals overlaying each other. Turn OFF depth masking while drawing the decals and turn int back on afterwards:

glDepthMask(GL_FALSE)
Mike O
  • 149
  • 10
  • Well, conceptually, your approach could work. It is not really clear how your decal matrix is constructed, and how your "decal space" is defined. What I don't get is the `clip` function in combination for the values you give in the text. It translates to `if (v.x < 0.5 || v.x > -0.5)` which is always true. You probably confused the values in the text... – derhass Apr 17 '17 at 17:14
  • Confuse is mostly what I am lately :) I probably have the values of tr and bl switched.. id have to look at the code that passes them. The decal matrix is just the standard 4x4 transform matrix from model to world space. It actually does clip the decal and paint in the correct place, its just that it does not project to the correct size.. its basically retaining the 1x1 size from the clip. – Mike O Apr 17 '17 at 18:11
  • Ok.. its not retaining the 1x1 scale.. I was wrong. It is scaling but not as I expected. In the 2nd image I posted, that is the bounding bounding box (1 x 1 x 1) multiplied by their associated matrices. If I could somehow scale the bounding box corners for the clip test with out inducing any rotation, that might fix it. I'll see if I can find a way to do that and get back here ASAP with info on my results. – Mike O Apr 17 '17 at 18:34
  • How is your bounding box (the ones you used to create the second image) actually defined? `[-0.5, 0.5]` in object space? And what kind of matrix are you using to place the decals in the world? – derhass Apr 17 '17 at 18:42
  • For the bounding box I create a cube with 6 quads.. Mostly this is a visualization aid as I don't really need it to preform the clipping. I reverse translate the point I get from the screen back to the bounding boxes original size using the inverse matrix for each decal. Each decal has its own associated matrix. Its no different than any model to world matrix. I tried scaling the 2 corners of the bounding box size and so far,, It's not working. I might have the matrix row/column mixed up? – Mike O Apr 17 '17 at 19:11
  • I update the question. – Mike O Apr 17 '17 at 19:13
  • Actually I do need the vertices from the bounding box so I have something to draw and use to get the point on the screen. I'm tired and burnt out on this. I have a bout a week on this and its all running together at this point.. sorry – Mike O Apr 17 '17 at 19:24

1 Answers1

2

OK.. I'm so excited. I found the issue. I updated the code above again. I had a mistake in what I was sending the shader for tr and bl: Here is the change to clip:

void clip(vec3 v){
   if (v.x > tr.x || v.x < bl.x ) { discard; }
   if (v.y > tr.y || v.y < bl.y ) { discard; }
   if (v.z > tr.z || v.z < bl.z ) { discard; }
}
Mike O
  • 149
  • 10