½ÃÀÛÆäÀÌÁö·Î Áñ°Üã±âÃß°¡
·Î±×ÀÎ
ȸ¿ø°¡ÀÔ l Ã⼮üũ l ¸¶ÀÌÆäÀÌÁö l CGIMALL
3D À̹ÌÁö Å¥ºê °¶·¯¸® ÀÎÅÍ·¢¼Ç È¿°ú : happycgi
ÀÚ·á½Ç »çÀÌÆ®µî·Ï ·©Å·100 ÇÁ·Î±×·¥¸®ºä °ü¸®ÀÚÃßõÀÚ·á Ãʺ¸°¡À̵å
Ä¿¹Â´ÏƼ
Àüü ÆîÃ帱â
Äü¸Þ´º¸µÅ© jquery , CSS , PHP , Javascript , ¹«·áÆùÆ® , ASP
»ó¼¼°Ë»ö
Ȩ > CSS > ±âº» ¼Ò½º > 3D À̹ÌÁö Å¥ºê °¶·¯¸® ÀÎÅÍ·¢¼Ç È¿°ú »ó¼¼Á¤º¸
»çÀÌÆ®µî·Ï
Ŭ¶ó¿ìµåű×
javascript
HTML
PHP
Api
CSS
ASP
mysql
2022
Slide
jquery
¸ÞÀϸµ
¸ðµâ
°¶·¯¸®
ÇöÀçÁ¢¼ÓÀÚ ¸í »õ·Î°íħ
3D À̹ÌÁö Å¥ºê °¶·¯¸® ÀÎÅÍ·¢¼Ç È¿°ú
¼Ò½ºÅë°èÁ¤º¸ ¿À·ù½Å°í ¹× ¹®ÀÇ
ÇØÇÇÆÀ
³×ƼÁð
Æ®À§ÅÍ·Î º¸³»±â ÆäÀ̽ººÏÀ¸·Î º¸³»±â
¼Ò½ººÐ·ù ±âº» ¼Ò½º
´Ù¿î·Îµå Ƚ¼ö 11 ȸ
°£´Ü¼³¸í ¸¶¿ì½º ¿òÁ÷ÀÓ¿¡ ¹ÝÀÀÇÏ¿© À̹ÌÁö Å¥ºê°¡ ÀÔüÀûÀ¸·Î ¿òÁ÷ÀÌ´Â 3D °¶·¯¸® ÀÎÅÍ·¢¼Ç È¿°ú
Æò°¡Çϱâ ÈǸ¢ÇÔ ¸Å¿ìÁÁÀ½ ÁÁÀ½ ±¦ÂúÀ½ º¸Åë º°·Î
ȨÆäÀÌÁö¹Ù·Î°¡±â ¼Ò½º´Ù¿î·Îµå µ¥¸ð ¹Ì¸®º¸±â ½ºÅ©·¦Çϱâ

¿©·¯ °³ÀÇ À̹ÌÁö¸¦ 3D Å¥ºê ÇüÅ·Π¹èÄ¡ÇÏ¿© ¸¶¿ì½º ¿òÁ÷ÀÓ¿¡ µû¶ó ÀÔüÀûÀ¸·Î ¹ÝÀÀÇÏ´Â
À̹ÌÁö °¶·¯¸® È¿°ú¸¦ ±¸ÇöÇÑ ¿¹Á¦ÀÔ´Ï´Ù. 
»ç¿ëÀÚ°¡ ¸¶¿ì½º¸¦ ¿òÁ÷À̸é À̹ÌÁö°¡ ÀÚ¿¬½º·´°Ô À̵¿Çϰųª °­Á¶µÇ¾î º¸¿©Áö¸ç,
ÀϹÝÀûÀÎ À̹ÌÁö ¸ñ·Ïº¸´Ù ´õ »ýµ¿°¨ ÀÖ´Â È­¸é ±¸¼ºÀ» ¸¸µé ¼ö ÀÖ½À´Ï´Ù.


Æ÷Æ®Æú¸®¿À ÆäÀÌÁö, ÀÛǰ °¶·¯¸®, »óǰ ¼Ò°³ ÆäÀÌÁö µî¿¡¼­
½Ã°¢ÀûÀ¸·Î Èï¹Ì·Î¿î ÄÜÅÙÃ÷ Ç¥ÇöÀ» À§ÇØ È°¿ëÇÒ ¼ö ÀÖ´Â ÀÎÅÍ·¢¼Ç È¿°úÀÔ´Ï´Ù.

HTML ±¸Á¶

<!-- Lightbox HTML -->

<div id="lightbox">

    <div id="close-btn">&times;</div>

    <img id="lightbox-img" src="" alt="Fullsize">

</div>


<script type="importmap">

  {

    "imports": {

      "three": "https://unpkg.com/three@0.160.0/build/three.module.js",

      "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"

    }

  }

</script>



CSS ¼Ò½º

  body { margin: 0; overflow: hidden; background-color: #000; }

        canvas { display: block; }


        /* Lightbox styles */

        #lightbox {

            display: none; /* Hidden by default */

            position: fixed;

            z-index: 1000;

            top: 0;

            left: 0;

            width: 100%;

            height: 100%;

            background-color: rgba(0, 0, 0, 0.9);

            justify-content: center;

            align-items: center;

            opacity: 0;

            transition: opacity 0.3s ease;

        }


        #lightbox.active {

            display: flex; /* Use flex to center content */

            opacity: 1;

        }


        #lightbox img {

            max-width: 90%;

            max-height: 90%;  

        }


        #close-btn {

            position: absolute;

            top: 20px;

            right: 40px;

            color: #fff;

            font-size: 30px;

            cursor: pointer;

            font-family: sans-serif;

            user-select: none;

        }



JS ¼Ò½º

 import * as THREE from 'three';

    import { OrbitControls } from 'three/addons/controls/OrbitControls.js';


    // === 1. SCENE SETUP ===

    const scene = new THREE.Scene();

    scene.background = new THREE.Color(0x000000);


    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);

    camera.position.set(0, 100, 120);


    const renderer = new THREE.WebGLRenderer({ antialias: true });

    renderer.setSize(window.innerWidth, window.innerHeight);

    renderer.setPixelRatio(window.devicePixelRatio); // For high DPI screens

    document.body.appendChild(renderer.domElement);


    const controls = new OrbitControls(camera, renderer.domElement);

    controls.enableDamping = true; // Inertia

    controls.dampingFactor = 0.05;

    controls.minDistance = 50;

    controls.maxDistance = 500;

    controls.maxPolarAngle = Math.PI / 2; // Prevent camera from going under the grid


    // Lighting

    const ambientLight = new THREE.AmbientLight(0xffffff, 1.0);

    scene.add(ambientLight);


    const dirLight = new THREE.DirectionalLight(0xffffff, 1.5);

    dirLight.position.set(20, 80, 50);

    scene.add(dirLight);


    // === 2. CUBE GENERATION ===

    const cols = 12;

    const rows = 8;

    const cubeSize = 10;

    const gap = 0.5;

    const step = cubeSize + gap;


    // Calculate grid dimensions to center it

    const gridWidth = cols * step - gap;

    const gridDepth = rows * step - gap;

    const startX = -gridWidth / 2 + cubeSize / 2;

    const startZ = -gridDepth / 2 + cubeSize / 2;


    const cubes = []; 


    const textureLoader = new THREE.TextureLoader();

    const sideMaterial = new THREE.MeshLambertMaterial({ color: 0x222222 }); // Dark grey sides

    const geometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);


    for (let i = 0; i < cols; i++) {

        for (let j = 0; j < rows; j++) {

            

            // Use 'seed' to get the same image for thumbnail and full size

            const seed = `img_${i}_${j}`; 

            const thumbUrl = `https://picsum.photos/seed/${seed}/200/200`; // Low res for texture

            const fullUrl = `https://picsum.photos/seed/${seed}/1200/800`; // High res for lightbox


            const texture = textureLoader.load(thumbUrl);

            texture.anisotropy = renderer.capabilities.getMaxAnisotropy();

            

            const topMaterial = new THREE.MeshLambertMaterial({ map: texture });

            

            // Material array: [Right, Left, Top, Bottom, Front, Back]

            const materials = [

                sideMaterial, sideMaterial, 

                topMaterial, // Top face has the image

                sideMaterial, sideMaterial, sideMaterial

            ];


            const cube = new THREE.Mesh(geometry, materials);


            // Positioning

            cube.position.x = startX + i * step;

            cube.position.z = startZ + j * step;

            cube.position.y = 0;


            // Store custom data for animation and lightbox

            cube.userData = { 

                targetY: 0, 

                fullUrl: fullUrl

            };


            scene.add(cube);

            cubes.push(cube);

        }

    }


    // === 3. RAYCASTING SETUP ===

    const raycaster = new THREE.Raycaster();

    const mouse = new THREE.Vector2(9999, 9999); // Start off-screen

    let hoveredCube = null;


    function onMouseMove(event) {

        // Normalize mouse coordinates (-1 to +1)

        mouse.x = (event.clientX / window.innerWidth) * 2 - 1;

        mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

    }

    window.addEventListener('mousemove', onMouseMove, false);


    // === 4. CLICK HANDLERS & LIGHTBOX ===

    const mouseDownPos = new THREE.Vector2();

    

    // Track mouse down position

    window.addEventListener('mousedown', (event) => {

        mouseDownPos.x = event.clientX;

        mouseDownPos.y = event.clientY;

    });


    const lightbox = document.getElementById('lightbox');

    const lightboxImg = document.getElementById('lightbox-img');

    const closeBtn = document.getElementById('close-btn');


    function openLightbox(url) {

        lightboxImg.src = url;

        lightbox.style.display = 'flex';

        requestAnimationFrame(() => lightbox.classList.add('active'));

    }


    function closeLightbox() {

        lightbox.classList.remove('active');

        setTimeout(() => {

            lightbox.style.display = 'none';

            lightboxImg.src = "";

        }, 300); // Wait for transition

    }


    window.addEventListener('click', (event) => {

        // Calculate distance between mousedown and mouseup

        const dx = event.clientX - mouseDownPos.x;

        const dy = event.clientY - mouseDownPos.y;

        const distance = Math.sqrt(dx * dx + dy * dy);


        // If moved less than 5px, treat as a click. Otherwise, it's a drag (camera control).

        if (distance < 5 && hoveredCube) {

            openLightbox(hoveredCube.userData.fullUrl);

        }

    });


    // Close on background click or close button

    lightbox.addEventListener('click', (e) => {

        if (e.target !== lightboxImg) closeLightbox();

    });

    closeBtn.addEventListener('click', closeLightbox);



    // === 5. ANIMATION LOOP ===

    function animate() {

        requestAnimationFrame(animate);

        controls.update();


        // LOGIC: Check intersection with actual cubes

        raycaster.setFromCamera(mouse, camera);

        const intersects = raycaster.intersectObjects(cubes);


        if (intersects.length > 0) {

            // Get the first (closest) cube

            hoveredCube = intersects[0].object;

        } else {

            hoveredCube = null;

        }


        // Update cursor style

        if (hoveredCube) {

            document.body.style.cursor = 'pointer';

        } else {

            document.body.style.cursor = 'default';

        }


        // Animate cubes

        cubes.forEach(cube => {

            if (cube === hoveredCube) {

                // Lift height set to 5

                cube.userData.targetY = 5;

            } else {

                cube.userData.targetY = 0;

            }


            // Smooth interpolation (Lerp)

            cube.position.y += (cube.userData.targetY - cube.position.y) * 0.15;

        });


        renderer.render(scene, camera);

    }


    // Handle window resize

    window.addEventListener('resize', () => {

        camera.aspect = window.innerWidth / window.innerHeight;

        camera.updateProjectionMatrix();

        renderer.setSize(window.innerWidth, window.innerHeight);

    });


    animate();


 

³×ƼÁð ÀÇ°ß   ÀÌ¿ëÇϽŠÀÚ·áÀÇ Èı⸦ ÀÚÀ¯·Ó°Ô ÀÛ¼ºÇϼ¼¿ä. (»ó¾÷ÀûÀÎ ±¤°í ¹× µµ¹è¼º ±Û µîÀº »çÀüÅ뺸¾øÀÌ »èÁ¦µÉ ¼ö ÀÖ½À´Ï´Ù.)
³»¿ë ¾ÆÀ̵ð Àǰ߳²±â±â
µî·ÏµÈ ÀǰßÀÌ ¾ø½À´Ï´Ù.
1
À̸§
³»¿ë
:³×¸Â¾Æ¿ä: :È­³ª´Â±º¿ä: :Àá¿Í: :¿ì¿ïÇØ: :À̰ǾƳÄ: :¿ÕÇÏÇÏ: ¿Õ¿ôÀ½~ ³î·¥~
Æò°¡Çϱâ ÈǸ¢ÇÔ ¸Å¿ìÁÁÀ½ ÁÁÀ½ ±¦ÂúÀ½ º¸Åë º°·Î
µµ¹è¹æÁöŰ
 58237992 º¸ÀÌ´Â µµ¹è¹æÁö۸¦ ÀÔ·ÂÇϼ¼¿ä.