ThreeJS 官方案例学习(webgl_decals)
1.效果图
2.源码
< template>
< div>
< div id= "container" > < / div>
< / div>
< / template>
< script>
import * as THREE from 'three' ;
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' ;
import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js' ;
import Stats from 'three/examples/jsm/libs/stats.module.js' ;
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
import { DecalGeometry } from 'three/examples/jsm/geometries/DecalGeometry.js' ;
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js' ;
import gsap from 'gsap' ;
export default {
data ( ) {
return {
container : null ,
scene : null ,
camera : null ,
renderer : null ,
controller : null ,
stats : null ,
mixer : null ,
mesh : null ,
gui : null ,
clock : new THREE. Clock ( ) ,
raycaster : new THREE. Raycaster ( ) ,
params : null ,
intersection : {
intersects : false ,
point : new THREE. Vector3 ( ) ,
normal : new THREE. Vector3 ( )
} ,
mouse : new THREE. Vector2 ( ) ,
intersects : [ ] ,
mouseHelper : null ,
line : null ,
position : new THREE. Vector3 ( ) ,
orientation : new THREE. Euler ( ) ,
size : new THREE. Vector3 ( 10 , 10 , 10 ) ,
decals : [ ] ,
} ;
} ,
mounted ( ) {
this . params = {
minScale : 10 ,
maxScale : 20 ,
rotate : true ,
clear : ( ) => {
this . removeDecals ( ) ;
}
}
this . init ( )
this . animate ( )
window. addEventListener ( "resize" , this . onWindowSize)
let moved = false ;
this . controller. addEventListener ( 'change' , ( ) => { moved = true ; } ) ;
window. addEventListener ( "pointerdown" , ( ) => { moved = false } )
window. addEventListener ( "pointerup" , ( event ) => {
if ( moved === false ) {
this . checkIntersection ( event. clientX, event. clientY)
if ( this . intersection. intersects) this . shoot ( )
}
} )
window. addEventListener ( 'pointermove' , this . onPointerMove) ;
} ,
beforeUnmount ( ) {
console. log ( 'beforeUnmount===============' ) ;
this . container = null
this . scene = null
this . camera = null
this . renderer = null
this . controller = null
this . stats = null
this . mixer = null
this . model = null
} ,
methods : {
init ( ) {
this . container = document. getElementById ( 'container' )
this . setScene ( )
this . setCamera ( )
this . setRenderer ( )
this . setController ( )
this . addHelper ( )
this . setLight ( )
this . setGltfLoader ( )
this . addStatus ( )
} ,
setScene ( ) {
this . scene = new THREE. Scene ( )
} ,
setCamera ( ) {
this . camera = new THREE. PerspectiveCamera ( 60 , this . container. clientWidth / this . container. clientHeight, 1 , 1000 )
this . camera. position. z = 120
this . camera. aspect = this . container. clientWidth / this . container. clientHeight;
this . camera. updateProjectionMatrix ( ) ;
this . camera. lookAt ( new THREE. Vector3 ( 0 , 0 , 0 ) )
this . scene. add ( this . camera)
} ,
setRenderer ( ) {
this . renderer = new THREE. WebGLRenderer ( {
antialias : true ,
} )
this . renderer. setSize ( this . container. clientWidth, this . container. clientHeight) ;
this . renderer. setPixelRatio ( window. devicePixelRatio) ;
this . renderer. sortObjects = false ;
this . container. appendChild ( this . renderer. domElement) ;
} ,
setController ( ) {
this . controller = new OrbitControls ( this . camera, this . renderer. domElement) ;
this . controller. minDistance = 50 ;
this . controller. maxDistance = 200 ;
} ,
addHelper ( ) {
let helper = new THREE. CameraHelper ( this . camera) ;
let axisHelper = new THREE. AxesHelper ( 150 ) ;
let gridHelper = new THREE. GridHelper ( 100 , 30 , 0x2C2C2C , 0x888888 ) ;
this . mouseHelper = new THREE. Mesh ( new THREE. BoxGeometry ( 1 , 1 , 10 ) , new THREE. MeshNormalMaterial ( ) ) ;
this . mouseHelper. visible = false ;
this . scene. add ( this . mouseHelper) ;
} ,
setPMREMGenerator ( ) {
const pmremGenerator = new THREE. PMREMGenerator ( this . renderer) ;
this . scene. environment = pmremGenerator. fromScene ( new RoomEnvironment ( this . renderer) , 0.04 ) . texture;
} ,
setLight ( ) {
const ambientLight = new THREE. AmbientLight ( 0x666666 ) ;
this . scene. add ( ambientLight) ;
const dirLight1 = new THREE. DirectionalLight ( 0xffddcc , 3 ) ;
dirLight1. position. set ( 1 , 0.75 , 0.5 ) ;
this . scene. add ( dirLight1) ;
const dirLight2 = new THREE. DirectionalLight ( 0xccccff , 3 ) ;
dirLight2. position. set ( - 1 , 0.75 , - 0.5 ) ;
this . scene. add ( dirLight2) ;
} ,
addStatus ( ) {
this . stats = new Stats ( ) ;
this . container. appendChild ( this . stats. dom) ;
} ,
setGltfLoader ( ) {
let that = this
const loader = new GLTFLoader ( ) ;
const dracoLoader = new DRACOLoader ( ) ;
dracoLoader. setDecoderPath ( "/draco/gltf/" ) ;
loader. setDRACOLoader ( dracoLoader) ;
const geometry = new THREE. BufferGeometry ( ) ;
geometry. setFromPoints ( [ new THREE. Vector3 ( ) , new THREE. Vector3 ( ) ] ) ;
this . line = new THREE. Line ( geometry, new THREE. LineBasicMaterial ( ) ) ;
this . scene. add ( this . line) ;
const textureLoader = new THREE. TextureLoader ( )
const map = textureLoader. load ( require ( "../../../public/model/gltf/LeePerrySmith/Map-COL.jpg" ) ) ;
map. colorSpace = THREE . SRGBColorSpace;
const specularMap = textureLoader. load ( '../../../public/models/gltf/LeePerrySmith/Map-SPEC.jpg' ) ;
const normalMap = textureLoader. load ( '../../../public/models/gltf/LeePerrySmith/Infinite-Level_02_Tangent_SmoothUV.jpg' ) ;
loader. load ( '/model/gltf/LeePerrySmith/LeePerrySmith.glb' , ( gltf ) => {
that. mesh = gltf. scene. children[ 0 ] ;
that. mesh. material = new THREE. MeshPhongMaterial ( {
specular : 0x111111 ,
map : map,
specularMap : specularMap,
shininess : 25 ,
} ) ;
that. scene. add ( that. mesh)
that. mesh. scale. set ( 10 , 10 , 10 )
} , undefined , ( err => {
console. error ( err)
} ) )
const gui = new GUI ( ) ;
gui. add ( this . params, 'minScale' , 1 , 30 ) ;
gui. add ( this . params, 'maxScale' , 1 , 30 ) ;
gui. add ( this . params, 'rotate' ) ;
gui. add ( this . params, 'clear' ) ;
gui. open ( ) ;
} ,
onPointerMove ( event ) {
if ( event. isPrimary) {
this . checkIntersection ( event. clientX, event. clientY) ;
}
} ,
checkIntersection ( x, y ) {
if ( this . mesh === undefined ) return
this . mouse. x = ( x / window. innerWidth) * 2 - 1 ;
this . mouse. y = - ( y / window. innerHeight) * 2 + 1 ;
this . raycaster. setFromCamera ( this . mouse, this . camera) ;
this . raycaster. intersectObject ( this . mesh, false , this . intersects) ;
if ( this . intersects. length > 0 ) {
const p = this . intersects[ 0 ] . point
this . mouseHelper. position. copy ( p) ;
this . intersection. point. copy ( p) ;
const n = this . intersects[ 0 ] . face. normal. clone ( ) ;
n. transformDirection ( this . mesh. matrixWorld) ;
n. multiplyScalar ( 10 ) ;
n. add ( this . intersects[ 0 ] . point) ;
this . intersection. normal. copy ( this . intersects[ 0 ] . face. normal) ;
this . mouseHelper. lookAt ( n) ;
const positions = this . line. geometry. attributes. position;
positions. setXYZ ( 0 , p. x, p. y, p. z) ;
positions. setXYZ ( 1 , n. x, n. y, n. z) ;
positions. needsUpdate = true ;
this . intersection. intersects = true ;
this . intersects. length = 0 ;
} else {
this . intersection. intersects = false ;
}
} ,
shoot ( ) {
this . position. copy ( this . intersection. point) ;
this . orientation. copy ( this . mouseHelper. rotation) ;
if ( this . params. rotate) this . orientation. z = Math. random ( ) * 2 * Math. PI ;
const scale = this . params. minScale + Math. random ( ) * ( this . params. maxScale - this . params. minScale) ;
this . size. set ( scale, scale, scale) ;
const textureLoader = new THREE. TextureLoader ( )
const decalDiffuse = textureLoader. load ( '/textures/decal/decal-diffuse.png' ) ;
decalDiffuse. colorSpace = THREE . SRGBColorSpace;
const decalNormal = textureLoader. load ( '/textures/decal/decal-normal.jpg' ) ;
const decalMaterial = new THREE. MeshPhongMaterial ( {
specular : 0x444444 ,
map : decalDiffuse,
normalMap : decalNormal,
normalScale : new THREE. Vector2 ( 1 , 1 ) ,
shininess : 30 ,
transparent : true ,
depthTest : true ,
depthWrite : false ,
polygonOffset : true ,
polygonOffsetFactor : - 4 ,
wireframe : false ,
} ) ;
const material = decalMaterial. clone ( ) ;
material. color. setHex ( Math. random ( ) * 0xffffff ) ;
const m = new THREE. Mesh ( new DecalGeometry ( this . mesh, this . position, this . orientation, this . size) , material) ;
this . decals. push ( m) ;
this . scene. add ( m) ;
} ,
removeDecals ( ) {
this . decals. forEach ( ( d ) => {
this . scene. remove ( d) ;
} ) ;
this . decals. length = 0 ;
} ,
onWindowSize ( ) {
this . camera. aspect = this . container. clientWidth / this . container. clientHeight;
this . camera. updateProjectionMatrix ( ) ;
this . renderer. setSize ( this . container. clientWidth, this . container. clientHeight) ;
this . renderer. setPixelRatio ( window. devicePixelRatio)
} ,
animate ( ) {
const delta = this . clock. getDelta ( ) ;
requestAnimationFrame ( this . animate) ;
this . controller. update ( delta) ;
this . stats. update ( ) ;
this . renderer. render ( this . scene, this . camera) ;
} ,
} ,
} ;
< / script>
< style>
#container {
position : absolute;
width : 100 % ;
height : 100 % ;
}
< / style>