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

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
devsh
Competition winner
Posts: 2057
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK
Contact:

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

Post 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.
Last edited by devsh on Wed May 23, 2018 5:10 pm, edited 1 time in total.
CuteAlien
Admin
Posts: 9628
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

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

Post 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.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
devsh
Competition winner
Posts: 2057
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK
Contact:

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

Post 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).
Last edited by devsh on Wed May 23, 2018 5:31 pm, edited 1 time in total.
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

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

Post 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?
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
devsh
Competition winner
Posts: 2057
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK
Contact:

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

Post 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
devsh
Competition winner
Posts: 2057
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK
Contact:

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

Post 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);
 
devsh
Competition winner
Posts: 2057
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK
Contact:

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

Post 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.
mant
Posts: 125
Joined: Sun Jan 27, 2013 3:38 pm

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

Post by mant »

Thanks, this technique is very handy.
My cube and plane are using it for bump mapping.

Image
Post Reply