آموزش Three.js

saalek110

Well-Known Member
JavaScript:
<script type="importmap">
{
  "imports": {
    "three": "./path/to/three.module.js"
  }
}
</script>
کد:

JavaScript:
<script type="module">
import * as THREE from 'three';
 
...
 
</script>

نوع اسکریپت
اولی importmap است دومی ماژول.

برای import کردن three باید نوع اسکریپت ماژول باشد.

میگه اول آدرس فقط باید به یکی از دو صورت باشد:

JavaScript:
Note that path specifier can start only with ./ or ../.

نقطه یا دو نقطه.

این جوری هم میشه نوشت:

JavaScript:
<script type="importmap">
{
  "imports": {
    "three": "https://cdn.jsdelivr.net/npm/three@<version>/build/three.module.js",
    "three/addons/": "https://cdn.jsdelivr.net/npm/three@<version>/examples/jsm/"
  }
}
</script>

یعنی داخل importmap می توانیم cdn استفاده کنیم.
 

saalek110

Well-Known Member
فایل منیجر ورژن ۱۰ ، برای کار در لوکال منتشر شد.
من خودم الان با ksweb در لوکال کار می کنم.
باهاش می توانید فایلهای html پوشه example را اجرا کنید و کدهای اونها را نگاه کنید.


 

saalek110

Well-Known Member
سالک: یک چیزایی نقل قول کردیم و چند تا سورس از مثالهای پوشه examples مرور کردیم.

حالا من می خوام اون برنامه گردش در چمن را که ۵ تا دکمه داشت را ادامه بدهم.

یک سورس بود که یک سری مکعب می ریخت روی صفحه که با انگشت میشد تکونشون داد...
می خوام برنامه گردش در چمن را روی اون پیاده کنم. یعنی آبجکتهای سنگ و درخت و چمن و آسمان را درش وارد کنم.

ولی یک مشکل برخورد کردم. کد زیر را ببینید:
JavaScript:
    const geometry_stone = new THREE.BoxGeometry();
      const geometry_stone2 = new THREE.BoxBufferGeometry();

فرقشون در اون کلمه buffer است . ولی دومی برنامه را مختل می کنه. دلیلش را نمی دونم. فعلا از اولی استفاده می کنم تا بعدا معلوم بشه چرا.
 

saalek110

Well-Known Member
JavaScript:
  //-------------------- stone ------------
      const geometry_stone = new THREE.BoxGeometry(20,20,20);
const texture_stone = new THREE.TextureLoader().load(   'textures/stone.jpg' );

const material_stone = new THREE.MeshBasicMaterial( { map: texture_stone } );
   const mesh_stone = new THREE.Mesh( geometry_stone, material_stone );
   
    mesh_stone.position.x = -150;
    mesh_stone.position.y = -100;
    mesh_stone.position.z = 5500;
    scene.add( mesh_stone );


کلمه const را جلوی همه چی گذاشتم ...تا .....بالاخره کدهای بالا را خطا نداد.
کدهای بالا یک مکعب تکسچردار است. ولی هنوز مکعب را در برنامه ندیدم. شاید جلوی دوربین نیست. باید سایز و مختصاتش را عوض کنم.

با عقب و جلو کردن دوربین و تغییر سایز و محل بلوک سیمانی ، آوردمش جلوی دوربین. عکس:

Screenshot_۲۰۲۴-۱۰-۰۴_۱۲۴۹۴۶.jpg

رنگش سفید ، خوب معلوم نیست.
تکسچرش را عوض می کنم.

Screenshot_۲۰۲۴-۱۰-۰۴_۱۲۵۴۳۸.jpg

حالا بهتر معلومه.
 
آخرین ویرایش:

saalek110

Well-Known Member
دکمه هایم را قبل تگ بادی بسته گذاشتم ولی دکمه ها رفت بالای صفحه.کدش:

HTML:
  <center>
<img src="textures/buttons/left.png" height="90" size="90" alt="Img" onclick="myFunction('4')">
 <img src="textures/buttons/up.png" height="90" size="90" alt="Img" onclick="myFunction('1')">
<img src="textures/buttons/stop.png" height="90" width="90" alt="Img" onclick="myFunction('3')">
<img src="textures/buttons/down.png" height="90" size="90" alt="Img" onclick="myFunction('2')">
<img src="textures/buttons/right.png" height="90" size="90" alt="Img" onclick="myFunction('5')">  
      </center>

قبلا در تاپیک عکس های دکمه را برای دانلود گذاشتم.

Screenshot_۲۰۲۴-۱۰-۰۴_۱۳۰۴۴۲.jpg

در عکس بالا می بینید دکمه ها رفته بالای صفحه، ولی من انتهای کدها گذاشتم دکمه ها را. فعلا مهم نیست ، شاید بعدا از خود three.js ابزار دکمه پیدا کردیم.
 

saalek110

Well-Known Member
تابع خود را هم اضافه کردم:در قبل بسته شدن اسکریپت اضافه کردم.
JavaScript:
function myFunction(x) {
   if (x==1) {
     flag=0;
 if(my_control==0     && flag==0)    {   my_control=-20;    flag=1; }
 if(my_control==-20   && flag==0)    {   my_control=-40;    flag=1; }
if(my_control==-40   && flag==0)    {   my_control=-60;    flag=1; }
if(my_control==-60   && flag==0)    {   my_control=-80;    flag=1; }
if(my_control==-80   && flag==0)    {   my_control=-100;    flag=1; }
}//x=1

if (x==2) { my_control=20; }
if (x==3) { my_control=0; }

if (x==4) {  rotation +=0.2618;   }
if (x==5) { rotation -=0.2618;    }
 
}  // function

ولی این را کجا درج کنم؟!! :
JavaScript:
camera.rotation.y=rotation;
camera.position.x += Math.sin(rotation) * my_control;
camera.position.z += Math.cos(rotation) * my_control;

در برنامه قبلی ، داخل تابع animate درج شده بود. ولی این برنامه تابع animate نداره.
 
آخرین ویرایش:

saalek110

Well-Known Member
برنامه اصلی اینه:
JavaScript:
                <!DOCTYPE html>
<html lang="en">
    <head>
        <title>three.js webgl - drag controls</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <link type="text/css" rel="stylesheet" href="main.css">
        <style>
            body {
                background-color: #f0f0f0;
                color: #444;
            }
            a {
                color: #08f;
            }
        </style>
    </head>
    <body>

        
        <script type="importmap">
            {
                "imports": {
                    "three": "./three-js-master/build/three.module.js",
                    "three/addons/": "./three-js-master/examples/jsm/"
                }
            }
        </script>

        <script type="module">

            import * as THREE from 'three';

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

            let container;
            let camera, scene, renderer;
            let controls, group;
            let enableSelection = false;

            const objects = [];

            const mouse = new THREE.Vector2(), raycaster = new THREE.Raycaster();

            init();

            function init() {

                container = document.createElement( 'div' );
                document.body.appendChild( container );

                camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 500 );
                camera.position.z = 25;

                scene = new THREE.Scene();
                scene.background = new THREE.Color( 0xf0f0f0 );

                scene.add( new THREE.AmbientLight( 0xaaaaaa ) );

                const light = new THREE.SpotLight( 0xffffff, 10000 );
                light.position.set( 0, 25, 50 );
                light.angle = Math.PI / 9;

                light.castShadow = true;
                light.shadow.camera.near = 10;
                light.shadow.camera.far = 100;
                light.shadow.mapSize.width = 1024;
                light.shadow.mapSize.height = 1024;

                scene.add( light );

                group = new THREE.Group();
                scene.add( group );

                const geometry = new THREE.BoxGeometry();

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

                    const object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );

                    object.position.x = Math.random() * 30 - 15;
                    object.position.y = Math.random() * 15 - 7.5;
                    object.position.z = Math.random() * 20 - 10;

                    object.rotation.x = Math.random() * 2 * Math.PI;
                    object.rotation.y = Math.random() * 2 * Math.PI;
                    object.rotation.z = Math.random() * 2 * Math.PI;

                    object.scale.x = Math.random() * 2 + 1;
                    object.scale.y = Math.random() * 2 + 1;
                    object.scale.z = Math.random() * 2 + 1;

                    object.castShadow = true;
                    object.receiveShadow = true;

                    scene.add( object );

                    objects.push( object );

                }

                renderer = new THREE.WebGLRenderer( { antialias: true } );
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( window.innerWidth, window.innerHeight );
                renderer.shadowMap.enabled = true;
                renderer.shadowMap.type = THREE.PCFShadowMap;

                container.appendChild( renderer.domElement );

                controls = new DragControls( [ ... objects ], camera, renderer.domElement );
                controls.rotateSpeed = 2;
                controls.addEventListener( 'drag', render );

                //

                window.addEventListener( 'resize', onWindowResize );

                document.addEventListener( 'click', onClick );
                window.addEventListener( 'keydown', onKeyDown );
                window.addEventListener( 'keyup', onKeyUp );

                render();

            }

            function onWindowResize() {

                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

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

                render();

            }

            function onKeyDown( event ) {

                enableSelection = ( event.keyCode === 16 ) ? true : false;
            
                if ( event.keyCode === 77 ) {

                    controls.touches.ONE = ( controls.touches.ONE === THREE.TOUCH.PAN ) ? THREE.TOUCH.ROTATE : THREE.TOUCH.PAN;
            
                }

            }

            function onKeyUp() {

                enableSelection = false;

            }

            function onClick( event ) {

                event.preventDefault();

                if ( enableSelection === true ) {

                    const draggableObjects = controls.objects;
                    draggableObjects.length = 0;

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

                    raycaster.setFromCamera( mouse, camera );

                    const intersections = raycaster.intersectObjects( objects, true );

                    if ( intersections.length > 0 ) {

                        const object = intersections[ 0 ].object;

                        if ( group.children.includes( object ) === true ) {

                            object.material.emissive.set( 0x000000 );
                            scene.attach( object );

                        } else {

                            object.material.emissive.set( 0xaaaaaa );
                            group.attach( object );

                        }

                        controls.transformGroup = true;
                        draggableObjects.push( group );

                    }

                    if ( group.children.length === 0 ) {

                        controls.transformGroup = false;
                        draggableObjects.push( ...objects );

                    }

                }

                render();

            }

            function render() {

                renderer.render( scene, camera );

            }

        </script>

    </body>
</html>
 

saalek110

Well-Known Member
داخل رویداد کلیک گذاشتم کدهای دو پست قبل را ولی کار نکرد.
گویا اینجا به بن بست خوردم.

متغیرهای لازم را هم بالای برنامه تعریف کردم.ولی کار نکرد.
ولی همین که بلوک را توانستم وارد برنامه کنم خوبه.
 

saalek110

Well-Known Member
در سورس زیر با لمس و کشیدن در صحنه می رویم جلو:

JavaScript:
        <!DOCTYPE html>
<html lang="en">
    <head>
        <title>three.js webgl - map controls</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <link type="text/css" rel="stylesheet" href="main.css">
        <style>
            body {
                background-color: #ccc;
                color: #000;
            }

            a {
                color: #f00;
            }
        </style>
    </head>

    <body>
        <div id="info">
            <a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - map controls
        </div>

        <script type="importmap">
            {
                "imports": {
                    "three": "../build/three.module.js",
                    "three/addons/": "./jsm/"
                }
            }
        </script>

        <script type="module">

            import * as THREE from 'three';

            import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

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

            let camera, controls, scene, renderer;

            init();
            //render(); // remove when using animation loop

            function init() {

                scene = new THREE.Scene();
                scene.background = new THREE.Color( 0xcccccc );
                scene.fog = new THREE.FogExp2( 0xcccccc, 0.002 );

                renderer = new THREE.WebGLRenderer( { antialias: true } );
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( window.innerWidth, window.innerHeight );
                renderer.setAnimationLoop( animate );
                document.body.appendChild( renderer.domElement );

                camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
                camera.position.set( 0, 200, - 400 );

                // controls

                controls = new MapControls( camera, renderer.domElement );

                //controls.addEventListener( 'change', render ); // call this only in static scenes (i.e., if there is no animation loop)

                controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
                controls.dampingFactor = 0.05;

                controls.screenSpacePanning = false;

                controls.minDistance = 100;
                controls.maxDistance = 500;

                controls.maxPolarAngle = Math.PI / 2;

                // world

                const geometry = new THREE.BoxGeometry();
                geometry.translate( 0, 0.5, 0 );
                const material = new THREE.MeshPhongMaterial( { color: 0xeeeeee, flatShading: true } );

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

                    const mesh = new THREE.Mesh( geometry, material );
                    mesh.position.x = Math.random() * 1600 - 800;
                    mesh.position.y = 0;
                    mesh.position.z = Math.random() * 1600 - 800;
                    mesh.scale.x = 20;
                    mesh.scale.y = Math.random() * 80 + 10;
                    mesh.scale.z = 20;
                    mesh.updateMatrix();
                    mesh.matrixAutoUpdate = false;
                    scene.add( mesh );

                }

                // lights

                const dirLight1 = new THREE.DirectionalLight( 0xffffff, 3 );
                dirLight1.position.set( 1, 1, 1 );
                scene.add( dirLight1 );

                const dirLight2 = new THREE.DirectionalLight( 0x002288, 3 );
                dirLight2.position.set( - 1, - 1, - 1 );
                scene.add( dirLight2 );

                const ambientLight = new THREE.AmbientLight( 0x555555 );
                scene.add( ambientLight );

                //

                window.addEventListener( 'resize', onWindowResize );


                const gui = new GUI();
                gui.add( controls, 'zoomToCursor' );
                gui.add( controls, 'screenSpacePanning' );

            }

            function onWindowResize() {

                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

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

            }

            function animate() {

                controls.update(); // only required if controls.enableDamping = true, or if controls.autoRotate = true

                render();

            }

            function render() {

                renderer.render( scene, camera );

            }

        </script>

    </body>
</html>
 

saalek110

Well-Known Member
سورس پست قبل ،عنوانش هم map control است:

Screenshot_۲۰۲۴-۱۰-۰۴_۱۳۳۰۲۴.jpg

اگر دو انگشتی لمس کنیم می توانیم زاویه را هم تغییر دهیم.
دقیقا اون چیزی که من می حوام نیست ولی بد هم نیست.
کنترل جالبی است کلا.

حالا کلی سورس دیگه هم هست.
 

saalek110

Well-Known Member
با لمس مکعب خلق میشه:
JavaScript:
        <!DOCTYPE html>
<html lang="en">
    <head>
        <title>three.js webgl - interactive - voxel painter</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <link type="text/css" rel="stylesheet" href="main.css">
        <style>
            body {
                background-color: #f0f0f0;
                color: #444;
            }
            a {
                color: #08f;
            }
        </style>
    </head>
    <body>

        <div id="info">
            <a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - voxel painter - webgl<br>
            <strong>click</strong>: add voxel, <strong>shift + click</strong>: remove voxel
        </div>

        <script type="importmap">
            {
                "imports": {
                    "three": "../build/three.module.js",
                    "three/addons/": "./jsm/"
                }
            }
        </script>

        <script type="module">

            import * as THREE from 'three';

            let camera, scene, renderer;
            let plane;
            let pointer, raycaster, isShiftDown = false;

            let rollOverMesh, rollOverMaterial;
            let cubeGeo, cubeMaterial;

            const objects = [];

            init();
            render();

            function init() {

                camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
                camera.position.set( 500, 800, 1300 );
                camera.lookAt( 0, 0, 0 );

                scene = new THREE.Scene();
                scene.background = new THREE.Color( 0xf0f0f0 );

                // roll-over helpers

                const rollOverGeo = new THREE.BoxGeometry( 50, 50, 50 );
                rollOverMaterial = new THREE.MeshBasicMaterial( { color: 0xff0000, opacity: 0.5, transparent: true } );
                rollOverMesh = new THREE.Mesh( rollOverGeo, rollOverMaterial );
                scene.add( rollOverMesh );

                // cubes

                const map = new THREE.TextureLoader().load( 'textures/square-outline-textured.png' );
                map.colorSpace = THREE.SRGBColorSpace;
                cubeGeo = new THREE.BoxGeometry( 50, 50, 50 );
                cubeMaterial = new THREE.MeshLambertMaterial( { color: 0xfeb74c, map: map } );

                // grid

                const gridHelper = new THREE.GridHelper( 1000, 20 );
                scene.add( gridHelper );

                //

                raycaster = new THREE.Raycaster();
                pointer = new THREE.Vector2();

                const geometry = new THREE.PlaneGeometry( 1000, 1000 );
                geometry.rotateX( - Math.PI / 2 );

                plane = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { visible: false } ) );
                scene.add( plane );

                objects.push( plane );

                // lights

                const ambientLight = new THREE.AmbientLight( 0x606060, 3 );
                scene.add( ambientLight );

                const directionalLight = new THREE.DirectionalLight( 0xffffff, 3 );
                directionalLight.position.set( 1, 0.75, 0.5 ).normalize();
                scene.add( directionalLight );

                renderer = new THREE.WebGLRenderer( { antialias: true } );
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( window.innerWidth, window.innerHeight );
                document.body.appendChild( renderer.domElement );

                document.addEventListener( 'pointermove', onPointerMove );
                document.addEventListener( 'pointerdown', onPointerDown );
                document.addEventListener( 'keydown', onDocumentKeyDown );
                document.addEventListener( 'keyup', onDocumentKeyUp );

                //

                window.addEventListener( 'resize', onWindowResize );

            }

            function onWindowResize() {

                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

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

                render();

            }

            function onPointerMove( event ) {

                pointer.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1 );

                raycaster.setFromCamera( pointer, camera );

                const intersects = raycaster.intersectObjects( objects, false );

                if ( intersects.length > 0 ) {

                    const intersect = intersects[ 0 ];

                    rollOverMesh.position.copy( intersect.point ).add( intersect.face.normal );
                    rollOverMesh.position.divideScalar( 50 ).floor().multiplyScalar( 50 ).addScalar( 25 );

                    render();

                }

            }

            function onPointerDown( event ) {

                pointer.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1 );

                raycaster.setFromCamera( pointer, camera );

                const intersects = raycaster.intersectObjects( objects, false );

                if ( intersects.length > 0 ) {

                    const intersect = intersects[ 0 ];

                    // delete cube

                    if ( isShiftDown ) {

                        if ( intersect.object !== plane ) {

                            scene.remove( intersect.object );

                            objects.splice( objects.indexOf( intersect.object ), 1 );

                        }

                        // create cube

                    } else {

                        const voxel = new THREE.Mesh( cubeGeo, cubeMaterial );
                        voxel.position.copy( intersect.point ).add( intersect.face.normal );
                        voxel.position.divideScalar( 50 ).floor().multiplyScalar( 50 ).addScalar( 25 );
                        scene.add( voxel );

                        objects.push( voxel );

                    }

                    render();

                }

            }

            function onDocumentKeyDown( event ) {

                switch ( event.keyCode ) {

                    case 16: isShiftDown = true; break;

                }

            }

            function onDocumentKeyUp( event ) {

                switch ( event.keyCode ) {

                    case 16: isShiftDown = false; break;

                }

            }

            function render() {

                renderer.render( scene, camera );

            }

        </script>

    </body>
</html>

Screenshot_۲۰۲۴-۱۰-۰۴_۱۳۴۸۱۱.jpg
 

saalek110

Well-Known Member
من الان در گوشی ام ، خیلی از این سورس ها با کیبورد و ماوس کار می کنه.

شاید گاهی بهتر باشه به جای گشتن میان سورس ها ، در گوگل سرچ کنیم.

کد alert:

JavaScript:
alert('hi');

وقتی خواستید ببینید کنترل برنامه به جایی منتقل میشه یا نه ، یک alert اونجا بزارید. مثلا آیا وارد فلان تابع می شویم یا نه.

برای اضافه کردن دکمه سایت زیر را ببینید. تست کردم کار می کنه.



نقل کد از سایت بالا : کد زیر را در قسمت html بگذارید:

HTML:
<button id="PAW" class="pauseButton">Pause</button>

و این را در قسمت جاوا اسکریپت یعنی داخل تگ های script :

JavaScript:
document.getElementById("PAW").addEventListener("click", togglePause, false);

JavaScript:
function togglePause() {
 
}

تابع هم در قسمت جاوا اسکریپت.
 
آخرین ویرایش:

saalek110

Well-Known Member
با استفاده از کد سایت پست قبل من توانستم با زدن دکمه دوربین را جابجا کنم. ولی باید مرتب دکمه ها را بزنم.
در برنامه قبلی یک متغیر را تنظیم می کردم و تابع animate مدام باعث حرکت بود. ولی سورسی که من انتخاب کردم تابع animate نداره که من کدم را داخلش درج کنم.

ایجاد تابع animate مجازی با setInterval:

سالک؛ دیشب که داشتم سایت three.js را مطالعه می کردم ، مقایسه ای بین setInterval کرده بود و انگار تابع animate , و گفته بود مزیت تابع animate اینه که اگر برویم به یک صفحه دیگر سایت ، از کار می افته ولی setInterval از کار نمی افته.

با توجه به حرفهای خود سایت three.js حالا که سورس فوق تابع animate نداره ، من از setInterval استفاده می کنم.

JavaScript:
setInterval(my_timer, 100);

کد بالا را در تابع init بزارید. معنی کد بالا اینه که تابع my_timer را هر یک دهم ثانیه اجرا کن. زمانش به یک هزارم ثانیه است. اسم my_timer را هم من خودم ساختم، شما هر اسمی دوست داشید بزارید.

حالا تابع my_timer چیه؟

JavaScript:
function my_timer() {
   
        camera.position.z=camera.position.z-1;
        render();
}

در تابع بالا ، موقعیت z دوربین را کمی من جابجا کردم و بعد تابع render را صدا زدم.

سیستم استفاده از setInterval کار می کنه ولی به خوبی تابع animate نیست ولی برای بعضی کارها فکر کنم چیز خوبیه. مثلا هر یکی دو ثانیه یک چیزی را چک کند. برای ساخت بازی خوبه.

گویا باید دنبال جایگزین تابع animate باز بگردم.
 
آخرین ویرایش:

جدیدترین ارسال ها

بالا