3.js - 漫天孔明灯(使用OrbitControls 锁定相机镜头)

main.js

javascript 复制代码
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import gsap from 'gsap'


// @ts-ignore
import vertexShader from './shader/11-03/vertex.glsl?raw'
// @ts-ignore
import fragmentShader from './shader/11-03/fragment.glsl?raw'


// ------------------------------------------------------ 	------------------------------------------------
	const scene = new THREE.Scene()
	
	const camera = new THREE.PerspectiveCamera(90, window.innerHeight / window.innerHeight, 0.1, 1000)
	camera.position.set(0, 0, 2)
	camera.aspect = window.innerWidth / window.innerHeight
	scene.add(camera)
	
	const axesHelper = new THREE.AxesHelper(5)
	scene.add(axesHelper)
// ------------------------------------------------------------------------------------------------------



`【loadAsync:异步加载hdr,返回一个Promise】`
const rgbeLoader = new RGBELoader()
rgbeLoader.loadAsync('../public/assets/2k.hdr').then(texture => {
	texture.mapping = THREE.EquirectangularReflectionMapping
	scene.background = texture
	scene.environment = texture
})

`【着色器材质】`
const shaderMaterial = new THREE.ShaderMaterial({
	vertexShader: vertexShader,
	fragmentShader: fragmentShader,
	side: THREE.DoubleSide,
	// transparent: true,
})

`【加载灯笼的glb】`
const gltfLoader = new GLTFLoader()
gltfLoader.load('../public/assets/model/flyLight.glb', gltf => {
	console.log('gltf=', gltf)
	
	for (let i = 0; i < 150; i++) {
		let flyLight = gltf.scene.clone(true)
		// console.log('flyLight=', flyLight)
		flyLight.children[1].material = shaderMaterial
	
		let x = (Math.random() - 0.5) * 300
		let z = (Math.random() - 0.5) * 300
		let y = Math.random() * 60 + 25
		flyLight.position.set(x, y, z)
	
		gsap.to(flyLight.rotation, {
			y: 2 * Math.PI, // 绕着y轴旋转360°
			duration: 10 + Math.random() * 30, // 持续时间
			repeat: -1 // gsap中,-1代表,无限次循环
		})
		
		gsap.to(flyLight.position, {
			x: '+=' + Math.random() * 5, // x坐标,增加一个在0到5之间的随机值
			y: '+=' + Math.random() * 20, // y坐标,增加一个在0到20之间的随机值
			duration: 5 + Math.random() * 10,
			yoyo: true, // 动画在每次重复时都将反向进行,即:从 a -> b 到 b -> a
			repeat: -1
		})
	
		scene.add(flyLight)
	}
})


// ------------------------------------------------------ 	------------------------------------------------

`【渲染器】`
	const renderer = new THREE.WebGLRenderer({ alpha: true })
	renderer.shadowMap.enabled = true	
	【最新版本属性名字有改变】:`renderer.outputEncoding`已经变更为`renderer.outputColorSpace`
	// renderer.outputEncoding = THREE.sRGBEncoding // 设置渲染器的输出编码方式(此API已弃用)
	renderer.outputColorSpace = THREE.SRGBColorSpace // 设置渲染器的输出编码方式(此API已启用)
	renderer.toneMapping = THREE.ACESFilmicToneMapping // 色调映射
	renderer.toneMappingExposure = 0.2 // 色调映射-曝光度
	renderer.setSize(window.innerWidth, window.innerHeight)
	document.body.appendChild(renderer.domElement)

`【轨道控制器】`
	const controls = new OrbitControls(camera, renderer.domElement)
	controls.enableDamping = true
	controls.autoRotate = true // 相机绕着y轴,顺时针自动旋转
	controls.autoRotateSpeed = 0.1 // 自动旋转的速度
	
	`最大极角 == 最小极角:代表,相机会被锁定在这个角度上,无法上下移动(锁定相机镜头)`
	controls.maxPolarAngle = (Math.PI / 3) * 2
	controls.minPolarAngle = (Math.PI / 3) * 2
	// controls.maxPolarAngle = (Math.PI / 2) * 0.95 // 稍微小于90度
	// controls.minPolarAngle = Math.PI / 4 // 45度


	function animate() {
		controls.update()
		requestAnimationFrame(animate)
		renderer.render(scene, camera)
	}
	animate()
	
	window.addEventListener('resize', () => {
		camera.aspect = window.innerWidth / window.innerHeight
		camera.updateProjectionMatrix()
		renderer.setSize(window.innerWidth, window.innerHeight)
		renderer.setPixelRatio(window.devicePixelRatio)
	})

vertex.glsl

javascript 复制代码
precision lowp float;

varying vec4 vPosition;
varying vec4 gPosition;

void main() {
    vec4 modelPosition = modelMatrix*vec4(position, 1.0);

    vPosition = modelPosition;
    
    gPosition = vec4(position, 1.0);

    gl_Position = projectionMatrix*viewMatrix*modelPosition;
}

fragment.glsl

javascript 复制代码
precision lowp float;

varying vec4 vPosition;
varying vec4 gPosition;

void main() {
    vec4 redColor = vec4(1, 0, 0, 1);
    vec4 yellowColor = vec4(1, 1, 0.5, 1);
    vec4 mixColor = mix(yellowColor, redColor, gPosition.y/3.0);

    /*
        gl_FrontFacing:
            OpenGL和WebGL中,片段着色器中的内置变量,
            用于指示当前处理的片段是否属于一个正面朝向的三角形,
        
        OpenGL和WebGL的渲染管线中,三角形可以被定义为,正面或背面,
            这取决于,它们的顶点顺序与当前设置的前脸/后脸判断标准(通常是基于顶点的绕序,如顺时针或逆时针)是否一致。
    */
    if(gl_FrontFacing) {
        // 如果,片段属于正面朝向的三角形
        gl_FragColor = vec4(mixColor.xyz-(vPosition.y-20.0)/80.0-0.1, 1);
    } else {
        gl_FragColor = vec4(mixColor.xyz, 1);
    }

    // 这个是我测试用的
    // gl_FragColor = vec4(1, 0, 0, 1);
}
相关推荐
一颗烂土豆2 天前
🚴‍♂️ Vue3 + Three.js 实战:如何写一个“不晕车”的沉浸式骑行播放器 🎥
vue.js·游戏·three.js
Elaine3363 天前
Gemini生成的3D交互圣诞树(娱乐版)
3d·交互·three.js·前端可视化
Awu12274 天前
Vue3自定义渲染器:原理剖析与实践指南
前端·vue.js·three.js
龙猫不热4 天前
THREE.js 关于Material基类下的depthTest 和 depthWrite的理解
前端·three.js
阿里巴啦8 天前
用React+Three.js 做 3D Web版搭建三维交互场景:模型的可视化摆放与轻量交互
前端·react·three.js·模型可视化·web三维·web三维交互场景
阿里巴啦10 天前
React + Three.js + R3F + Vite 实战:可交互的三维粒子化展厅
react.js·three.js·粒子化·drei·postprocessing·三维粒子化
叫我詹躲躲10 天前
基于 Three.js 的 3D 地图可视化:核心原理与实现步骤
前端·three.js
map_3d_vis11 天前
JSAPIThree 加载单体三维模型学习笔记:SimpleModel 简易加载方式
学习笔记·three.js·gltf·glb·初学者·三维模型·mapvthree·jsapithree·simplemodel
Addisonx14 天前
深度复盘 III: 核心逻辑篇:构建 WebGL 数字孪生的“业务中枢”与“安全防线”
webgl·three.js
爱看书的小沐14 天前
【小沐学WebGIS】基于Three.JS绘制二三维地图地球晨昏效果(WebGL / vue / react )
javascript·vue.js·gis·webgl·three.js·opengl·晨昏线