Page 1 of 1

Don't have Tangent Space or Normals? calculate in shader!

Posted: Wed May 23, 2018 4:40 pm
by devsh
You can build your own with

Code: Select all

 
in vec3 vertexPos; // model/object, world or even view space, doesn't matter
 
.. somewhere in your shader
 
    vec3 tangent = dFdx(vertexPos);
    vec3 bitangent = dFdy(vertexPos);
    vec3 n = normalize( cross( tangent , bitangent ) );
 

NOTE 1: This only works for flat-shaded surfaces (no curved triangles).
NOTE 2: This doesn't align your tangent and bitangent with the U and V axis of your texture space.

Re: Don't have Tangent Space or Normals? calculate in shader

Posted: Wed May 23, 2018 5:01 pm
by CuteAlien
edit: Removed as original post got edited (typo) and I have to read docs about correct function now first anyway ;-)
edit2: And thanks @devsh for clarification! Will check this out some time - looks interesting.

Re: Don't have Tangent Space or Normals? calculate in shader

Posted: Wed May 23, 2018 5:12 pm
by devsh
I am sure, dFdx is a derivative of F (anything) along screenspace X and Y directions.
Has absolutely no relation to texture space unless F is your uv coordinate.

The normal calculated is 100% correct for meshes without smoothgroups (tested).

Re: Don't have Tangent Space or Normals? calculate in shader

Posted: Wed May 23, 2018 11:34 pm
by Mel
May come in handy for projects which use strictly vertex positions, and nothing more... or uses the rest of the vertex space for other purposes. Still... how eficient is that function? wouldn't it need to calculate the values of the provided parameter on the surrounding fragments in order to provide the correct value?

Re: Don't have Tangent Space or Normals? calculate in shader

Posted: Thu May 24, 2018 8:25 am
by devsh
Well, if you read up on compute shaders and how fragment shaders actuall work, you will find out that they run in a SIMD lane in lockstep 8,16 or 32 pixels at once in nice little 2d slabs, with extra work being done on triangle edges (dead pixel invocations).

You pixel shader needs to be able to do dFdx,dFdy and fwidth in hardware for purposes of mipmap texturing (textureLod(uv,fwidth(uv))) and anisotropic filtering (textureGrad(uv,dFdx(uv),dFdy(uv))).

The GPU instruction set always has an instruction for swizzling data between SIMD lanes so differences between pixels are easy to obtain, you only pay for the "-" operation.


I also cooked up a version (not tested yet) that will calculate the correct tangent and bi-tangent aligned with your UV space for normal mapping.

Code: Select all

 
in vec3 vertexPos; // model/object, world or even view space, doesn't matter
in vec2 texCoord; // the texcoord you're using for normal mapping, can be uniformly scaled and will produce same results (no need to calculate separately for detail maps)
 
.. somewhere in your shader
 
    vec2 u1 = dFdx(texCoord);
    vec2 u2 = dFdy(texCoord);
    //! Should optimize this line by seeing what SPIR-V compiler outputs for this
    //mat3x2 tanSpace = mat3x2(dFdx(vertexPos),dFdy(vertexPos))*inverse(mat2(u1,u2)));
    //no need for divide by determinant if vectors get normalized
    mat3x2 tanSpace = mat3x2(dFdx(vertexPos),dFdy(vertexPos))*mat2(u2.y,-u1.y,-u2.x,u1.x);
 
    vec3 tangent = normalize(tanSpace[0]);
    vec3 bitangent = normalize(tanSpace[1]);
    vec3 normal = cross( tangent , bitangent);
#ifndef ORTHOGONAL_UV_SPACE
    normal = normalize(normal);
#endif
 
If your uv space is orthogonal, i.e. the texture is not sheared or non-uniformly stretched over ANY of your triangles, then you can #define ORTHOGONAL_UV_SPACE

Re: Don't have Tangent Space or Normals? calculate in shader

Posted: Thu May 24, 2018 9:28 am
by devsh
One last thing to cook-up would be a tangent space calculation that can be smooth for smooth interpolated normals.

Maybe something like..
http://www.thetenthplanet.de/archives/1180

Ideally I'd want the output of the pixel shader implicit tangent space calculation to be identical to the output of MikkTspace algorithm, so that there would be no discrepancies between using and not using a tangent space with glTF 2.0 assets.


EDIT: You could lift the calculated tangent and bitangent out of the triangle plane with normalize(t - n * dot(n, t)), to become perpendicular to the smooth normal. So the whole shader would become

Code: Select all

 
in vec3 vertexPos; // model/object, world or even view space, doesn't matter
in vec3 smoothNormal; //interpolated normal across "bent" triangles
in vec2 texCoord; // the texcoord you're using for normal mapping, can be uniformly scaled and will produce same results (no need to calculate separately for detail maps)
 
.. somewhere in your shader
 
    vec2 u1 = dFdx(texCoord);
    vec2 u2 = dFdy(texCoord);
    mat3x2 tanSpace = mat3x2(dFdx(vertexPos),dFdy(vertexPos))*mat2(u2.y,-u1.y,-u2.x,u1.x);
 
    vec3 tangent = normalize(tanSpace[0]-smoothNormal*dot(smoothNormal,tanSpace[0]));
    vec3 normal = normalize(smoothNormal);
#ifndef ORTHOGONAL_UV_SPACE
    vec3 bitangent = normalize(cross(smoothNormal,tangent));
#else
    vec3 bitangent = normalize(tanSpace[1]-smoothNormal*dot(smoothNormal,tanSpace[1]));
#endif
 
FINAL SOLUTION: I shouldn't really care about what you want your bitangent to be from the texture warps, the tangent space needs to be orthonormal and no software will bake a tangent space normal map in any other parametrization. With this code I can't see a different between precomputed tangent space and pixel shader generated one (note I had to flip the direction of the tangent as compared to previous examples due to texture coord orientation in OpenGL GLSL).

Code: Select all

 
in vec3 vertexPos; // model/object, world or even view space, doesn't matter
in vec3 smoothNormal; //interpolated normal across "bent" triangles
in vec2 texCoord; // the texcoord you're using for normal mapping, can be uniformly scaled and will produce same results (no need to calculate separately for detail maps)
 
.. somewhere in your shader
 
    vec3 denormTangent = dFdx(texCoord.y)*dFdy(vPos)-dFdx(vPos)*dFdy(texCoord.y);
    vec3 tangent = normalize(denormTangent-smoothNormal*dot(smoothNormal,denormTangent));
 
    vec3 normal = normalize(smoothNormal);
    vec3 bitangent = cross(normal,tangent);
 

Re: Don't have Tangent Space or Normals? calculate in shader

Posted: Fri May 25, 2018 12:12 pm
by devsh
Wrote a really long wiki entry detailing different ways of normal/bump/derivative mapping
https://github.com/buildaworldnet/Irrli ... htly-wrong

I have yet to do the example and explanation for lighting seams, how pixel shader tangent space implicit calculation works and why per vertex tangent vectors will always be wrong for some meshes.

The general takeway is:
Use derivative mapping!
Throw ` irr::scene::IMeshManipulator::createMeshWithTangents` in the bin and never reimplement or try to fix it.

Re: Don't have Tangent Space or Normals? calculate in shader

Posted: Sun Jun 17, 2018 10:39 pm
by devsh
You can throw in https://www.khronos.org/registry/OpenGL ... ontrol.txt for better looking derivatives

Just use `dFdxFine` etc.!

Re: Don't have Tangent Space or Normals? calculate in shader

Posted: Tue Oct 30, 2018 5:57 pm
by mant
Thanks, this technique is very handy.
My cube and plane are using it for bump mapping.

Image