آموزش 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
حالت import کردن را هم شبیه هنه برنامه های three.js کردم یعنی این طوری:

JavaScript:
    <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';

همان طور که در کد بالا می بینید ، فقط فایل three.js اصلی را import کردم و فعلا از پوشه jsm چیزی استفاده نکردم.

من فعلا روشم اینه که فایلم را بیرون بسته بزارم و به فایلهای درون بسته را import کنم.
 
آخرین ویرایش:

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 toggle1() {
rotation -=0.2618;
}  // t1 function

function toggle2() {
     flag=0;
 if(my_control==0     && flag==0)    {   my_control=1;    flag=1; }
 if(my_control==1   && flag==0)    {   my_control=2;    flag=1; }
if(my_control==2   && flag==0)    {   my_control=3;    flag=1; }
if(my_control==3   && flag==0)    {   my_control=4;    flag=1; }
if(my_control==4   && flag==0)    {   my_control=5;    flag=1; }
}  // t2 function


function toggle3() {
     my_control=0;
}  // t3 function


function toggle4() {
     my_control=-1;
}  // t4 function


function toggle5() {
     rotation +=0.2618;
}  // t5 function


document.getElementById("PAW1").addEventListener("click", toggle1, false);
document.getElementById("PAW2").addEventListener("click", toggle2, false);
document.getElementById("PAW3").addEventListener("click", toggle3, false);
document.getElementById("PAW4").addEventListener("click", toggle4, false);
document.getElementById("PAW5").addEventListener("click", toggle5, false);
        </script>
 
 
        <button id="PAW1" class="pauseButton"  style="width:70px; height:100px;">left</button>
        <button id="PAW2" class="pauseButton"  style="width:70px; height:100px;">ahead</button>
        <button id="PAW3" class="pauseButton"  style="width:70px; height:100px;">stop</button>
        <button id="PAW4" class="pauseButton"  style="width:70px; height:100px;">back</button>
        <button id="PAW5" class="pauseButton"  style="width:70px; height:100px;">right</button>

در کد بالا دقت کنید که این خط:
JavaScript:
     </script>

وسط کدهاست و مرز بین قسمت جاوا اسکریپت و قسمت html انتهای کدهاست.
چون کد بالا ، دو قسمت جاوا اسکریپتی و html ئی دارد.

بعد اینکه importmap بسته شد و تگ اسکریپت با تایپ ماژول باز شد ، اون تگ در انتهای فایل بسته میشه که همین تگی است که در بالا می بینید ، یعنی تگی است که تقریبا کل برنامه داخلش هست.


پس من برای سازگار کردن برنامه ام با مثالهای بسته three.js

اولا شبیه اونها import. کردم.

دوما مجبور شدم کلیدهای نوع دیگری به کار ببرم.


سوما اون کدی که یکی دو پست بالاتر گفتم ، یعنی این:
JavaScript:
const geometry_stone = new THREE.BoxGeometry();
 const geometry_stone2 = new THREE.BoxBufferGeometry();

اونی که کلمه بافر نداره را الان در برنامه جدید استفاده کردم.


و شاید چهارما ، به جای var ها اول برنامه let گذاشتم و وسط برنامه const گذاشتم. ولی شاید این کار لازم نبود.چهارمی قطعی نیست.

عکسی از برنامه من:
Screenshot_۲۰۲۴-۱۰-۰۴_۲۰۵۴۰۷.jpg
 
آخرین ویرایش:

saalek110

Well-Known Member
خوب ، برنامه ام شبیه برنانه های پوشه examples شد.

قدم بعد استفاده از فایلهای پوشه jsm یعنی همون فایلهای import کردنی است.

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

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 مجازی با setInterval:

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

با توجه به حرفهای خود سایت three.js
، من از 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 نیست ولی برای بعضی کارها فکر کنم چیز خوبیه. مثلا هر یکی دو ثانیه یک چیزی را چک کند. برای ساخت بازی خوبه.
 
آخرین ویرایش:

saalek110

Well-Known Member
برنامه دارای قابلیت دریافت مدل:

حالا توانستم در اون زمین چمن خود ، آبجکت ساخته شده خود را بذارم.
اون شی قهوه ای که ترکیب مکعب و کره و چند وجهی بود و طرح آجر داره را من در اون سایت به شکل آنلاین ساختم و اکسپورت اش کردم به دستگاهم دانلود شد.یک فایل با پسوند obj ، سپس در برنامه three.js اون آبجکت را به کار بردم.

خیلی آبجکتهای مجانی زیبا در سایتهای مخصوصش هست که میشه وارد برنامه خودمون بکنیم.

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

من خودم چند دکمه اضافه کردم به برنامه و باهاش در برنامه حرکت می کنم ، احتمالا خود three.js ابزارهای بهتری دارد.

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

قابلیت های برنامه: (آپشن برنامه)
آسمان دارد،

زمین چمن خیلی خیلی بزرگی دارد.
دو تا دیوار آجری دارد. یک بلوک با تسکچر بتون دارد.
دو تا شکل مربع به رنگهای زرد و قرمز دارد.
دو تا درخت با پسوند png دارد که از هر طرف همون شکلی است.
فایل model.obj شما را وارد برنامه می کند.
۵ تا دکمه برای چرخش در زمین دارد. دکمه های عقب رفتن و جلو رفتن ۵ سرعته است.دکمه چرخش به چپ و راست و دکمه استاپ حرکت هم دارد.
 
آخرین ویرایش:

saalek110

Well-Known Member
کد برنامه قبل بزرگ بود در پست جا نشد ، در github زدم. لینک:


دکمه سبز رنگ را بزنید و سپس دانلود زیپ را بزنید، اونجا من آپلود نکردم ، کد را درج کردم خودش زیپ ساخت. خطر ویروس کم است.

حتما هم نباید دانلود کنید ، کد را باز کنید ، خودتان کپی کنید.

من قبل اسم پوشه three.js دو نقطه گذاشتم ، چون برنامه ام داخل یک پوشه بود ، کد زیر:


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

شما تک نقطه بزارید. در این قسمت حتما باید یا تک نقطه بشه یا دو نقطه. خود سایت three.js اینو گفته. بعدش هم نام بسته three.js را من گذاشتم. که خودم قبلا به three-js-master تغییر داده بودم.
 
آخرین ویرایش:

saalek110

Well-Known Member
۵ تکسچر زیر را، بزارید در پوشه textures این پوشه کنار برنامه باشه.
یک مدل بسازید با نام model.obj ، در پوشه model کنار برنامه بزارید.
فایل جوری تنظیم شده که کنار بسته three.js کار کنه. اسم پوشه three.js را من گذاشته ام three-js-master پوشه شما که فرق داره در خطوط اول برنامه ادیت کنید.

بروید سایت بالا یک شکلی بسازید ، اکسپورت کنید به obj یا از هر جایی خواستید فایل obj تهیه کنید با نام model.obj در پوشه model کنار برنامه باشه.


بازدید تاپیک به ۷ هزار رسید.
 

پیوست ها

  • grasslight-big.jpg
    grasslight-big.jpg
    2.5 مگایابت · بازدیدها: 0
  • tree11.png
    tree11.png
    2.2 مگایابت · بازدیدها: 0
  • stone.jpg
    stone.jpg
    19.4 کیلوبایت · بازدیدها: 0
  • sky.jpg
    sky.jpg
    62.7 کیلوبایت · بازدیدها: 0
  • brick_diffuse.jpg
    brick_diffuse.jpg
    1 مگایابت · بازدیدها: 0
آخرین ویرایش:

saalek110

Well-Known Member
نور باعث قابل دیدن شدن اجسام میشه.

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

MeshBasicMaterial
MeshNormalMaterial

بلوکهای برنامه من بیسک متریال است و نور نیاز ندارد، برنامه من اصلا منبع نور ندارد.

یک سری متریال دیگر:


MeshLambertMaterial
MeshPhongMaterial
MeshStandardMaterial
MeshPhysicalMaterial
MeshToonMaterial

اینم فکر کنم هست:
SpriteMaterial
درختهای برنامه من با این متریال ساخته شده.
 
آخرین ویرایش:

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

بالا