Use vue to learn three.js to load advanced geometry-load PDB format model

1.demo effect

Insert picture description here

As shown in the figure above, the demo loads the PDB format model through PDBLoader, and presents the molecular structure model on the page

2. Implementation points

2.1 PDB model placement path

When loading files in vue, the default path is public, so the files that need to be loaded are placed in this path, and the variable publicPath is created in the data attribute of vue. The value of this variable is the environment variable process.env.BASE_URL in vue

data() {
    
    
  return {
    
    
    publicPath: process.env.BASE_URL
  }
}

2.2 Load PDB model

Here we import the model through PDBLoader, but here we need to pay attention to the import path, splicing the publicpath variable we created to the file path, and the related processing can be carried out in the imported callback function as follows:

const loader = new PDBLoader()
loader.load(`${
      
      THIS.publicPath}models/aspirin.pdb`, model => {
    
    
	//对导入模型进行相关处理
	...
})

2.3 Processing molecular vertices

// 获取分子位置分量数组
 const atomsPositionArray = model.geometryAtoms.attributes.position.array
 // 获取分子颜色分量数组
 const colors = model.geometryAtoms.attributes.color.array

 // 每三个分量确定一个顶点
 for (let i = 0; i < atomsPositionArray.length; i += 3) {
    
    
   const sphere = new THREE.SphereGeometry(0.2)
   // 根据颜色分量确定顶点颜色
   const material = new THREE.MeshPhongMaterial({
    
    
     color: new THREE.Color(colors[i], colors[i + 1], colors[i + 2])
   })
   const mesh = new THREE.Mesh(sphere, material)

   // 根据位置分量确定顶点颜色
   mesh.position.set(
     atomsPositionArray[i],
     atomsPositionArray[i + 1],
     atomsPositionArray[i + 2]
   )
   this.group.add(mesh)
 }

2.4 Handling molecular connections

// 获取连线顶点位置分量数组
const bondsPositionArray = model.geometryBonds.attributes.position.array
// 每六个分量确定两个顶点,两个顶点建立一条连线
for (let i = 0; i < bondsPositionArray.length; i += 6) {
    
    
  const onePoint = new THREE.Vector3(
    bondsPositionArray[i],
    bondsPositionArray[i + 1],
    bondsPositionArray[i + 2]
  )
  const otherPoint = new THREE.Vector3(
    bondsPositionArray[i + 3],
    bondsPositionArray[i + 4],
    bondsPositionArray[i + 5]
  )
  //使用两个顶点创建样条曲线
  const path = new THREE.CatmullRomCurve3([onePoint, otherPoint])
  //根据样条曲线路径创建管道几何体
  const tube = new THREE.TubeGeometry(path, 1, 0.04)
  const material = new THREE.MeshPhongMaterial({
    
     color: 0xcccccc })
  const mesh = new THREE.Mesh(tube, material)
  this.group.add(mesh)
}

3.demo code

<template>
  <div>
    <div id="container"></div>
  </div>
</template>

<script>
import * as THREE from 'three'
import {
    
     OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import {
    
     PDBLoader } from 'three/examples/jsm/loaders/PDBLoader.js'

export default {
    
    
  data() {
    
    
    return {
    
    
      publicPath: process.env.BASE_URL,
      group: null,
      camera: null,
      scene: null,
      renderer: null,
      controls: null
    }
  },
  mounted() {
    
    
    this.init()
  },
  methods: {
    
    
    // 初始化
    init() {
    
    
      this.createScene() // 创建场景
      this.loadPDB() // 加载PDB模型
      this.createLight() // 创建光源
      this.createCamera() // 创建相机
      this.createRender() // 创建渲染器
      this.createControls() // 创建控件对象
      this.render() // 渲染
    },
    // 创建场景
    createScene() {
    
    
      this.scene = new THREE.Scene()
    },
    // 加载PDB模型
    loadPDB() {
    
    
      const THIS = this
      const loader = new PDBLoader()
      this.group = new THREE.Group()
      loader.load(`${
      
      THIS.publicPath}models/aspirin.pdb`, model => {
    
    
        // 获取分子位置分量数组
        const atomsPositionArray = model.geometryAtoms.attributes.position.array
        // 获取分子颜色分量数组
        const colors = model.geometryAtoms.attributes.color.array

        // 每三个分量确定一个顶点
        for (let i = 0; i < atomsPositionArray.length; i += 3) {
    
    
          const sphere = new THREE.SphereGeometry(0.2)
          // 根据颜色分量确定顶点颜色
          const material = new THREE.MeshPhongMaterial({
    
    
            color: new THREE.Color(colors[i], colors[i + 1], colors[i + 2])
          })
          const mesh = new THREE.Mesh(sphere, material)

          // 根据位置分量确定顶点颜色
          mesh.position.set(
            atomsPositionArray[i],
            atomsPositionArray[i + 1],
            atomsPositionArray[i + 2]
          )
          this.group.add(mesh)
        }

        // 获取连线顶点位置分量数组
        const bondsPositionArray = model.geometryBonds.attributes.position.array
        // 每六个分量确定两个顶点,两个顶点建立一条连线
        for (let i = 0; i < bondsPositionArray.length; i += 6) {
    
    
          const onePoint = new THREE.Vector3(
            bondsPositionArray[i],
            bondsPositionArray[i + 1],
            bondsPositionArray[i + 2]
          )
          const otherPoint = new THREE.Vector3(
            bondsPositionArray[i + 3],
            bondsPositionArray[i + 4],
            bondsPositionArray[i + 5]
          )
          //使用两个顶点创建样条曲线
          const path = new THREE.CatmullRomCurve3([onePoint, otherPoint])
          //根据样条曲线路径创建管道几何体
          const tube = new THREE.TubeGeometry(path, 1, 0.04)
          const material = new THREE.MeshPhongMaterial({
    
     color: 0xcccccc })
          const mesh = new THREE.Mesh(tube, material)
          this.group.add(mesh)
        }

        this.scene.add(this.group)
      })
    },

    // 创建光源
    createLight() {
    
    
      // 环境光
      const ambientLight = new THREE.AmbientLight(0xffffff, 0.1) // 创建环境光
      this.scene.add(ambientLight) // 将环境光添加到场景

      const spotLight = new THREE.SpotLight(0xffffff) // 创建聚光灯
      spotLight.position.set(50, 50, 50)
      spotLight.castShadow = true
      this.scene.add(spotLight)
    },
    // 创建相机
    createCamera() {
    
    
      const element = document.getElementById('container')
      const width = element.clientWidth // 窗口宽度
      const height = element.clientHeight // 窗口高度
      const k = width / height // 窗口宽高比
      // PerspectiveCamera( fov, aspect, near, far )
      this.camera = new THREE.PerspectiveCamera(35, k, 0.1, 1000)
      this.camera.position.set(8, 8, 8) // 设置相机位置

      this.camera.lookAt(new THREE.Vector3(0, 0, 0)) // 设置相机方向
      this.scene.add(this.camera)
    },
    // 创建渲染器
    createRender() {
    
    
      const element = document.getElementById('container')
      this.renderer = new THREE.WebGLRenderer({
    
     antialias: true, alpha: true })
      this.renderer.setSize(element.clientWidth, element.clientHeight) // 设置渲染区域尺寸
      this.renderer.shadowMap.enabled = true // 显示阴影
      this.renderer.shadowMap.type = THREE.PCFSoftShadowMap
      this.renderer.setClearColor(0x3f3f3f, 1) // 设置背景颜色
      element.appendChild(this.renderer.domElement)
    },

    render() {
    
    
      if (this.group) {
    
    
        this.group.rotation.y += 0.006
        this.group.rotation.x += 0.006
      }
      this.renderer.render(this.scene, this.camera)
      requestAnimationFrame(this.render)
    },
    // 创建控件对象
    createControls() {
    
    
      this.controls = new OrbitControls(this.camera, this.renderer.domElement)
    }
  }
}
</script>
<style>
#container {
    
    
  position: absolute;
  width: 100%;
  height: 100%;
}
</style>

Guess you like

Origin blog.csdn.net/qw8704149/article/details/113731055