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

👨‍⚕️ 主页: gis分享者

👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅!

👨‍⚕️ 收录于专栏:threejs gis工程师


文章目录


一、🍀前言

本文详细介绍如何基于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. ☘️代码样例

html 复制代码
<!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>

效果如下:

相关推荐
gis分享者17 小时前
学习threejs,使用CubeCamera相机创建反光效果
threejs·cubecamera·立方体相机·反光效果
gis分享者6 天前
学习threejs,通过SkinnedMesh来创建骨骼和蒙皮动画
threejs·蒙皮网格·skinnedmesh·骨骼和蒙皮动画
gis分享者7 天前
学习threejs,导入FBX格式骨骼绑定模型
threejs·骨骼动画·fbx
gis分享者8 天前
学习threejs,对模型多个动画切换展示
threejs·动画切换
allenjiao9 天前
webgl threejs 云渲染(服务器渲染、后端渲染)解决方案
webgl·云渲染·threejs·服务器渲染·后端渲染·云流化·三维云渲染
gis分享者14 天前
学习threejs,使用TWEEN插件实现动画
threejs·tween·补间动画
优雅永不过时·14 天前
Three.js 原生 实现 react-three-fiber drei 的 磨砂反射的效果
前端·javascript·react.js·webgl·threejs·three
gis分享者15 天前
学习threejs,使用第一视角控制器FirstPersonControls控制相机
threejs·第一人称视角
gis分享者16 天前
学习threejs,使用导入的模型生成粒子
threejs·模型粒子化