# WebGL NormalMap

• original
• opengl normal map
• Reference example
• Analyze according to the picture below
• The first one is that all normals of the fragments are perpendicular to the plane, and the second one is not
• The first picture is completely diminishing, and the second picture is still brighter than the side at a distance from the light.

• Blueish, meaning that the normal is more perpendicular to the fragment; greenish, meaning that the normal is higher than perpendicular to the surface

• The rgb of the normal map stores t (tangent, X axis), b (secondary tangent, Y axis), and n (normal, Z axis) respectively, forming a tangent space, which is called the TBN coordinate system.

• The color range of the normal map is [0,1], and the TBN coordinate system that represents the position direction is [-1,1], and the XY of the coordinate system NDC is also [-1,1]
• Normal map to TBN coordinate system
```//convert the normal range for the normal maps [-1,1]
Vec3 normal = the normalize (normal * 2.0 - 1.0 ); //
copy the code```
• The advantage of the normal map is that a low-precision model can be used to show very high details, which looks like a high-precision model.
• The calculation method of illumination on the surface of the object is changed, and it is not suitable for use on objects with large concave and convex, and the effect of mutual occlusion of objects cannot be achieved.

• The TBN matrix calculated in the actual situation is often not an orthogonal matrix. It is necessary to perform the Gram-Schmidt process of this matrix to orthogonalize this matrix.

• An algorithm for calculating the normal vector, the derivative (dFdx/dFdy) calculates the rate of change of each pixel at the point passed by the interpolation as a normal
• View this normal material
• You can use the color picker to know the rgb of a point (I used QQ screenshots)
• Anything less than 255/2 in rgb will be opposite to the screen space xyz axis direction

```#extension GL_OES_standard_derivatives: enable//Note To open the extension
copy the code```
• Normal test statement
```gl_FragColor = vec4 (packNormalToRGB (normal)
, 1.0); duplicated code```
```#version 300 es
#define varying in
out highp vec4 pc_fragColor;
#define gl_FragColor pc_fragColor
#define gl_FragDepthEXT gl_FragDepth
#define texture2D texture
#define textureCube texture
#define texture2DProj textureProj
#define texture2DLodEXT textureLod
#define texture2DProjLodEXT textureProjLod
#define textureCubeLodEXT textureLod
precision highp float;
precision highp int;
#define HIGH_PRECISION
#define GAMMA_FACTOR 2
#define DOUBLE_SIDED
uniform mat4 viewMatrix;
uniform vec3 cameraPosition;
uniform bool isOrthographic;
vec4 LinearToLinear ( in vec4 value )  {
return value;
}
vec4 GammaToLinear ( in vec4 value, in float gammaFactor )  {
return vec4( pow( value.rgb, vec3( gammaFactor) ), value.a );
}
vec4 LinearToGamma ( in vec4 value, in float gammaFactor )  {
return vec4( pow( value.rgb, vec3( 1.0/gammaFactor) ), value.a );
}
vec4 sRGBToLinear ( in vec4 value )  {
return vec4 (Mix (POW (value.rgb * .9478672986 + Vec3 ( 0.0521327014 ), Vec3 ( 2.4 )), value.rgb * 0.0773993808 , Vec3 (lessThanEqual (value.rgb, Vec3 ( 0.04045 ) )) ), value.a );
}
vec4 LinearTosRGB ( in vec4 value )  {
return vec4( mix( pow( value.rgb, vec3( 0.41666 )) * 1.055 -vec3( 0.055 ), value.rgb * 12.92 , vec3( lessThanEqual( value.rgb, vec3 ) ( 0.0031308 ) )) ), value.a );
}
vec4 RGBEToLinear ( in vec4 value )  {
return vec4 (EXP2 value.rgb * (* value.a 255.0 - 128.0 ), 1.0 );
}
vec4 LinearToRGBE ( in vec4 value )  {
float maxComponent = max( max( value.r, value.g ), value.b );
float fExp = clamp( ceil( log2( maxComponent) ),- 128.0 , 127.0 );
return vec4( value.rgb/exp2( fExp ), (fExp + 128.0 )/255.0 );
}
vec4 RGBMToLinear ( in vec4 value, in float maxRange )  {
return vec4( value.rgb * value.a * maxRange, 1.0 );
}
vec4 LinearToRGBM ( in vec4 value, in float maxRange )  {
float maxRGB = max( value.r, max( value.g, value.b) );
float M = clamp( maxRGB/maxRange, 0.0 , 1.0 );
M = ceil( M * 255.0 )/255.0 ;
return vec4( value.rgb/(M * maxRange ), M );
}
vec4 RGBDToLinear ( in vec4 value, in a float maxRange )  {
return vec4 (value.rgb * ((maxRange/255.0 )/value.a), 1.0 );
}
vec4 LinearToRGBD ( in vec4 value, in float maxRange )  {
float maxRGB = max( value.r, max( value.g, value.b) );
float D = max( maxRange/maxRGB, 1.0 );
CLAMP = D (Floor (D)/255.0 , 0.0 , 1.0 );
return vec4 (value.rgb * (D * ( 255.0/maxRange)), D);
}
const mat3 cLogLuvM =  mat3( 0.2209 , 0.3390 , 0.4184 , 0.1138 , 0.6780 , 0.7319 , 0.0102 , 0.1130 , 0.2969 );
vec4 LinearToLogLuv ( in vec4 value )  {
vec3 Xp_Y_XYZp = cLogLuvM * value.rgb;
Xp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6 , 1e-6 , 1e-6 ) );
vec4 vResult;
vResult.xy = Xp_Y_XYZp.xy/Xp_Y_XYZp.z;
float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0 ;
vResult.w = fract( Le );
= vResult.z (Le - (Floor (vResult.w * 255.0 ))/255.0 )/255.0 ;
return vResult;
}
const MAT3 cLogLuvInverseM MAT3 = ( 6.0014 , - 2.7008 , - 1.7996 , - 1.3320 , 3.1029 , - 5.7721 , 0.3008 , - 1.0882 , 5.6268 );
vec4 LogLuvToLinear ( in vec4 value )  {
Le * = value.z a float 255.0 + value.w;
vec3 Xp_Y_XYZp;
Xp_Y_XYZp.y = exp2( (Le- 127.0 )/2.0 );
Xp_Y_XYZp.z = Xp_Y_XYZp.y/value.y;
Xp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;
vec3 vRGB = cLogLuvInverseM * Xp_Y_XYZp.rgb;
return vec4( max( vRGB, 0.0 ), 1.0 );
}
vec4 linearToOutputTexel ( vec4 value ) {
return LinearToLinear( value );
}
#define NORMAL
uniform float opacity;
# if defined( FLAT_SHADED) || defined( USE_BUMPMAP) || defined( TANGENTSPACE_NORMALMAP)
varying vec3 vViewPosition;
#endif
varying vec3 vNormal;
#ifdef USE_TANGENT
varying vec3 vTangent;
varying vec3 vBitangent;
#endif
#endif
vec3 packNormalToRGB ( const in vec3 normal )   {
return normalize( normal) * 0.5 + 0.5 ;
}
vec3 unpackRGBToNormal ( const in vec3 rgb )   {
return  2.0 * rgb.xyz- 1.0 ;
}
const float PackUpscale = 256./255 .;
const float UnpackDownscale = 255./256 .;
const vec3 PackFactors = vec3( 256. * 256. * 256. , 256. * 256. , 256. );
const vec4 UnpackFactors = UnpackDownscale/vec4( PackFactors, 1. );
const float ShiftRight8 = 1./256 .;
vec4 packDepthToRGBA ( const in float v )   {
vec4 r = vec4( fract( v * PackFactors ), v );
r.yzw -= r.xyz * ShiftRight8;
return r * PackUpscale;
}
float unpackRGBAToDepth ( const in vec4 v )   {
return dot( v, UnpackFactors );
}
vec4 pack2HalfToRGBA ( vec2 v ) {
vec4 r = vec4( vx, fract( vx * 255.0 ), vy, fract( vy * 255.0 ));
return vec4( rx-ry/255.0 , ry, rz-rw/255.0 , rw);
}
vec2 unpackRGBATo2Half ( vec4 v ) {
return vec2( vx + (vy/255.0 ), vz + (vw/255.0 ) );
}
float viewZToOrthographicDepth ( const in float viewZ, const in float near, const in float far )     {
return (viewZ + near)/(near-far );
}
float orthographicDepthToViewZ ( const in float linearClipZ, const in float near, const in float far )     {
return linearClipZ * (near-far)-near;
}
float viewZToPerspectiveDepth ( const in float viewZ, const in float near, const in float far )     {
return (( near + viewZ) * far)/(( far-near) * viewZ );
}
float perspectiveDepthToViewZ ( const in float invClipZ, const in float near, const in float far )     {
return (near * far)/((far-near) * invClipZ-far );
}
# if (defined( USE_UV) &&! defined( UVS_VERTEX_ONLY))
varying vec2 vUv;
#endif
////////////////////////////////
//There is a bumpMap
#ifdef USE_BUMPMAP
uniform sampler2D bumpMap;
uniform float bumpScale;
vec2 dHdxy_fwd () {
vec2 dSTdx = dFdx( vUv );
vec2 dSTdy = dFdy( vUv );
float Hll = bumpScale * texture2D( bumpMap, vUv ).x;
float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x-Hll;
float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x-Hll;
return vec2( dBx, dBy );
}
vec3 perturbNormalArb ( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {
vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z) );
vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z) );
vec3 vN = surf_norm;
vec3 R1 = cross( vSigmaY, vN );
vec3 R2 = cross( vN, vSigmaX );
float fDet = dot( vSigmaX, R1 );
* = FDET (a float (gl_FrontFacing) * 2.0 - 1.0 );
vec3 vGrad = sign( fDet) * (dHdxy.x * R1 + dHdxy.y * R2 );
return normalize( abs( fDet) * surf_norm-vGrad );
}
#endif

//Normal Map
#ifdef USE_NORMALMAP
uniform sampler2D normalMap;
uniform vec2 normalScale;
#endif
#ifdef OBJECTSPACE_NORMALMAP
uniform mat3 normalMatrix;
#endif
# if ! defined (USE_TANGENT) && (defined (TANGENTSPACE_NORMALMAP) || defined (USE_CLEARCOAT_NORMALMAP))
vec3 perturbNormal2Arb ( vec3 eye_pos, vec3 surf_norm, vec3 mapN ) {
vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z) );
vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z) );
vec2 st0 = dFdx( vUv.st );
vec2 st1 = dFdy( vUv.st );
float scale = sign( st1.t * st0.s-st0.t * st1.s );
vec3 S  = (q0 * st1.t-q1 * st0.t) * scale;
vec3 T = (-q0 * st1.s + q1 * st0.s) * scale;
//https://github.com/mrdoob/three.js/issues/17559
if (S != vec3( 0.0 ))
S = normalize(S);
if (T != vec3( 0.0 ))
T = normalize(T);
vec3 N = normalize( surf_norm );
mat3 tsn = mat3( S, T, N );
* = mapN.xy (a float (gl_FrontFacing) * 2.0 - 1.0 );
return the normalize (TSN mapN *);
}
#endif
# if defined( USE_LOGDEPTHBUF) && defined( USE_LOGDEPTHBUF_EXT)
uniform float logDepthBufFC;
varying float vFragDepth;
varying float vIsPerspective;
#endif
# if  0 > 0
varying vec3 vClipPosition;
uniform vec4 clippingPlanes[ 0 ];
#endif
Copy code```
• normalMaterial main function
```void main() {
#if 0> 0
vec4 plane;
#if 0 <0
bool clipped = true ;
#endif
#endif
#if defined( USE_LOGDEPTHBUF) && defined( USE_LOGDEPTHBUF_EXT)
gl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord .z: log2 (vFragDepth) * logDepthBufFC * 0.5 ;
#endif
vec3 fdx = vec3 ( dFdx (vViewPosition.x ), dFdx(VViewPosition.y), dFdx (vViewPosition.z));
Vec3 FDY = Vec3 ( dFdy (vViewPosition.x), dFdy (vViewPosition.y), dFdy (vViewPosition.z));
Vec3 Normal = the normalize ( Cross (FDX, FDY));
#else
Vec3 Normal = the normalize (vNormal);
#ifdef DOUBLE_SIDED
Normal Normal * = ( a float ( gl_FrontFacing ) * 2.0 - 1.0 );
#endif
#ifdef USE_TANGENT
Vec3 Tangent = the normalize (vTangent);
Vec3 bitangent = the normalize (vBitangent);
#ifdef DOUBLE_SIDED
Tangent Tangent * = ( a float ( gl_FrontFacing ) * 2.0 - 1.0 );
* = bitangent bitangent ( a float ( gl_FrontFacing ) * 2.0 - 1.0 );
#endif
#if defined (TANGENTSPACE_NORMALMAP) || defined (USE_CLEARCOAT_NORMALMAP)
MAT3 VTBN = MAT3 (Tangent, bitangent, Normal);
#endif
#endif
#endif
Vec3 geometryNormal = normal;

//three calculation mode
#ifdef OBJECTSPACE_NORMALMAP//object space calculation
Normal = texture2D (normalmap, VUV) .xyz * 2.0 - 1.0 ;   //get normal map
#ifdef FLIP_SIDED//
normal =-normal;
#endif
#ifdef DOUBLE_SIDED doubleside//
Normal Normal * = ( a float ( gl_FrontFacing ) * 2.0 - 1.0 );
#endif
Normal = the normalize (normalMatrix * Normal);
#elif defined (TANGENTSPACE_NORMALMAP)//tangent space calculation
Vec3 mapN = texture2D ( normalmap, VUV) .xyz * 2.0 - 1.0 ;
mapN.xy *= normalScale;
#ifdef USE_TANGENT//If there is,
normal = normalize (vTBN * mapN );
#else//If there is no
normal = perturbNormal2Arb( -vViewPosition, normal, mapN );
#endif
#elif defined( USE_BUMPMAP)//bump calculation
normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );
#endif

}
Copy code```