three.js自定义材质 切线空间及阴影


前言

法线贴图中的法线向量定义在切线空间中,在切线空间中,法线永远指着正z方向。切线空间是位于三角形表面之上的空间:法线相对于单个三角形的本地参考框架。它就像法线贴图向量的本地空间;它们都被定义为指向正z方向,无论最终变换到什么方向。使用一个特定的矩阵我们就能将本地/切线空间中的法线向量转成世界或视图空间下,使它们转向到最终的贴图表面的方向。

摘自法线贴图 - LearnOpenGL CN

一、three.js获取切线空间

新版three.js 的BufferGeometryUtils.js 删除了computeTangents方法,以computeMikkTSpaceTangents 替代

computeMikkTSpaceTangents 参数为( BuffGeoMetry , MikkTSpace , negateSign )

  • negateSign -- 是否对每个切线的符号分量 (.w) 取反。某些格式的法线贴图约定需要,包括 glTF。

返回切线数据(vec4)

mikktspace 为 three/examples/jsm/libs/mikktspace.module.js (旧版没有,可以npm引入)

使用computeMikkTSpaceTangents前需要等MikkTSpace.ready完成

二、使用步骤

1.初始化initMikkTSpace

代码如下:

import { computeMikkTSpaceTangents } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
import { wasm,isReady,ready,generateTangents } from 'three/examples/jsm/libs/mikktspace.module.js'async initMikkTSpace(cb){await readycb()}const geo01 = new THREE.SphereGeometry(6, 64, 32)// const geo01 = new THREE.BoxGeometry( 6, 6, 6 );this.initMikkTSpace(cb=>{let MikkTSpace = {wasm:wasm,isReady:isReady,generateTangents:generateTangents}computeMikkTSpaceTangents(geo01,MikkTSpace)
})

2.编辑材质

getShadowMask()返回的是阴影

主要步骤:1.attribute vec4 tangent;是computeMikkTSpaceTangents直接添加到geo的切线数据

2.

vec3 nowPoint = vec3((modelViewMatrix * vec4( position, 1.0 )).xyz);

lightToPos = myLight - nowPoint;

获取光与点的向量

vNormal = normalize(normalMatrix * normal);//获取法线

vec3 vTangent = normalize( normalMatrix * tangent.xyz );//切线

vec3 vBinormal = normalize(cross( vNormal, vTangent ) * tangent.w);//副切线

tbn = mat3(vTangent, vBinormal, vNormal);//切线空间

代码如下:

initShader(tangent,myLight){let textureLoader = new THREE.TextureLoader();let vertexShader = `varying vec3 vNormal;varying vec2 vUv;attribute vec4 tangent;varying vec4 vtangent;varying mat3 tbn;uniform vec3 myLight;varying vec3 lightToPos;${THREE.ShaderChunk[ "common" ]}${THREE.ShaderChunk[ "bsdfs" ]}${THREE.ShaderChunk[ "shadowmap_pars_vertex" ]}void main(){${THREE.ShaderChunk['beginnormal_vertex']}${THREE.ShaderChunk['defaultnormal_vertex']}${THREE.ShaderChunk[ "begin_vertex" ]}${THREE.ShaderChunk[ "project_vertex" ]}${THREE.ShaderChunk[ "worldpos_vertex" ]}${THREE.ShaderChunk[ "shadowmap_vertex" ]}vec3 nowPoint = vec3((modelViewMatrix * vec4( position, 1.0 )).xyz);lightToPos = myLight - nowPoint;vNormal = normalize(normalMatrix * normal);vec3 vTangent = normalize( normalMatrix * tangent.xyz );vec3 vBinormal = normalize(cross( vNormal, vTangent ) * tangent.w);tbn = mat3(vTangent, vBinormal, vNormal);vUv = uv;vtangent = tangent;gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );}`let fragmentShader = `// uniform vec3 light;varying vec3 vNormal;varying vec3 lightToPos;varying vec2 vUv;varying vec4 vtangent;uniform sampler2D u_texture;uniform sampler2D u_textureNormal;varying mat3 tbn;${THREE.ShaderChunk[ "common" ]}${THREE.ShaderChunk[ "packing" ]}${THREE.ShaderChunk[ "bsdfs" ]}${THREE.ShaderChunk[ "lights_pars_begin" ]}${THREE.ShaderChunk[ "shadowmap_pars_fragment" ]}${THREE.ShaderChunk[ "shadowmask_pars_fragment" ]}//获取纹理颜色vec3 mygetPixelColor(sampler2D mytexture) {return texture2D(mytexture, vUv).rgb;}void main(){vec3 normalColor = mygetPixelColor(u_textureNormal);normalColor = normalColor * 2.0 - 1.0;vec3 anynormalColor = normalize(tbn * normalColor);//处理光照float diff = max( dot(anynormalColor , normalize(lightToPos)) , 0.0 );vec3 diffuse = diff * vec3(1,1,1);//diff * lightColorvec3 textureColor = mygetPixelColor(u_texture);vec3 addDiffuse = textureColor + diffuse;vec3 shadowColor = vec3(0,0,0);vec3 addShadow = mix( shadowColor , addDiffuse ,getShadowMask());gl_FragColor = vec4(addShadow,1.0);}`//着色器材质let sm = new THREE.ShaderMaterial({uniforms: THREE.UniformsUtils.merge( [THREE.UniformsLib[ "lights" ],{opacity:  { type: 'f', value: 1.0 },// tangent:  { value: tangent },myLight:  { value: myLight.position},u_texture:{value:textureLoader.load(require('../../assets/brickwall.jpg'))},u_textureNormal:{value:textureLoader.load(require('../../assets/brickwall_normal.jpg'))},}] ),vertexShader: vertexShader,fragmentShader: fragmentShader,side: THREE.FrontSide,lights: true});return sm},

3.阴影处理

renderer.shadowMap.enabled = true;...let light = new THREE.DirectionalLight(0xffffff);light.position.set(42,60,0);//告诉平行光需要开启阴影投射light.castShadow = true;light.shadow.mapSize.width = 1024; // default 512light.shadow.mapSize.height = 1024; // default 512//阴影相机范围light.shadow.camera.near = 0.5; // default 0.5light.shadow.camera.far = 100; // default 500light.shadow.camera.left = -30light.shadow.camera.right = 30light.shadow.camera.top = 30light.shadow.camera.bottom = -30scene.add(light);

效果

材质图片

three.js自定义材质 切线空间及阴影相关推荐

  1. UE4 无需切线空间应用凹凸贴图

    Unreal Engine 4.9 照亮环境 凹凸贴图(Bump mapping) 最早由一名图形程序员发明(1978 James Blinn),它通过调整后的着色计算 来创建凹凸表面的假象,无需增加 ...

  2. Cesium中自定义材质material

    文章转自: https://blog.csdn.net/weixin_38676065/article/details/126123975 学习参考文章:https://github.com/Anal ...

  3. 冯乐乐 unity_Unity常用矩阵运算的推导补遗——切线空间

    在上一篇文章中,我写了一些关于Unity中各个坐标空间及其转换矩阵是如何得到的,说实在的,我是那种"记忆需要依靠外部装置存储"类.如同<攻壳机动队>的电子脑一样的人,每 ...

  4. OpenGL 法线贴图 切线空间 整理

    1. What`s Bump Mapping? Bump Mapping通过改变几何体表面各点的法线,使本来是平的东西看起来有凹凸的效果,是一种欺骗眼睛的技术:). 我们知道,如果几何体表面有高低不平 ...

  5. 处理顶点——通过切线空间的凹凸映射添加逐像素细节

    问题 虽然前一个教程中具有不变法线的平面物体工作良好,但如果对一个曲面或有转角的表面进行凹凸映射仍会遇到麻烦. 主要问题是包含在凹凸映射中的偏离法线是在切线空间中的,这意味着它与默认法线有联系. 为了 ...

  6. Unity Shader - 切线空间的法线贴图应用(T2W W2T)

    法线贴图 法线贴图(或是法线纹理)其实就是一张图片中的RGB通道分别存储着法线方向的纹理(有些为了数据压缩将X,Y存储在RG通道,Z是通过1-dot(xy,xy)来近似计算). 它的由来是因为高模运行 ...

  7. Cesium 1.02.0 及以上版本下自定义材质报错:[Cesium WebGL] ERROR: 0:1: ‘varying‘ : Illegal use of reserved word

    Cesium 1.02.0 及以上版本下自定义材质报错:[Cesium WebGL] ERROR: 0:1: 'varying' : Illegal use of reserved word 报错原因 ...

  8. Unity Shader 学习笔记(5)Shader变体、Shader属性定义技巧、自定义材质面板

    写在之前 Shader变体.Shader属性定义技巧.自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用 ...

  9. threejs 三面体_Three.js 自定义了一个几何体

    几何体本质: 立方体几何体BoxGeometry本质上就是一系列的顶点构成,只是Threejs的APIBoxGeometry把顶点的生成细节封装了,用户可以直接使用. 比如一个立方体网格模型,有6个面 ...

最新文章

  1. 通过Internet与数据链路层传输信息时会发生什么?—Vecloud微云
  2. jedis使用_Mybatis的二级缓存、使用Redis做二级缓存
  3. qt mysql查询中文相等_请教:Qt如何实现查询数据库中具有中文表名的表
  4. python软件设计数据分析统计服_Python 和 R 数据分析/挖掘工具互查
  5. inject 响应式_Vue 3 组合式 provide/inject
  6. 提高jQuery的性能
  7. Java私人学习笔记——第2章 数据类型和运算符
  8. JAVA 实现《俄罗斯方块》游戏|CSDN创作打卡
  9. 《网络基础》p84.interface gigabitethernet 0/0/0报错
  10. 图片批量转换成jpg格式的方法!
  11. 解决电脑上装了两个不同版本的sqlserver 导致索引越界等一系列问题。
  12. 卡麦吉梅隆大学 计算机排名,2019年卡内基梅隆大学信息系统排名
  13. html图片顺时针倾斜50度,【资源】纯CSS右上角倾斜四十五度封页角效果
  14. HTML技术(基础/列表/表格/表单)
  15. ffplay控制音量方法
  16. 微信小程序详解 php,微信小程序canvas基础详解
  17. [转]结合php5与zend_search_lucene来创建一个全文搜索引擎
  18. HDU-OJ 杭电1495非常可乐
  19. mysql源代码解析经典类——Field类
  20. 推荐一本数学科普:黎曼猜想漫谈

热门文章

  1. [RK3399][Android7.1] Uboot display 加载过程小结
  2. 《基于关联规则算法的电子商务商品推荐系统设计与实现》 CSTPCD期刊
  3. 可穿戴医疗设备是“香饽饽”,但也有数据和安全之痛
  4. 如何理解信息隐蔽和局部化?
  5. MartrikonOP java Unknown error (80040154)
  6. android logo动画,Android logo与开机动画
  7. POJ-3273 Monthly Expense 解题报告
  8. 14 ARP课堂笔记
  9. JavaScript基础:Iterator概念及用法
  10. js 走马灯效果 无缝 不断实现