I'm trying to do screen space deferred decals following the presentation made by Pope Kim about SSDs in WH40K: Space Marine (link). I've gotten to the point where I can render a decal if the bounding box is placed at the world space origin (0, 0, 0). The moment I move the bounding volume the decal is still trying to render at the world space origin and can be seen if you look "through" the bounding volume. The red planes in the picture are the bounding volume rendered for reference, the circular patch is the decal.

Picture showing a decal rendered correctly at world space origin

enter image description here Picture showing a decal clipped when rendered offset from world space origin

enter image description here Decal as seen "through" offset bounding volume.

My code is fairly similar to that off Kim, with minor differences accounting for the D3D to OpenGL transition.

Vertex shader:

#version 430

layout (shared) uniform PerFrameBlock
    mat4 gView;
    mat4 gProjection;

uniform mat4 modelMatrix;
uniform vec3 decalSize;

layout ( location = 0 ) in vec4 positionIN;
layout ( location = 1 ) in vec4 normalIN;
layout ( location = 2 ) in vec4 tangentIN;
layout ( location = 3 ) in ivec4 boneIndices;
layout ( location = 4 ) in vec4 boneWeights;
layout ( location = 5 ) in vec2 uvIN;

out vec4 posFS;
out vec4 posW;
out vec2 uvFS;

void main()
    posW = modelMatrix * vec4(positionIN.xyz * 1, positionIN.w);
    //Move position to clip space
    posFS = gProjection * gView * posW;
    uvFS = uvIN;
    gl_Position = posFS;

Fragment shader:

#version 430
#extension GL_ARB_texture_rectangle : enable

in vec4 posFS;
in vec4 posW;
in vec2 uvFS;

uniform sampler2D gNormalDepth;
uniform sampler2D gDiffuse;

uniform float gGamma;

uniform mat4 invProjView;
uniform mat4 invModelMatrix;

vec4 reconstruct_pos(float z, vec2 uv_f)
    vec4 sPos = vec4(uv_f * 2.0 - 1.0, z, 1.0);
    sPos = invProjView * sPos;
    return vec4((sPos.xyz / sPos.w ), sPos.w);

layout ( location = 1 ) out vec4 diffuseRT;
layout ( location = 2 ) out vec4 specularRT;
layout ( location = 3 ) out vec4 glowMatIDRT;

void main()
    vec2 screenPosition = posFS.xy / posFS.w;

    vec2 depthUV = screenPosition * 0.5f + 0.5f;
    depthUV += vec2(0.5f / 1280.0f, 0.5f / 720.0f); //half pixel offset
    float depth = texture2D(gNormalDepth, depthUV).w;

    vec4 worldPos = reconstruct_pos(depth, depthUV);
    vec4 localPos = invModelMatrix * worldPos;

    float dist = 0.5f - abs(localPos.y);
    float dist2 = 0.5f - abs(localPos.x);

    if (dist > 0.0f && dist2 > 0)
        vec2 uv = vec2(localPos.x, localPos.y) + 0.5f;
        vec4 diffuseColor = texture2D(gDiffuse, uv);
        diffuseRT = diffuseColor;
        diffuseRT = vec4(1.0f, 0, 0, 1);

I think my problem stems from the conversion from world space to decal space using the inverse model matrix. The model matrix is build by the following code:

        glm::quat rot;
glm::mat4 rotationMatrix;
glm::mat4 translationMatrix;
glm::mat4 scaleMatrix;

        rotationMatrix = glm::toMat4(rotation);
        translationMatrix = glm::translate(glm::mat4(1.0f), position);
        scaleMatrix = glm::scale(scaleX, scaleY, scaleZ);

       modelMatrix = translationMatrix * rotationMatrix * scaleMatrix;

To get the inverse I just use the following:


What I'm wondering is if inverting the model matrix like this actually correct or am I doing something with it which would cause this kind of behaviour? I've tried offsetting the calculated local space position in the shader with no luck (probably because the distance gets borked) and I'm starting to run out of ideas.

Any help is much appreciated!


1 Answer 1


It looks like the translation component of your inverse model matrix is being ignored. Can you confirm this by trying it with a rotated/scaled model?

(If nothing changes at all, it could be a sign that the inverse model matrix isn't being passed to the shader correctly, and so it's always using an identity matrix with no translation)

If it is indeed selective to translation, and scale/rotation come through as expected, then suspect #1 is your w component. Try manually setting worldPos.w = 1.0f; before multiplying by the inverse model matrix, to ensure the translation isn't being zeroed-out.

I implemented one of these myself recently in Unity, and the rest of your code looks correct to my eye, but I'm not super fluent in native OpenGL.

  • 1
    \$\begingroup\$ You were right, setting worldPos.w = 1.0f solved the issue, thanks a lot for the help! \$\endgroup\$
    – Bentebent
    Feb 15, 2014 at 10:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .