首页 > 其他分享 >【Three.JS零基础入门教程】第七篇:材质详解

【Three.JS零基础入门教程】第七篇:材质详解

时间:2024-08-21 16:52:33浏览次数:14  
标签:const 入门教程 THREE Three 第七篇 window renderer new scene

  前期回顾:

【Three.JS零基础入门教程】第一篇:搭建开发环境

【Three.JS零基础入门教程】第二篇:起步案例

【Three.JS零基础入门教程】第三篇:开发辅助

【Three.JS零基础入门教程】第四篇:基础变换

【Three.JS零基础入门教程】第五篇:项目规划

【Three.JS零基础入门教程】第六篇:物体详解

不同的材质的应用场景不同, 从整体上分为两大类

  • 普通材质: 不受光线的影响
  • 物理材质: 模拟物体对光线的反射, 在不同的光照下显示效果不同

推荐

  • 快速写Deom, 简单使用的话, 采用MeshNormalMaterial, 最简单, 便于观察, 有明显的立体效果
  • 在生产时使用MeshStandardMaterial, 便于模拟真实的物理效果(为了保证更逼真的效果, 最好是设置环境贴图, 模拟物体对环境光的反射/折射)

1) 基础材质

对于基础材质MeshBasicMaterial我们主要使用的属性

  • color: 定义表面的颜色
  • wireframe: 是否显示线框

完整示例:

import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import * as dat from 'dat.gui'
// 一. 创建场景
const scene = new THREE.Scene()
// 二. 创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight
)
camera.position.set(0, 0, 5)
// 三. 创建物体
const cubeGeometry = new THREE.BoxGeometry(2, 2, 2)
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 })
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
scene.add(cube)
// 集成Gui
const gui = new dat.GUI()
const data = {
  color: 0xff0000,
  wireframe: false,
}
gui.addColor(data, 'color').onChange((value) => {
console.log(cube.material)
  cube.material.color.set(value)
})
gui.add(data, 'wireframe').onChange((value) => {
  cube.material.wireframe = value
})
// 四. 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setAnimationLoop(animation)
// 将渲染的canvas添加到body元素中
document.body.appendChild(renderer.domElement)
// 五. 辅助工具
const control = new OrbitControls(camera, renderer.domElement)
const axesHelper = new THREE.AxesHelper(10)
scene.add(axesHelper)
const gridHelper = new THREE.GridHelper(20, 20, 0xffffff, 0xffffff)
gridHelper.material.transparent = true
gridHelper.material.opacity = 0.5
scene.add(gridHelper)
function animation() {
  renderer.render(scene, camera)
}
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth, window.innerHeight)
})

color对象的注意事项color对象的rgb范围为[0,1]的浮点数, 而不是[0, 255]的整数在源码中做了归一化处理, 就是数学里的比例换算再映射到sRGB色彩空间。

setHex( hex, colorSpace = SRGBColorSpace ) {
  hex = Math.floor( hex );
// 归一化处理
this.r = ( hex >> 16 & 255 ) / 255;
this.g = ( hex >> 8 & 255 ) / 255;
this.b = ( hex & 255 ) / 255;
// 默认映射到SRGB色彩空间
  ColorManagement.toWorkingColorSpace( this, colorSpace );
return this;
}

改造MeshGui支持材质属性调整

import * as THREE from 'three'
import { BaseGui } from './BaseGui'
function defaultHandler(item, key, value) {
  item[key] = value
}
const Vector3Config = {
  x: {
    handler: defaultHandler,
  },
  y: {
    handler: defaultHandler,
  },
  z: {
    handler: defaultHandler,
  },
}
function eulerHandler(item, key, value) {
  item[key] = THREE.MathUtils.degToRad(value)
}
const EulerConfig = {
  x: {
    extend: [-180, 180],
    handler: eulerHandler,
  },
  y: {
    extend: [-180, 180],
    handler: eulerHandler,
  },
  z: {
    extend: [-180, 180],
    handler: eulerHandler,
  },
}
function geometryHandler(item, key, value, parent) {
const params = { ...parent.geometry.parameters }
  params[key] = value
  parent.geometry.dispose()
  parent.geometry = new THREE[parent.geometry.type](...Object.values(params))
}
const GeometryMapping = {
  BoxGeometry: {
    width: {
      name: 'x轴宽度',
      extend: [2, 20],
      handler: geometryHandler,
    },
    height: {
      name: 'y轴高度',
      extend: [2, 20],
      handler: geometryHandler,
    },
    depth: {
      name: 'z轴深度',
      extend: [2, 20],
      handler: geometryHandler,
    },
  },
  SphereGeometry: {
    radius: {
      name: '半径',
      min: 1,
      handler: geometryHandler,
    },
    widthSegments: {
      name: '水平分段数',
      min: 3,
      handler: geometryHandler,
    },
    heightSegments: {
      name: '垂直分段数',
      min: 2,
      handler: geometryHandler,
    },
  },
  PlaneGeometry: {
    width: {
      name: 'x轴宽度',
      min: 1,
      handler: geometryHandler,
    },
    height: {
      name: 'y轴高度',
      min: 1,
      handler: geometryHandler,
    },
  },
}
function colorHandler(item, key, value) {
  item[key].set(value)
}
const MaterialMapping = {
  MeshBasicMaterial: {
    color: {
      method: 'addColor',
      handler: colorHandler,
    },
    wireframe: {
      handler: defaultHandler,
    },
  },
}
export class MeshGui extends BaseGui {
constructor(options = {}) {
if (!options.target.isMesh) {
      console.error('target must be an instance of Mesh')
return
    }
super()
this.init(options)
  }
init(options) {
this.mesh = options.target
this.geometry = this.mesh.geometry
this.material = this.mesh.material
this.position = this.mesh.position
this.rotation = this.mesh.rotation
this.scale = this.mesh.scale
    options.position !== false ? this.initPosition() : ''
    options.rotation !== false ? this.initRotation() : ''
    options.scale !== false ? this.initScale() : ''
    options.geometry !== false ? this.initGeometry() : ''
    options.material !== false ? this.initMaterial() : ''
  }
  initPosition() {
    console.log(this.position)
this.initGuiFolder(this.position, '位置', this.mesh, Vector3Config)
  }
  initRotation() {
this.initGuiFolder(this.rotation, '旋转(度)', this.mesh, EulerConfig)
  }
  initScale() {
this.initGuiFolder(this.scale, '缩放', this.mesh, Vector3Config)
  }
  initGeometry() {
const geometry = this.geometry
const type = geometry.type
const config = GeometryMapping[type]
if (config) {
this.initGuiFolder(geometry.parameters, geometry.type, this.mesh, config)
    }
  }
  initMaterial() {
const material = this.material
const type = this.material.type
const config = MaterialMapping[type]
if (config) {
this.initGuiFolder(material, type, this.mesh, config)
    }
  }
}

2) 法向材质

MeshNormalMaterial(法向材质)的颜色由当前点的法向量(三维坐标)做为rgb值法向量法向量(Normal vector)是指垂直于某一对象表面上的一条向量在三维几何中,一个平面可以由一个点和一个法向量唯一确定法向量在计算物体表面的光照、碰撞检测以及物体位置的计算等方面很有用

three.js中, 法向量是一个Vector3(三维矢量对象), 有xyz三个属性. 取值范围[0, 1].正好可以做为颜色的rgb

import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { MeshGui } from '../gui'
// 一. 创建场景
const scene = new THREE.Scene()
// 二. 创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight
)
camera.position.set(0, 0, 5)
// 三. 创建物体
const cubeGeometry = new THREE.BoxGeometry(2, 2, 2)
const cubeMaterial = new THREE.MeshNormalMaterial()
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
scene.add(cube)
new MeshGui({
  target: cube,
})
// 四. 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setAnimationLoop(animation)
// 将渲染的canvas添加到body元素中
document.body.appendChild(renderer.domElement)
// 五. 辅助工具
const control = new OrbitControls(camera, renderer.domElement)
const axesHelper = new THREE.AxesHelper(10)
scene.add(axesHelper)
const gridHelper = new THREE.GridHelper(20, 20, 0xffffff, 0xffffff)
gridHelper.material.transparent = true
gridHelper.material.opacity = 0.5
scene.add(gridHelper)
function animation() {
  renderer.render(scene, camera)
}
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth, window.innerHeight)
})

3) PBR材质

PBR材质, 也叫物理材质, 用于模拟光照到不同的物体上发生反射, 产生更逼真的显示效果比较著名的物理模型有

  • Lambert模型: 主要模拟光线漫反射, 通常用于模拟木材/石材等表面粗糙的材质
  • Phong模型: 主要模拟光线镜面反射, 通常用于模拟金属/镜子等表面光滑的材质

效果:

树的顶部用圆锥体模拟示例:

// 1. 创建一个Lambert材质的圆锥体
const coneGeometry = new THREE.ConeGeometry(2, 10, 32)
const coneMaterial = new THREE.MeshLambertMaterial({ color: 0x00ffff })
const cone = new THREE.Mesh(coneGeometry, coneMaterial)
cone.position.y = 5

此时, 我们发现渲染的是一个黑色的圆锥体, 是因为没有添加光源的原因.这里我们分别使用两种不同的光源测试对比

  1. 环境光, 整个场景都会照亮, 不会形成阴影
  2. 点光源, 光从一个点向四周辐射, 向光面被照亮, 背光面会形成阴影

环境光

// 添加环境灯光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
scene.add(ambientLight)

点光源

// 添加点光源
const pointLight = new THREE.PointLight(0xffffff, 1)
pointLight.position.set(10, 10, 10)
scene.add(pointLight)

测试效果

树干可以用一个圆柱体模拟示例

// 1. 创建一个Lambert材质的圆锥体
const coneGeometry = new THREE.ConeGeometry(3, 5, 32)
const coneMaterial = new THREE.MeshLambertMaterial({ color: 0x00ffff })
const cone = new THREE.Mesh(coneGeometry, coneMaterial)
cone.position.y = 5

// 2. 创建一个Phone材质的圆柱体
const cylinderGeometry = new THREE.CylinderGeometry(1, 1, 5, 32, 2)
const cylinderMaterial = new THREE.MeshPhongMaterial({ color: 0x00ffff })
const cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial)

// 创建一个组
const group = new THREE.Group()
// 添加到组中
group.add(cone)
group.add(cylinder)

建模后, 可以添加到一个组里, 方便统一管理

完整示例:

import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'

import { MeshGui } from '../gui'

// 一. 创建场景
const scene = new THREE.Scene()

// 二. 创建相机
const camera = new THREE.PerspectiveCamera(
  45,
  window.innerWidth / window.innerHeight
)
camera.position.set(0, 0, 5)

// 三. 创建物体
// 1. 创建一个Lambert材质的圆锥体
const coneGeometry = new THREE.ConeGeometry(3, 5, 32)
const coneMaterial = new THREE.MeshLambertMaterial({ color: 0x00ffff })
const cone = new THREE.Mesh(coneGeometry, coneMaterial)
cone.position.y = 5

// 2. 创建一个Phone材质的圆柱体
const cylinderGeometry = new THREE.CylinderGeometry(1, 1, 5, 32, 2)
const cylinderMaterial = new THREE.MeshPhongMaterial({ color: 0x00ffff })
const cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial)

// 创建一个组
const group = new THREE.Group()
// 添加到组中
group.add(cone)
group.add(cylinder)

scene.add(group)

// 添加点光源
const pointLight = new THREE.PointLight(0xffffff, 1)
pointLight.position.set(10, 10, 10)
scene.add(pointLight)

// 四. 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setAnimationLoop(animation)
// 将渲染的canvas添加到body元素中
document.body.appendChild(renderer.domElement)

// 五. 辅助工具
const control = new OrbitControls(camera, renderer.domElement)

const axesHelper = new THREE.AxesHelper(10)
scene.add(axesHelper)

const gridHelper = new THREE.GridHelper(20, 20, 0xffffff, 0xffffff)
gridHelper.material.transparent = true
gridHelper.material.opacity = 0.5
scene.add(gridHelper)

function animation() {
  renderer.render(scene, camera)
}

window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()

  renderer.setSize(window.innerWidth, window.innerHeight)
})

戳下方卡片,即可免fei获取7天课程和视频教程

GIS资料免费领​www.wjx.cn/vm/Qm8Ful2.aspx​编辑

三维GIS开发特训营,2404期即将开班↓↓


我们不仅提供优质课程,助你学习更进一步!新中地学生threejs作品:

免fei领视频版教程及源码戳↓↓

GIS资料免费领icon-default.png?t=N7T8https://www.wjx.cn/vm/Qm8Ful2.aspx

标签:const,入门教程,THREE,Three,第七篇,window,renderer,new,scene
From: https://blog.csdn.net/2401_84715637/article/details/141320635

相关文章

  • CTF入门教程(非常详细)从零基础入门到竞赛,看这一篇就够了!
     一、CTF简介CTF(CaptureTheFlag)中文一般译作夺旗赛,在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会,以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。发展至今,已经成为全球范围网络安全圈流行的竞赛......
  • CTF入门教程(非常详细)从零基础入门到竞赛,看这一篇就够了!
     一、CTF简介CTF(CaptureTheFlag)中文一般译作夺旗赛,在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会,以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。发展至今,已经成为全球范围网络安全圈流行的竞赛......
  • CTF入门教程(非常详细)从零基础入门到竞赛,看这一篇就够了!
     一、CTF简介CTF(CaptureTheFlag)中文一般译作夺旗赛,在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会,以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。发展至今,已经成为全球范围网络安全圈流行的竞赛......
  • CTF入门教程(非常详细)从零基础入门到竞赛,看这一篇就够了!
     一、CTF简介CTF(CaptureTheFlag)中文一般译作夺旗赛,在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会,以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。发展至今,已经成为全球范围网络安全圈流行的竞赛......
  • CTF入门教程(非常详细)从零基础入门到竞赛,看这一篇就够了!
     一、CTF简介CTF(CaptureTheFlag)中文一般译作夺旗赛,在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会,以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。发展至今,已经成为全球范围网络安全圈流行的竞赛......
  • 数据结构之 红黑树入门教程、红黑树代码示例
    红黑树(Red-BlackTree)是一种自平衡的二叉查找树(BST),它在插入、删除和查找操作后通过一些特定的规则来维护树的平衡,从而确保这些操作的时间复杂度始终为O(logn)。红黑树主要应用在需要高效动态集合操作的场景中,如操作系统中的进程调度器、数据库中的索引等。红黑树的基本性......
  • Docker 入门教程
    本文是官方GettingStarts教程的阅读笔记,包含对步骤、命令的记录和解释。教程分一系列课程,包括有:安装Docker运行容器和创建自定义容器创建高效可复用的镜像,并推送到DockerHub上GetDockerDesktopDockerDesktop是简单易用的Docker工具软件,使用DockerDesktop可......
  • python入门教程(非常详细!3w+ 文字)
    先序:学习编程语言要先学个轮廓,刚开始只用学核心的部分,一些细节、不常用的内容先放着,现用现查即可;把常用的东西弄熟练了在慢慢补充。1、安装Python解释器为什么需要安装PythonPython语言本身是由解释器执行的,因此你需要在你的计算机上安装Python解释器。这个解释器会将......
  • java 入门教程(非常详细!1.6w+ 文字)
    先序:学习编程语言要先学个轮廓,刚开始只用学核心的部分,一些细节、不常用的内容先放着,现用现查即可;把常用的东西弄熟练了在慢慢补充。1.Java概述Java是一种面向对象的编程语言,由SunMicrosystems(现在的Oracle)在1995年推出。Java程序可以在任何支持Java虚拟机(JVM)的......
  • python入门教程(非常详细!3w+ 文字)
    先序:学习编程语言要先学个轮廓,刚开始只用学核心的部分,一些细节、不常用的内容先放着,现用现查即可;把常用的东西弄熟练了在慢慢补充。1、安装Python解释器为什么需要安装PythonPython语言本身是由解释器执行的,因此你需要在你的计算机上安装Python解释器。这个解释......