likes
comments
collection
share

没有前端能抵抗住的三维效果之————个人three模版

作者站长头像
站长
· 阅读数 17

没有前端能抵抗住的三维效果之———个人three模版

没有前端能抵抗住的三维效果之————个人three模版

前言——使用vite + vue3 + ts+ three.js 写一个自己常用的three模版,加一点点介绍;节后陆续更新点three demo ,您们的点赞、评论是我不断向前的动力

1、创建项目

npm create vite@latest
......

2、安装three 依赖

pnpm i three

pnpm i --save-dev @types/three  

pnpm i vite-plugin-glsl

依赖信息

  "devDependencies": {
    "@types/three": "^0.164.0",
    "@vitejs/plugin-vue": "^5.0.4",
    "typescript": "^5.2.2",
    "vite": "^5.2.0",
    "vue-tsc": "^2.0.6"
  }

3、配置glsl插件,修改vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import glsl from 'vite-plugin-glsl';
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    glsl({
      include: [
        '**/*.glsl',
        '**/*.wgsl',
        '**/*.vert',
        '**/*.frag',
        '**/*.vs',
        '**/*.fs',
      ],
      exclude: undefined,
      warnDuplicatedImports: true,
      defaultExtension: 'glsl',
      compress: false,
      root: '/',
    })
  ],
})

4、引入依赖

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'

5、声明scene

let scene = new THREE.Scene()

6、声明渲染器

const renderer = new THREE.WebGLRenderer({
  antialias: true, // 抗锯齿
})
renderer.setClearColor(0x000000)
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)

7、声明渲染方法

const render = (): void => {
  renderer.render(scene, camera)
}
const animate = (): void => {
  requestAnimationFrame(animate)
  render()
}
animate()

8、在场景中添加环境光、平行光

const createLight = () => {
  // 设置环境光
  const ambientLight = new THREE.AmbientLight(0xffffff) // 环境光
  scene.add(ambientLight)
  // 平行光
  const directionalLight = new THREE.DirectionalLight(0x00ff00, 1)
  // 设置光源的方向
  directionalLight.position.set(50, 50, 50)
  scene.add(directionalLight)
}
createLight()

9、添加一个立方体看看效果

const createMesh = () => {
  const geometry = new THREE.BoxGeometry(1, 1, 1)
  const material = new THREE.MeshStandardMaterial()
  return new THREE.Mesh(geometry, material)
}
const mesh = createMesh()
scene.add(mesh)
// 顺便让他旋转起来
// 在动画方法中 给立方体在x y轴上添加旋转值 每次渲染都会旋转一定值
const animate = (): void => {
  requestAnimationFrame(animate)
  mesh.rotation.x += 0.01 // 新增
  mesh.rotation.y += 0.01 // 新增
  render()
}
没有前端能抵抗住的三维效果之————个人three模版

10、添加个两个模型进来

// 添加经典猴子头模型
// 提前设置shader材质
const material = new THREE.ShaderMaterial({
  fragmentShader: fragmentShader,
  vertexShader: vertexShader,
  uniforms: {
    uTime: { value: 0 },
  },
  transparent: true,
})
// 直接使用load方法加载方式
const loader = new THREE.BufferGeometryLoader()
loader.load('models/json/suzanne_buffergeometry.json', (geometry) => {
  geometry.computeVertexNormals()
  geometry.scale(0.5, 0.5, 0.5) // 控制模型比例——直接赋值
  const mesh = new THREE.Mesh(geometry, material)
  mesh.position.x = -2 // 设置模型位置——直接赋值
  scene.add(mesh)
})
// 添加经典机器人
const loader2 = new GLTFLoader()
loader2.load(
  'models/gltf/RobotExpressive.glb',
  (gltf) => {
    gltf.scene.position.set(2, -0.5, 0)  // 设置模型位置——set方法
    gltf.scene.scale.set(0.2, 0.2, 0.2) // 控制模型比例——set方法
    scene.add(gltf.scene)
  },
  undefined,
   (e)=> {
    console.error(e)
  }
)

没有前端能抵抗住的三维效果之————个人three模版

12、引入glsl并解决引入glsl文件提示错误

import vertexShader from '../glsl/demo/main.vert'
import fragmentShader from '../glsl/demo/main.frag'
// 片元着色器
varying vec2 vUv;
uniform float uTime;
varying vec3 vNormal;

void main(){
    // 使用法向量简单上个色
    gl_FragColor=vec4(vec3(vNormal),1.);
    #include <tonemapping_fragment>
    #include <colorspace_fragment>
}
// 顶点着色器
uniform float uTime;
varying vec3 vNormal;
void main() {
	vec4 mPosition = modelMatrix * vec4(position, 1.0);
	mPosition.y += sin(uTime/2.)/2.; // 移动模型位置
	gl_Position =projectionMatrix *  viewMatrix *  mPosition;
	vNormal = normal;
}
// 找不到模块“../glsl/demo/main.vert”或其相应的类型声明
// 在vite-env.d.ts文件中添加如下代码
declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  const componnet: DefineComponent<{}, {}, any>
  export default componnet
}

declare module '*.vert' {
  const value: string
  export default value
}
declare module '*.frag' {
  const value: string
  export default value
}

13、再用平面创建一个好看背景

// 使用promise 加载素材方式
const addPlaneMesh = async () => {
  const loadTexture = (path: string) => {
    return new Promise((resolve) => {
      new THREE.TextureLoader().load(path, (texture) => {
        resolve(texture)
      })
    })
  }
  let texture = await loadTexture('/img/wp2698514.png') // 刀斯林无处不在!
  const geometry = new THREE.PlaneGeometry(16, 9)
  const material = new THREE.MeshLambertMaterial({
    // 设置纹理贴图:Texture对象作为材质map属性的属性值
    map: texture as THREE.Texture
  })
  const planeMesh = new THREE.Mesh(geometry, material)
  planeMesh.position.z = -3
  scene.add(planeMesh)
}
addPlaneMesh()
// TODO: 后续再实现一个load管理器

没有前端能抵抗住的三维效果之————个人three模版

14、配置onBeforeUnmount 刷新时避免重复添加

onBeforeUnmount(() => {
  // destroyThree() // TODO: 后面再补
  document.body.removeChild(renderer.domElement) // 避免热更新时创建新的canvas
})

结尾

代码地址 xzw199509/xiao-three-template (github.com) 内容如有不足之处多多提意见 ,我努力改

转载自:https://juejin.cn/post/7362547971060400166
评论
请登录