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 ] . pointthis . 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>