Cara Menampilkan Model Molekul 3D dengan Three.js + Addons PDBloader

Jumat, 30 Mei 2025 edit

Bagi yang baru pertama kali ingin menampilkan obyek molekul 3D dapat dicoba dengan memperlajari dan menggunakan kode di bawah ini. Boleh di muat di blog atau web pribadi atau disimpan sebagai file html tunggal untuk dibuka bahan belajar mandiri.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!-- Judul halaman yang akan muncul di tab browser -->
    <title>Model 3D SF6</title>
    <style>
        /* Reset margin body agar tidak ada jarak di tepi */
        body { margin: 0; overflow: hidden; }
        /* Container untuk kanvas 3D, mengisi seluruh viewport */
        #container { width: 100%; height: 100vh; }
    </style>
</head>
<body>
    <!-- Div untuk menampung kanvas rendering Three.js -->
    <div id="container"></div>

    <!-- Memuat Three.js core library versi 0.132.2 -->
    <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
    <!-- Memuat OrbitControls untuk interaksi putar/zoom -->
    <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.js"></script>
    <!-- Memuat PDBLoader untuk parse data molekul PDB -->
    <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/loaders/PDBLoader.js"></script>

    <script>
        // Periksa apakah THREE tersedia
        if (typeof THREE === 'undefined') {
            console.error('Three.js failed to load. Check CDN or network.');
            alert('Error: Three.js not loaded. Please check your internet connection or try again later.');
            throw new Error('Three.js not loaded');
        }

        // Membuat scene, tempat semua objek 3D berada
        const scene = new THREE.Scene();
        // Mengatur warna latar belakang scene menjadi putih
        scene.background = new THREE.Color(0xffffff);

        // Membuat kamera perspektif: FOV 30°, rasio aspek, near clip 0.1, far clip 1000
        const camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 0.1, 1000);
        // Mengatur posisi kamera di sumbu Z untuk melihat molekul
        camera.position.z = 10;

        // Membuat renderer WebGL dengan anti-aliasing untuk tepian halus
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        // Mengatur ukuran renderer sesuai ukuran window
        renderer.setSize(window.innerWidth, window.innerHeight);
        // Menambahkan elemen kanvas renderer ke container
        document.getElementById('container').appendChild(renderer.domElement);

        // Menambahkan OrbitControls untuk interaksi mouse (putar, zoom, pan)
        const controls = new THREE.OrbitControls(camera, renderer.domElement);
        // Mengaktifkan damping untuk gerakan lebih halus
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;

        // Menambahkan ambient light untuk pencahayaan merata
        const ambientLight = new THREE.AmbientLight(0x606060, 0.8); // Warna abu, intensitas 0.8
        scene.add(ambientLight);
        // Menambahkan directional light untuk efek bayangan
        const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6); // Warna putih, intensitas 0.6
        directionalLight.position.set(1, 1, 1).normalize(); // Arah cahaya dari (1, 1, 1)
        scene.add(directionalLight);

        // Objek properti atom: warna dan radius untuk belerang (S) dan fluor (F)
        const atomProperties = {
            'S': { color: 0xffff00, radius: 1.04 }, // Kuning untuk sulfur
            'F': { color: 0x00ff00, radius: 0.64 }  // Hijau untuk fluor
        };

        // Fungsi untuk membuat label teks sebagai sprite
        function createLabelSprite(element) {
            // Membuat elemen canvas 2D untuk teks
            const canvas = document.createElement('canvas');
            canvas.width = 256;  // Ukuran canvas 256x256 piksel
            canvas.height = 256;
            // Mendapatkan konteks 2D untuk menggambar
            const context = canvas.getContext('2d');
            // Mengatur font teks (bold, ukuran 48px, Arial)
            context.font = 'bold 48px Arial';
            // Mengatur warna teks hitam
            context.fillStyle = 'black';
            // Mengatur perataan teks ke tengah
            context.textAlign = 'center';
            context.textBaseline = 'middle';
            // Menggambar teks di tengah canvas
            context.fillText(element, 128, 128);

            // Membuat tekstur dari canvas
            const texture = new THREE.CanvasTexture(canvas);
            texture.needsUpdate = true; // Memastikan tekstur diperbarui
            // Membuat material sprite dengan tekstur transparan
            const spriteMaterial = new THREE.SpriteMaterial({
                map: texture,
                transparent: true,
                depthTest: false // Mencegah label tertutup objek lain
            });
            // Membuat sprite dari material
            const sprite = new THREE.Sprite(spriteMaterial);
            // Mengatur skala sprite untuk ukuran teks
            sprite.scale.set(0.8, 0.8, 0.8);
            // Mengatur urutan render untuk prioritas
            sprite.renderOrder = 1;
            return sprite;
        }

        // Fungsi untuk membuat atom sebagai bola
        function createAtoms(geometry, json) {
            const group = new THREE.Group(); // Grup untuk menampung semua atom
            const positions = geometry.attributes.position.array; // Posisi atom dari geometri
            for (let i = 0; i < json.atoms.length; i++) {
                const element = json.atoms[i][4].trim(); // Nama elemen (S atau F)
                const props = atomProperties[element]; // Ambil properti elemen
                // Membuat geometri bola untuk atom
                const sphereGeometry = new THREE.SphereGeometry(props.radius * 0.5, 16, 16);
                // Membuat material dengan warna dan efek kilau
                const material = new THREE.MeshPhongMaterial({
                    color: props.color,
                    shininess: 50,
                    specular: 0x111111
                });
                // Membuat mesh atom
                const sphere = new THREE.Mesh(sphereGeometry, material);
                // Mengatur posisi atom dari data PDB
                sphere.position.set(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]);
                sphere.renderOrder = 0; // Atom dirender sebelum sprite
                group.add(sphere);
                // Membuat dan menambahkan label sprite
                const label = createLabelSprite(element);
                // Mengatur posisi label sama dengan atom, dengan offset kecil di Z
                label.position.set(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2] + 0.01);
                group.add(label);
            }
            return group;
        }

        // Fungsi untuk membuat ikatan antar atom sebagai tabung
        function createBonds(geometry) {
            const group = new THREE.Group(); // Grup untuk menampung ikatan
            const positions = geometry.attributes.position.array; // Posisi ikatan dari geometri
            for (let i = 0; i < positions.length; i += 6) {
                // Titik awal ikatan
                const start = new THREE.Vector3(positions[i], positions[i + 1], positions[i + 2]);
                // Titik akhir ikatan
                const end = new THREE.Vector3(positions[i + 3], positions[i + 4], positions[i + 5]);
                // Membuat kurva garis untuk ikatan
                const path = new THREE.LineCurve3(start, end);
                // Membuat geometri tabung untuk ikatan
                const tubeGeometry = new THREE.TubeGeometry(path, 1, 0.08, 8, false);
                // Membuat material untuk ikatan
                const material = new THREE.MeshPhongMaterial({
                    color: 0xcccccc, // Warna abu-abu
                    shininess: 50,
                    specular: 0x111111
                });
                // Membuat mesh tabung
                const tube = new THREE.Mesh(tubeGeometry, material);
                group.add(tube);
            }
            return group;
        }

        // Data PDB untuk molekul SF6
        const pdbData = `COMPND    SF6
AUTHOR    GENERATED BY THREE.JS
HETATM    1  S    S  X   1       0.000   0.000   0.000  1.00  0.00           S
HETATM    2  F    F  X   1       0.000   0.000   1.580  1.00  0.00           F
HETATM    3  F    F  X   1       0.000   0.000  -1.580  1.00  0.00           F
HETATM    4  F    F  X   1       0.000   1.580   0.000  1.00  0.00           F
HETATM    5  F    F  X   1       0.000  -1.580   0.000  1.00  0.00           F
HETATM    6  F    F  X   1       1.580   0.000   0.000  1.00  0.00           F
HETATM    7  F    F  X   1      -1.580   0.000   0.000  1.00  0.00           F
CONECT    1    2    3    4    5    6    7
CONECT    2    1
CONECT    3    1
CONECT    4    1
CONECT    5    1
CONECT    6    1
CONECT    7    1
END`;

        // Mem-parse data PDB menggunakan PDBLoader
        const loader = new THREE.PDBLoader();
        const pdb = loader.parse(pdbData);
        // Membuat atom dan ikatan dari data PDB
        const atoms = createAtoms(pdb.geometryAtoms, pdb.json);
        const bonds = createBonds(pdb.geometryBonds);
        // Mengelompokkan atom dan ikatan ke dalam satu grup molekul
        const molecule = new THREE.Group();
        molecule.add(atoms, bonds);
        // Menambahkan molekul ke scene
        scene.add(molecule);

        // Menyesuaikan posisi kamera berdasarkan ukuran molekul
        const boundingBox = new THREE.Box3().setFromObject(molecule);
        const size = boundingBox.getSize(new THREE.Vector3()).length();
        camera.position.z = size * 2; // Jarak kamera 2x ukuran molekul
        controls.target.set(0, 0, 0); // Fokus kontrol ke pusat molekul
        controls.update();

        // Fungsi untuk menangani perubahan ukuran window
        function onWindowResize() {
            // Memperbarui rasio aspek kamera
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            // Memperbarui ukuran renderer
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        // Fungsi animasi untuk render berulang
        function animate() {
            requestAnimationFrame(animate); // Memanggil animate lagi
            controls.update(); // Memperbarui kontrol interaksi
            renderer.render(scene, camera); // Merender scene
        }

        // Menambahkan event listener untuk resize window
        window.addEventListener('resize', onWindowResize);
        // Memulai loop animasi
        animate();
    </script>
</body>
</html>


Selamat mencoba

Bagikan di

Tidak ada komentar:

Posting Komentar

 
Copyright © 2015-2025 Urip dot Info | Disain Template oleh Herdiansyah Dimodivikasi Urip.Info