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, 
这段代码用于创建三个不同的数据集对象:tum_dataset`, `tum_dataset_krum`, 和 `gaf_dataset`。这些数据集对象是基于 `BarianCropsDataset` 和 `GAFDataset` 类创建的。 对于 `tum_dataset` 数据集对象,它具有以下属性和参数: - `root` 参数指定数据集的根目录路径为 "../data/BavarianCrops"。 - `region` 参数设置为 "holl",表示数据集的区域为 "holl"。 - `partition` 参数设置为 "test",表示该数据集用于测试。 - `classmapping` 参数指定了类别映射的 CSV 文件路径,该文件位于 "../data/BavarianCrops/classmapping23.csv"。 - `scheme` 参数设置为 "random",表示数据集的划分方式为随机划分。 - `samplet` 参数设置为 50,表示从数据集中随机采样 50 个样本。 - `mode` 参数设置为 "traintest",表示该数据集用于训练和测试。 对于 `tum_dataset_krum` 数据集对象,它与 `tum_dataset` 的大部分参数相同,只有以下差异: - `region` 参数设置为 "krum",表示数据集的区域为 "krum"。 对于 `gaf_dataset` 数据集对象,它具有以下属性和参数: - `root` 参数指定数据集的根目录路径为 "/data/GAFdataset"。 - `region` 参数设置为 "holl",表示数据集的区域为 "holl"。 - `partition` 参数设置为 "test",表示该数据集用于测试。 - `scheme` 参数设置为 "random",表示数据集的划分方式为随机划分。 - `classmapping` 参数指定了类别映射的 CSV 文件路径,该文件位于 "../data/BavarianCrops/classmapping23.csv"。 - `features` 参数设置为 "all",表示数据集使用所有特征。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值