3
$\begingroup$

I am working on a normal mapping. I need to have the ability to scale normal vectors to adjust the normal strength of the surface. Currently the way I am doing it basically shows plausible results. But I am still not sure that it's 100% correct and to my surprise I found no info on the web regarding this subject. So here is how I do that (my lightning is in camera space):

vertex shader:

vec3 N = normalize(normalMatrix * a_normal); 
vec3 T = normalize(normalMatrix * a_tangent.xyz) * nStrength;          
vec3 B = normalize(normalMatrix * (cross(a_normal, a_tangent.xyz) * a_tangent.w) ) * nStrength; 

v_TBN =  mat3(T,B,N);

normalMatrix - inverse-transpose of model-view matrix.

The shading itself happens in the fragment shader. The only relevant part which is happening there is transforming normal from the texture with v_TBN varying:

Fragment shader:

 vec3 GetNormal(){
    vec3 n = texture(normalMap,uv).xyz;
    n = normalize(n * 2.0 - 1.0);
    return  normalize (v_TBN * n);
 }

So, as you can see, I scale tangent and bi-tangent vectors with nStrength scalar.That's the most optimal setup I figured out that gives most correct looking effect.Is this the right way to strengthen the normals?

$\endgroup$
4
  • 2
    $\begingroup$ I don't think there's a "right" way to "strengthen" normals. It's not clear what your intent is behind it. What kind of visual effect are you trying to accomplish here? $\endgroup$ Commented Jul 26, 2017 at 14:24
  • $\begingroup$ The desired 'effect' is a stronger bump.What?There are no standard techniques in CG to do that? $\endgroup$
    – Michael IV
    Commented Jul 26, 2017 at 14:26
  • 1
    $\begingroup$ What do you mean by a "stronger" bump? Again, what do you want it to look like? Be specific. Generally speaking, bump mapping only effectively works for short bumps (relative to the viewing distance). For taller bumps, you need geometry to reproduce it convincingly. Or at the very least, parallax normal mapping. $\endgroup$ Commented Jul 26, 2017 at 14:31
  • $\begingroup$ @NicolBolas I need an ability to adjust the strength similar to how it is done in any typical game engine.If you want an example, open Unity,drop a normal map on a surface and tweak its strength in the inspector.That's the effect. My code currently seems to produce similar effect. But,I wanted to know if there is a standard technique to do that because mine is based on trials&errors. $\endgroup$
    – Michael IV
    Commented Jul 26, 2017 at 14:35

1 Answer 1

7
$\begingroup$

Assuming that the normal was created by differentiating some height field f(u,v):

N = normalize(df_du, df_dv, 1)

What you probably want to achieve is to "strengthen" your normals as-if by scaling that height field by a factor C:

g(u,v) = C*f(u,v)

Then your new normal would be:

M = normalize(dg_du, dg_dv, 1) = normalize(C*N0, C*N1, N2)

Translating that to GLSL in your shader:

vec3 GetNormal(){
    vec3 n = texture(normalMap,uv).xyz;
    n = n * 2.0 - 1.0;
    n.xy *= C;
    n = normalize(n);
    return  normalize (v_TBN * n);
}

By constructing a normal map texture that stores only the height-field gradient (df_du, df_dv) without normalization, using the internal format GL_RG8_SNORM for the texture, and making sure that v_TBN is orthonormal, you can simplify it further to:

vec3 GetNormal() {
    vec3 n = normalize(vec3(texture(normalMap,uv).xy, C));
    return v_TBN*n;
}
$\endgroup$
4
  • 2
    $\begingroup$ BTW, you don't need to normalize twice. :) $\endgroup$ Commented Jul 27, 2017 at 20:27
  • $\begingroup$ @Nathan: sure, but that's what the OP did. I would use a signed RG texture too, or a quternion based pipeline. $\endgroup$ Commented Jul 27, 2017 at 20:43
  • $\begingroup$ @ybungalobill but can you point out if there is a problem with my approach because it seems to me like that's pretty similar to what I do.Also,performance wise,I would prefer doing less ALU operations in the fragment shader(the target is mobile platform) $\endgroup$
    – Michael IV
    Commented Jul 28, 2017 at 10:15
  • 2
    $\begingroup$ Oh, I missed that part. See, in principle it's the same. However, to get correct results you need to reorthogonalize the TBN per-fragment. Even without doing it, you can still improve the code performance wise. First, scale the normal maps so that z=1 and use GL_RG8_SNORM to store only the xy components. This already saves you half the memory bandwidth and the n=2n-1 calculation in the shader. Then your function becomes simply vec3 GetNormal(){ return v_TBN*normalize(vec3(texture(normalMap, uv).xy, C)); }, and that's already with correct scaling. $\endgroup$ Commented Jul 28, 2017 at 10:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.