three.js / 自定义 ShaderMaterial 实现 normal mapping

效果:
这里写图片描述

这里写图片描述

这次主要碰到两个难点:
1. 如何进行切线空间的变换,使得即使 mesh 发生变换,法线也依然是正确的。(图中平面已绕 x 轴旋转90°)
2. 正确计算光照

探索过程

一开始考虑基本上有3种可能:
1. 作为 attribute 引入,这也是 opengl 教程的方法
2. 在 vertex shader 里实现,有博客http://www.zwqxin.com/archives/shaderglsl/review-normal-map-bump-map.html 但是并看不懂
3. 在 frag shader 里实现
https://github.com/mrdoob/three.js/blob/6e89128f1ae239f29f2124a43133bb3d767b19bf/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl

原理博客:http://hacksoflife.blogspot.com/2009/11/per-pixel-tangent-space-normal-mapping.html

https://github.com/mrdoob/three.js/issues/7094 这个串就是讨论关于 three.js 的 tangent space 的实现问题的,可以看到在约 r70之前是有支持的,但是现在已经去掉了。
一开始是觉得按 attribute 的方法实现好像太累赘了,如果要做的话(按 learn-opengl 教程的风格),应该得取 geometry 的 face 信息,然后计算,然后新建一个 bufferGeometry 放入自定义 attribute。
不过经过这两天的研究,发现 three.js 用的是第三种办法,用 GLSL内置的函数dFdydFdx在片段着色器中计算TBN矩阵

一开始我用的就是这段代码:

https://github.com/mrdoob/three.js/blob/6e89128f1ae239f29f2124a43133bb3d767b19bf/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl

// Per-Pixel Tangent Space Normal Mapping
// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html

vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {

// Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988

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 ); // we do not care about the magnitude

vec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );
vec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );
vec3 N = normalize( surf_norm );
mat3 tsn = mat3( S, T, N );

vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;

mapN.xy *= normalScale;
mapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );

return normalize( tsn * mapN );

}

通过搜索 github 仓库,调用这个函数的代码是在
https://github.com/mrdoob/three.js/blob/6e89128f1ae239f29f2124a43133bb3d767b19bf/src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl#L23

normal = perturbNormal2Arb( -vViewPosition, normal );

vViewPosition = -modelViewPosition

// vertex shader
modelViewPosition = modelViewMatrix * vec4(position, 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值