当前位置: 首页 > article >正文

学习threejs,使用设置normalMap法向量贴图创建更加细致的凹凸和褶皱

👨‍⚕️ 主页: gis分享者
👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅!
👨‍⚕️ 收录于专栏:threejs gis工程师


文章目录

  • 一、🍀前言
    • 1.1 ☘️THREE.MeshPhongMaterial高光材质
    • 1.2 normalMap 法向量贴图
  • 二、🍀使用设置normalMap法向量贴图创建更加细致的凹凸和褶皱
    • 1. ☘️实现思路
    • 2. ☘️代码样例


一、🍀前言

本文详细介绍如何基于threejs在三维场景中使用设置normalMap法向量贴图创建更加细致的凹凸和褶皱效果,亲测可用。希望能帮助到您。一起学习,加油!加油!

1.1 ☘️THREE.MeshPhongMaterial高光材质

THREE.MeshPhongMaterial 是 Three.js 中的一种材质类型,用于模拟物体表面的光照效果,包括漫反射(diffuse)和镜面反射(specular)。这种材质遵循 Phong 反射模型,可以模拟出光滑表面的高光效果,因此非常适合用来渲染金属、塑料、瓷器等具有光泽表面的物体。
常用属性:
THREE.MeshPhongMaterial 继承自 THREE.Material,并具有一些特定的属性,可以用来控制材质的外观:

color:材质的基本颜色,默认为白色(0xffffff)。可以是一个整数,表示十六进制颜色值。
map:基础颜色贴图,可以用来替代材质的颜色。可以是一个 THREE.Texture 对象。
alphaMap:透明度贴图,可以用来定义材质的透明度。可以是一个 THREE.Texture 对象。
emissive:自发光颜色,默认为黑色(0x000000)。即使在没有光源的情况下,也会显示这个颜色。
emissiveMap:自发光贴图,可以用来定义自发光的颜色。可以是一个 THREE.Texture 对象。
specular:高光颜色,默认为白色(0x111111)。高光颜色定义了镜面反射的颜色。
shininess:高光强度,默认为 30。高光强度定义了高光区域的锐度,数值越高,高光越集中。
opacity:材质的全局透明度,默认为 1(不透明)。
transparent:是否开启透明模式,默认为 false。如果设置为 true,则需要设置 opacity 或者使用 alphaMap。
side:指定材质在哪一面渲染,可以是 THREE.FrontSide(正面)、THREE.BackSide(背面)或 THREE.DoubleSide(双面)。
wireframe:是否启用线框模式,默认为 false。
visible:是否渲染该材质,默认为 true。
depthTest:是否进行深度测试,默认为 true。
depthWrite:是否写入深度缓冲区,默认为 true。
blending:混合模式,默认为 THREE.NormalBlending。可以设置为 THREE.AdditiveBlending、THREE.SubtractiveBlending 等。
vertexColors:是否启用顶点颜色,默认为 THREE.NoColors。可以设置为 THREE.VertexBasicColors、THREE.VertexColors 或 THREE.FaceColors。
flatShading:是否使用平滑着色,默认为 false。如果设置为 true,则每个面片都将使用平均法线。
envMap:环境贴图,可以用来模拟环境光照。可以是一个 THREE.Texture 对象。
reflectivity:环境光反射率,默认为 1。
refractionRatio:折射率,默认为 0.98。
combine:环境贴图的组合方式,默认为 THREE.MixOperation。
bumpMap:凹凸贴图,可以用来模拟表面细节。可以是一个 THREE.Texture 对象。
bumpScale:凹凸贴图的比例,默认为 1。
normalMap:法线贴图,可以用来模拟表面细节。可以是一个 THREE.Texture 对象。
normalScale:法线贴图的比例,默认为 Vector2(1, 1)。
displacementMap:置换贴图,可以用来改变表面的高度。可以是一个 THREE.Texture 对象。
displacementScale:置换贴图的比例,默认为 1。
displacementBias:置换贴图的偏移,默认为 0。

1.2 normalMap 法向量贴图

概念:
normalMap 法向量贴图‌是一种在3D图形渲染中使用的技术,主要用于增强模型表面的细节和真实感。它通过使用一张特殊的纹理贴图来模拟物体表面的凹凸效果,从而在视觉上增加模型的细节和复杂度。
原理:
法线贴图的基本原理是将物体的法线信息存储在一张RGB纹理中。每个像素的RGB值代表该位置法线的方向,通过这种方式,可以在渲染时利用这些法线信息来模拟表面的凹凸效果。这种方法可以显著降低渲染时需要的面数和计算量,从而优化渲染效果‌。
与Bump Mapping(凹凸贴图)的区别:
法线贴图与Bump Mapping(凹凸贴图)是两种不同的技术,尽管它们在某些情况下可以相互替代。Bump Mapping需要计算每个像素的法线信息,而法线贴图则通过预计算的法线贴图来实现,简化了计算过程。此外,法线贴图能够提供更丰富的细节和更真实的视觉效果‌23。

二、🍀使用设置normalMap法向量贴图创建更加细致的凹凸和褶皱

1. ☘️实现思路

  • 1、初始化renderer渲染器
  • 2、初始化Scene三维场景
  • 3、初始化camera相机,定义相机位置 camera.position.set。
  • 4、初始化THREE.AmbientLight环境光源,scene场景加入环境光源,初始化THREE.DirectionalLight平行光源,设置平行光源位置,设置平行光源投影,scene添加平行光源,创建THREE.PointLight点光源,设置点光源位置,给点光源添加模型,scene添加点光源。
  • 5、加载几何模型:创建THREE.AxesHelper坐标辅助工具,创建THREE.PlaneBufferGeometry平面几何体、THREE.GridHelper地板割线,scene场景中加入创建的平面几何体和地板割线。创建正常纹理贴图对象normal和法向量纹理贴图对象bump,根据创建的纹理创建两个THREE.MeshPhongMaterial高光材质对象material1(使用normal和bump贴图)、material2(使用normal贴图),创建立方体THREE.BoxGeometry对象geometry,根据立方体geometry和材质THREE.MeshPhongMaterial对象创建两个THREE.Mesh网格对象cube1(使用material1材质)、cube2(使用material2材质)并设置位置,scene场景中加入cube1和cube2。具体实现参考代码样例。
  • 6、加入gui、controls控制,加入stats监控器,监控帧数信息。
  • 7、定义render方法,执行点光源动画以及其他渲染动画,具体代码参考代码样例。

2. ☘️代码样例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>learn53(使用设置NORMALMAP法向量贴图创建更加细致的凹凸和褶皱)</title>
    <script src="lib/threejs/127/three.js-master/build/three.js"></script>
    <script src="lib/threejs/127/three.js-master/examples/js/controls/OrbitControls.js"></script>
    <script src="lib/threejs/127/three.js-master/examples/js/libs/stats.min.js"></script>
    <script src="lib/threejs/127/three.js-master/examples/js/libs/dat.gui.min.js"></script>
    <script src="lib/js/Detector.js"></script>
    <script src="lib/js/ImageUtils.js"></script>
</head>
<style type="text/css">
    html, body {
        margin: 0;
        height: 100%;
    }

    canvas {
        display: block;
    }
</style>
<body onload="draw()">
</body>
<script>
  var renderer, camera, scene, gui, light, stats, controls, cube1, cube2, pointLight
  var angle = 0, radian, r = 5
  var initRender = () => {
    renderer = new THREE.WebGLRenderer({antialias: true})
    renderer.setSize(window.innerWidth, window.innerHeight)
    renderer.setPixelRatio(window.devicePixelRatio)
    renderer.setClearColor(0xeeeeee)
    renderer.shadowMap.enabled = true
    document.body.appendChild(renderer.domElement)
  }
  var initScene = () => {
    scene = new THREE.Scene()
    scene.backgroundColor = new THREE.Color(0xa0a0a0)
    scene.fog = new THREE.Fog(0xa0a0a0, 5, 50)
  }
  var initCamera = () => {
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200)
    camera.position.set(0, 12, 15)
  }
  var initGui = () => {
    gui = {
      normalScale: 1,
      animation: false,
      changeTexture: () => {
        var image = new Image()
        image.src = 'data/texture/normalmap/crate.gif'
        image.onload = (ev) => {
          var texture = new THREE.Texture(image)
          texture.needsUpdate = true
          cube1.material.map = texture

          var normal = new THREE.Texture(THREE.ImageUtils.getNormalMap(image))
          normal.needsUpdate = true
          cube1.material.normalMap = normal

          cube2.material.map = texture
        }
      }
    }
    var datGui = new dat.GUI()
    datGui.add(gui, 'normalScale', -2, 30).onChange(e => {
      cube1.material.normalScale.set(e, e)
      cube1.material.needsUpdate = true
    })
    datGui.add(gui, 'animation')
    datGui.add(gui, 'changeTexture')
  }
  var initLight = () => {
    scene.add(new THREE.AmbientLight(0x444444))

    light = new THREE.DirectionalLight(0xffffff)
    light.position.set(0, 20, 10)
    light.castShadow = true
    scene.add(light)

    pointLight = new THREE.PointLight(0x00ffff)
    pointLight.position.set(0, 5, 0)
    scene.add(pointLight)

    // 点光源添加模型,显示位置
    pointLight.add(new THREE.Mesh(new THREE.SphereGeometry(0.05, 20, 20), new THREE.MeshBasicMaterial({color: 0x00ffff})))
  }
  var initModel = () => {
    var helper = new THREE.AxesHelper(50)
    scene.add(helper)

    // 地板
    var mesh = new THREE.Mesh(new THREE.PlaneBufferGeometry(200, 200), new THREE.MeshPhongMaterial({
      color: 0xffffff,
      depthWrite: false
    }))
    mesh.rotation.x = -0.5 * Math.PI
    mesh.receiveShadow = true
    scene.add(mesh)

    // 格网
    var grid = new THREE.GridHelper(200, 50, 0x000000, 0x000000)
    grid.material.opacity = 0.2
    grid.material.transparent = true
    scene.add(grid)
    // 凹凸纹理
    var bump = new THREE.TextureLoader().load('data/texture/normalmap/plaster-normal.jpg')
    // 正常纹理
    var normal = new THREE.TextureLoader().load('data/texture/normalmap/plaster.jpg')
    var material1 = new THREE.MeshPhongMaterial({
      map: normal
    })
    material1.normalMap = bump

    var geometry = new THREE.BoxGeometry(6, 6, 6)

    cube1 = new THREE.Mesh(geometry, material1)
    cube1.position.set(-5, 5, 0)
    cube1.rotation.y += Math.PI / 6
    scene.add(cube1)

    var material2 = new THREE.MeshPhongMaterial({
      map: normal
    })
    cube2 = new THREE.Mesh(geometry, material2)
    cube2.position.set(5, 5, 0)
    cube2.rotation.y -= Math.PI / 6
    scene.add(cube2)
  }
  var initStats = () => {
    stats = new Stats()
    document.body.appendChild(stats.domElement)
  }
  var initControls = () => {
    controls = new THREE.OrbitControls(camera, renderer.domElement)
    controls.target.set(0, 5, 0)
    controls.enableDamping = true
  }
  var render = () => {
    if (gui.animation) {
      cube1.rotation.y += 0.01
      cube2.rotation.y -= 0.01
    }
    angle += 1
    radian = angle / 180 * Math.PI
    var x = Math.sin(radian)
    var y = Math.cos(radian)

    if (angle % 720 > 360) {
      y = -y + 2
    }

    pointLight.position.z = x * r
    pointLight.position.x = y * r - r
  }
  var onWindowResize = () => {
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()
    renderer.setSize(window.innerWidth, window.innerHeight)
  }
  var animate = () => {
    render()
    stats.update()
    controls.update()
    renderer.render(scene, camera)
    requestAnimationFrame(animate)
  }
  var draw = () => {
    if (!Detector.webgl) Detector.addGetWebGLMessage()
    initGui()
    initRender()
    initScene()
    initCamera()
    initLight()
    initModel()
    initStats()
    initControls()

    animate()

    window.onresize = onWindowResize
  }
</script>
</html>

效果如下:
在这里插入图片描述


http://www.kler.cn/a/409858.html

相关文章:

  • Burp入门(1)
  • React 常见问题解答:设置、安装、用户事件和最佳实践
  • 汽车HiL测试:利用TS-GNSS模拟器掌握硬件性能的仿真艺术
  • pytorch经典训练流程
  • uni-app 界面TabBar中间大图标设置的两种方法
  • Vue.js 学习总结(14)—— Vue3 为什么推荐使用 ref 而不是 reactive
  • Python Selenium介绍(一)
  • 深入解析Java面向对象编程:Object类、泛型、序列化与网络编程
  • 如何通过cPanel创建品牌电子邮件
  • H5流媒体播放器EasyPlayer.js网页直播/点播播放器如果H.265视频在播放器上播放不流畅,可以考虑的解决方案
  • JavaWeb——Ajax、Element、打包部署
  • 鱼眼相机模型-MEI
  • 24/11/25 视觉笔记 深度传感器和手势识别
  • Spring Boot英语知识网站:性能优化
  • 【Linux学习】【Ubuntu入门】2-3 make工具和makefile引入
  • MySQL基础知识大总结
  • Vue2 常见知识点(一)
  • RGB图片 、RGBA、 灰度图、二值图
  • 拳皇98笔记
  • 【人工智能】Python常用库-Pandas常用方法教程
  • Mybatis PLUS查询对List使用OR模糊查询
  • 读《Effective Java》笔记 - 条目7
  • C#基础控制台程序
  • stable Diffusion官方模型下载
  • 软件工程设计模式--结构型设计模式
  • 如何使用 PyCharm 工具连接远程服务器进行开发