实际效果请看demo:纹理贴图
<https://edwardzhong.github.io/sites/demo/dist/webglTexture.html>




为了增加额外细节,提升真实感,我们使用了漫反射贴图和高光贴图,它们都是向三角形进行附加纹理。但是从光的视角来看是表面法线向量使表面被视为平坦光滑的表面。以光照算法的视角考虑的话,只有一件事决定物体的形状,那就是垂直于它的法线向量。砖块表面只有一个法向量,表面完全根据这个法向量被以一致的方式照亮。如果每个片元都用不同的法线会怎样?这样我们就可以根据表面细微的细节对法线向量进行改变;这样就会获得一种表面看起来要复杂得多的幻觉:



每个片元使用了自己的法线,我们就可以让光照相信一个表面由很多微小的(垂直于法线向量的)平面所组成,物体表面的细节将会得到极大提升。这种每个片元使用各自的法线,替代一个面上所有片元使用同一个法线的技术叫做法线贴图(normal
mapping)或凹凸贴图(bump mapping)。

以上都是从 LearnOpenGL CN 相关的文章摘抄 法线贴图
<https://learnopengl-cn.github.io/05%20Advanced%20Lighting/04%20Normal%20Mapping/>
。没办法,WebGL相关比较深入的知识你只能去看openGL,好在原理基本相同,WebGL1就是基于openGL es
2.0,WebGL2就是基于openGL es 3.0。

法线贴图

法线贴图就是用纹理中的颜色向量r、g、b存储法线向量的x、y、z。不过它们有另外的称呼:t (切线)、b (副切线)、n
(法线),它们组成了一个切线空间,被称为TBN坐标系。由于颜色与方向的表示范围有区别,颜色范围是[0,1],而作为表示位置方向的TBN坐标系则是[-1,1],那么从法线贴图取出来的值要使用的话,得进行转换。
// 将法线向量转换为范围[-1,1] vec3 normal = normalize(normal * 2.0 - 1.0);
法线贴图偏蓝是因为所有法线的指向都偏向z轴(0, 0, 1), 对应于rgb 中的
blue分量,也就是蓝色。法线向量从z轴方向向其他方向轻微偏移,于是颜色也就发生轻微变化,这样看起来便有了一种深度。例如,你可以看到顶部颜色倾向于偏绿,这是因为顶部的法线偏向于指向正y轴方向(0,
1, 0)对应于rgb总的 green分量,也就是绿色。


法线贴图的优点是可以用一个低精度模型表现出非常高的细节,看起来像高精度模型那样。


只需要500个三角形的简单网格加上法线贴图就能达到媲美4M个三角形的精细网格模型的效果,可以说法线贴图优势巨大,处理4M个三角形的复杂度简直不可想象。

但法线贴图也不是万能的,它也有缺点。因为它只是改变了物体表面的光照计算方式,所以不适合用在凹凸起伏较大的物体上,这些物体会有遮挡的效果,法线贴图是无法实现的。

而文章 法线贴图
<https://learnopengl-cn.github.io/05%20Advanced%20Lighting/04%20Normal%20Mapping/>
里面有很详细的原理讲解和切线推导过程,最后求出如下的公式,我这里也不再叙述了。


着色器

我这里使用了另外一种更加方便的算法,能达到同样的效果,原理就是使用导数(dFdx/dFdy)求出每个像素在插值化传值过来的点的变化率当成一个法线,请看函数
dHdxy_fwd。然后再通过与当前平面的法向量进行叉积 (cross),即可求得同时垂直于这两个方向的法向量,请看函数 perturbNormalArb
,而最终的这个法向量就是我们所要求的值,非常地高明。下面是glsl 内置的几个关键函数。
dFdx(p) //在x方向的偏导数 dFdy(p) //在y方向的偏导数 cross(p0,p1) //向量p0,p1的叉乘
我们用到的求导函数 dFdx /
dFdy,在WebGL1是需要开启扩展的,顶点着色器无需变动,主要变动的是片元着色器。具体的计算过程,请看如下片元着色器代码:
#extension GL_OES_standard_derivatives : enable// 注意要开启该扩展 //... uniform
sampler2D u_diffMap; uniform sampler2D u_specMap; uniform sampler2D u_normMap;
//... vec2 dHdxy_fwd() { vec2 dSTdx = dFdx( v_texcoord ); vec2 dSTdy = dFdy(
v_texcoord ); float Hll = bumpScale * texture2D( u_normMap, v_texcoord ).x;
float dBx = bumpScale * texture2D( u_normMap, v_texcoord + dSTdx ).x - Hll;
float dBy = bumpScale * texture2D( u_normMap, v_texcoord + 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 *= ( float( gl_FrontFacing ) * 2.0 - 1.0 ); vec3 vGrad = sign( fDet ) * (
dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm -
vGrad ); } //... // 从法线贴图计算出逐像素法线向量 vec3 normal = normal = perturbNormalArb(
-v_position, normal, dHdxy_fwd()); //... // 总的光照 gl_FragColor = vec4(ambient +
diffuse + specular, diffuseColor.a);
最后效果请看demo:纹理贴图
<https://edwardzhong.github.io/sites/demo/dist/webglTexture.html>

后记

相关资料 LearnOpenGL CN <https://learnopengl-cn.github.io/>

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信