天天看點

threeJS 雙場景雙相機建立立方體環境貼圖和全景貼圖

本例來源于官方案例

效果圖

立方體貼圖

threeJS 雙場景雙相機建立立方體環境貼圖和全景貼圖

全景貼圖

threeJS 雙場景雙相機建立立方體環境貼圖和全景貼圖

總體步驟

① 建立場景和相機
②建立物體
③ 建立渲染器
④建立GUI
           

html代碼

<!DOCTYPE html>
<html >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        html,body{
            padding: 0;
            margin: 0;
            overflow: hidden;
        }

    </style>
</head>
<body>
<div id="container">

</div>
<script type="module">


</script>
</body>
</html>

           

導入檔案,建立全局變量和函數

import * as THREE from "./js/three.module.js";
    import {OrbitControls} from "./js/OrbitControls.js";
    import { GUI } from './js/dat.gui.module.js';
    import Stats from './js/stats.module.js';
  
 let renderer,scene,camera,spotLight,status,cameraCube,sceneCube,cubeMesh,SphereMaterial;

    action();
    function action() {
        onload();
        run();
    }
      function onload(){

     }
     function run()
     {
     }
     function onResize()
     {
     }

           

①建立場景和相機

onload函數中

let container=document.getElementById("container");//擷取container

        scene=new THREE.Scene();//建立場景(主)
        camera=new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,1,10000);
        camera.position.set(0, 0, 1000);//建立相機并設定位置


        sceneCube = new THREE.Scene();//建立立方體場景(副)
        cameraCube=new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,1,10000);
           

②建立物體

onload函數中

let ambientLight=new THREE.AmbientLight();//添加環境光
        scene.add(ambientLight);

        spotLight=new THREE.SpotLight(0xffffff);//添加點光源
        spotLight.position.set(-600, 300,300);
        spotLight.intensity=3;
        scene.add(spotLight);
        let spotLightMesh=new THREE.Mesh(new THREE.SphereGeometry(15,20,20),new THREE.MeshBasicMaterial());
        spotLight.add(spotLightMesh);

        let gridHelper=new THREE.GridHelper(30,20);
        scene.add(gridHelper);

        //立方體貼圖
        let textureCube=new THREE.CubeTextureLoader().setPath("img/").load(["posx.jpg", "negx.jpg","posy.jpg","negy.jpg","posz.jpg","negz.jpg"]);
        textureCube.encoding=THREE.sRGBEncoding;//在導入材質時,會預設将貼圖編碼格式定義為Three.LinearEncoding,故需将帶顔色資訊的貼圖(baseColorTexture, emissiveTexture, 和 specularGlossinessTexture)手動指定為Three.sRGBEncoding
        textureCube.mapping=THREE.CubeReflectionMapping;//立方體反映射①
        textureCube.format=THREE.RGBFormat;//預設THREE.RGBAFormat,對于JPG會自動設定為THREE.RGBFormat ②
       // console.log(textureCube);

        let SphereGeometry=new THREE.SphereBufferGeometry(300.0, 48, 24);
            SphereMaterial=new THREE.MeshLambertMaterial({envMap:textureCube});
        let SphereMesh=new THREE.Mesh(SphereGeometry,SphereMaterial);
        scene.add(SphereMesh);

        let cubeShader=THREE.ShaderLib["cube"];
        console.log(THREE.ShaderLib);
        let cubeMaterial=new THREE.ShaderMaterial({
               fragmentShader:cubeShader.fragmentShader,
               vertexShader:cubeShader.vertexShader,
               uniforms:cubeShader.uniforms,
               depthWrite:false,
               side: THREE.BackSide
        });
        cubeMaterial.uniforms["tCube"].value=textureCube;
        console.log(cubeMaterial);

        //全景貼圖
        let panorama=new THREE.TextureLoader().load("./img/2294472375_24a3b8ef46_o.jpg");
        panorama.mapping = THREE.EquirectangularReflectionMapping;
        panorama.magFilter = THREE.LinearFilter;//③
        panorama.minFilter = THREE.LinearMipmapLinearFilter;//④
        panorama.encoding = THREE.sRGBEncoding;
        let panoramaShader=THREE.ShaderLib["equirect"];
        let panoramaMaterial=new THREE.ShaderMaterial({
            fragmentShader:panoramaShader.fragmentShader,
            vertexShader: panoramaShader.vertexShader,
            uniforms:panoramaShader.uniforms,
            depthWrite: false,
            side: THREE.BackSide
        });
        panoramaMaterial.uniforms["tEquirect"].value=panorama;


        cubeMesh=new THREE.Mesh(new THREE.BoxBufferGeometry( 10, 100, 100 ),cubeMaterial);
        sceneCube.add(cubeMesh);
           

解釋

①:textureCube.mapping:就是UV貼圖的類型,它的取值如下
 THREE.CubeReflectionMapping:立方體反射映射
 THREE.CubeRefractionMapping:立方體折射映射
 THREE.EquirectangularReflectionMapping:圓柱反射映射
 THREE.EquirectangularRefractionMapping:圓柱折射映射
 THREE.SphericalReflectionMapping:球面反t射映射
 ② :textureCube.format
 預設紋理格式為THREE.RGBAFormat。其他格式有:
THREE.AlphaFormat:對應于GL_ALPHA。Alpha 值
THREE.RGBFormat:Red, Green, Blue 三原色值
THREE.RGBAFormat:Red, Green, Blue 和 Alpha 值
THREE.LuminanceFormat:灰階值
THREE.LuminanceAlphaFormat:灰階值和 Alpha 值
THREE.RGBEFormat

③ panorama.magFilter
該屬性定義當一個紋理單元(texel)覆寫多個像素點時紋理如何采樣。預設為 THREE.LinearFilter,表示擷取4個最近的紋理單元執行雙向線性插值計算(顯示效果好)。另外的選項是 THREE.NearestFilter, 表示使用最近的texel(性能優)

④:panorama.minFilter
該屬性定義當一個紋理單元(texel)不足以覆寫單個像素點時紋理如何采樣。預設為 THREE.LinearMipMapLinearFilter, 表示使用多級紋理貼圖(mipmapping)以及一個三線性濾波器。
其他選項是:
THREE.NearestFilter:最近濾鏡。在紋理基層上執行最鄰近過濾。
THREE.NearestMipMapNearestFilter:選擇最臨近的mip層,并執行最臨近的過濾。
THREE.NearestMipMapLinearFilter:在mip層之間執行線性插補,并執行最臨近的過濾。
THREE.LinearFilter:在紋理基層上執行線性過濾。
THREE.LinearMipMapNearestFilter:選擇最臨近的mip層,并執行線性過濾。
THREE.LinearMipMapLinearFilter:在mip層之間執行線性插補,并執行線性過濾。

           

參考部落格 Three.js Texture紋理屬性詳解

THREE.ShaderMaterial 參考部落格Three.js進階材質THREE.ShaderMaterial

③建立渲染器

onload函數中

renderer=new THREE.WebGLRenderer({antialias:true});
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.autoClear=false;
        renderer.gammaInput=true;
        renderer.gammaOutput=true;//inear轉gamma
        renderer.setSize(window.innerWidth,window.innerHeight);
        container.appendChild(renderer.domElement);
         status=new Stats();//建立頻率顯示
        container.appendChild(status.dom);//頻率挂到左上角
        let contorl=new OrbitControls(camera,renderer.domElement);//添加滑鼠滾動縮放,旋轉對象

        window.addEventListener('resize',onResize,false);//浏覽器大小改變監聽
           

onResize函數中

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

        cameraCube.aspect = window.innerWidth / window.innerHeight;
        cameraCube.updateProjectionMatrix();
        renderer.setSize(window.innerWidth,window.innerHeight);
    }
           

run函數中

function run() {
        requestAnimationFrame(run);
        camera.lookAt(scene.position);
        cameraCube.rotation.copy(camera.rotation);//指派相機的位置
        renderer.render(sceneCube,cameraCube);
        renderer.render(scene,camera);
        status.update();

    }
           

④建立GUI

onload函數中

let guiControls=new function(){
            this.Cube=function () {
                cubeMesh.material = cubeMaterial;
                cubeMesh.visible = true;
                SphereMaterial.envMap = textureCube;
                SphereMaterial.needsUpdate = true;
            };
           this.Equirectangular=function () {
                cubeMesh.material = panoramaMaterial;
                cubeMesh.visible = true;
                SphereMaterial.envMap=panorama;
                SphereMaterial.needsUpdate = true;
            }
        };

        let gui=new GUI();//建立gui
        gui.add(guiControls,'Cube');
        gui.add(guiControls,'Equirectangular');

           

總代碼

<!DOCTYPE html>
<html >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        html,body{
            padding: 0;
            margin: 0;
            overflow: hidden;
        }

    </style>
</head>
<body>
<div id="container">

</div>


<script type="module">
    import * as THREE from "./js/three.module.js";
    import {OrbitControls} from "./js/OrbitControls.js";
    import { GUI } from './js/dat.gui.module.js';
    import Stats from './js/stats.module.js';
    let renderer,scene,camera,spotLight,status,cameraCube,sceneCube,cubeMesh,SphereMaterial;

    action();
    function action() {
        onload();
        run();
    }
    function onload() {
        let container=document.getElementById("container");//擷取container

        scene=new THREE.Scene();//建立場景
        camera=new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,1,10000);
        camera.position.set(0, 0, 1000);//建立相機并設定位置


        sceneCube = new THREE.Scene();
        cameraCube=new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,1,10000);



        let ambientLight=new THREE.AmbientLight();//添加環境光
        scene.add(ambientLight);

        spotLight=new THREE.SpotLight(0xffffff);//添加點光源
        spotLight.position.set(-600, 300,300);
        spotLight.intensity=3;
        scene.add(spotLight);
        let spotLightMesh=new THREE.Mesh(new THREE.SphereGeometry(15,20,20),new THREE.MeshBasicMaterial());
        spotLight.add(spotLightMesh);

        let gridHelper=new THREE.GridHelper(30,20);
        scene.add(gridHelper);

        //立方體貼圖
        let textureCube=new THREE.CubeTextureLoader().setPath("img/").load(["posx.jpg", "negx.jpg","posy.jpg","negy.jpg","posz.jpg","negz.jpg"]);
        textureCube.encoding=THREE.sRGBEncoding;//在導入材質時,會預設将貼圖編碼格式定義為Three.LinearEncoding,故需将帶顔色資訊的貼圖(baseColorTexture, emissiveTexture, 和 specularGlossinessTexture)手動指定為Three.sRGBEncoding
        textureCube.mapping=THREE.CubeReflectionMapping;//立方體反映射
        textureCube.format=THREE.RGBFormat;//預設THREE.RGBAFormat,對于JPG會自動設定為THREE.RGBFormat
        console.log(textureCube);

        let SphereGeometry=new THREE.SphereBufferGeometry(300.0, 48, 24);
            SphereMaterial=new THREE.MeshLambertMaterial({envMap:textureCube});
        let SphereMesh=new THREE.Mesh(SphereGeometry,SphereMaterial);
        scene.add(SphereMesh);

        let cubeShader=THREE.ShaderLib["cube"];
        console.log(THREE.ShaderLib);
        let cubeMaterial=new THREE.ShaderMaterial({
               fragmentShader:cubeShader.fragmentShader,
               vertexShader:cubeShader.vertexShader,
               uniforms:cubeShader.uniforms,
               depthWrite:false,
               side: THREE.BackSide
        });
        cubeMaterial.uniforms["tCube"].value=textureCube;
        console.log(cubeMaterial);

        //全景貼圖
        let panorama=new THREE.TextureLoader().load("./img/2294472375_24a3b8ef46_o.jpg");
        panorama.mapping = THREE.EquirectangularReflectionMapping;
        panorama.magFilter = THREE.LinearFilter;
        panorama.minFilter = THREE.LinearMipmapLinearFilter;
        panorama.encoding = THREE.sRGBEncoding;
        let panoramaShader=THREE.ShaderLib["equirect"];
        let panoramaMaterial=new THREE.ShaderMaterial({
            fragmentShader:panoramaShader.fragmentShader,
            vertexShader: panoramaShader.vertexShader,
            uniforms:panoramaShader.uniforms,
            depthWrite: false,
            side: THREE.BackSide
        });
        panoramaMaterial.uniforms["tEquirect"].value=panorama;


        cubeMesh=new THREE.Mesh(new THREE.BoxBufferGeometry( 10, 100, 100 ),cubeMaterial);
        sceneCube.add(cubeMesh);



        renderer=new THREE.WebGLRenderer({antialias:true});
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.autoClear=false;
        renderer.gammaInput=true;
        renderer.gammaOutput=true;//inear轉gamma
        renderer.setSize(window.innerWidth,window.innerHeight);
        container.appendChild(renderer.domElement);

        let guiControls=new function(){
            this.Cube=function () {
                cubeMesh.material = cubeMaterial;
                cubeMesh.visible = true;
                SphereMaterial.envMap = textureCube;
                SphereMaterial.needsUpdate = true;
            };
           this.Equirectangular=function () {
                cubeMesh.material = panoramaMaterial;
                cubeMesh.visible = true;
                SphereMaterial.envMap=panorama;
                SphereMaterial.needsUpdate = true;
            }
        };

        let gui=new GUI();//建立gui
        gui.add(guiControls,'Cube');
        gui.add(guiControls,'Equirectangular');

        status=new Stats();//建立頻率顯示
        container.appendChild(status.dom);//頻率挂到左上角
        let contorl=new OrbitControls(camera,renderer.domElement);//添加滑鼠滾動縮放,旋轉對象

        window.addEventListener('resize',onResize,false);//浏覽器大小改變監聽
    }
    function onResize() {
        camera.aspect=window.innerWidth/window.innerHeight;
        camera.updateProjectionMatrix();

        cameraCube.aspect = window.innerWidth / window.innerHeight;
        cameraCube.updateProjectionMatrix();
        renderer.setSize(window.innerWidth,window.innerHeight);
    }

    function run() {
        requestAnimationFrame(run);
        camera.lookAt(scene.position);
        cameraCube.rotation.copy(camera.rotation);//指派相機的位置
        renderer.render(sceneCube,cameraCube);
        renderer.render(scene,camera);
        status.update();

    }


</script>
</body>
</html>
           

繼續閱讀