make three.js skybox from tilemap

2019-09-06 09:00发布

I can make a three.js skybox using 6 separate image files. But I want to be able to build a skybox from a single image made up of tiles.

For example if I have a composite tiled image as source (see (http://www.zen226082.zen.co.uk/STACK_EXCHANGE/Giant_A.jpg)... i dont have enough rep to post it here :-(.

I can load the composite image into a canvas and copy each tile into a cache buffer and then send them into a Three.js materials array. The materials are then used to make a skybox.

However there are two problems.

Problem 1 is that the composite image needs to be flipped. I can do this easilly in a graphics app like Paint.net but it would be nice to be able to do it programmatically in javascript/three.js.

Problem 2 is that Three.js requires that the tiles for Y+ and Y- also need to be rotated 180 degrees. This is difficult (for me) to do in a graphics application. For example here is a composite tiled image after modification to suit the Three.js code which I have been using (http://www.zen226082.zen.co.uk/STACK_EXCHANGE/Giant_B.jpg) (Horrible!)

My Question = Is there some way that I can perform a 180 degree rotation on the two tiles after extracting them from the composite image and before passing them into the skybox construction.

EDIT (10th August 2014) The Question should be "How to make a three.js skyBox from a single image file made up of 12 equal-sized tiles".

Here is the code I have been using:-

    var skyBoxGeometry = new THREE.CubeGeometry(skybox_sidelength,skybox_sidelength,skybox_sidelength); 
    var skyBoxMaterialArray = [];

    var widthOfOnePiece  = 2048/4;//...i.e. 512
    var heightOfOnePiece = 1536/3;//...i.e. 512
    var numColsToCut = 4;
    var numRowsToCut = 3;

    var image = new Image();
    image.src = "TRI_VP_files/images/SOW_skybox_2048_1536_v7.jpg";
    image.onload = SOW_F_cutImageUp;
    //... NB canvas origin is Top Left corner, X is left to right, Y is top to bottom

    function SOW_F_cutImageUp() {
        var imagePieces = [];

        for(var xxx = 0; xxx < numColsToCut; ++xxx) 
        {
            for(var yyy = 0; yyy < numRowsToCut; ++yyy) 
            {
                var canvas = document.createElement('canvas');
                canvas.width = widthOfOnePiece;
                canvas.height = heightOfOnePiece;
                var context = canvas.getContext('2d');
                context.drawImage(image, 
                xxx * widthOfOnePiece, yyy * heightOfOnePiece, widthOfOnePiece, heightOfOnePiece, 0, 0, canvas.width, canvas.height);

                imagePieces.push(canvas.toDataURL());

            }
        }
        //...  expected sequence of face view directions  = ["xpos", "xneg", "ypos", "yneg", "zpos", "zneg"];

        for (var iii = 0; iii < 6; iii++)
        {
                 if (iii == 0) imagePiece_num = 4 //... xpos
            else if (iii == 1) imagePiece_num = 10//... xneg
            else if (iii == 2) imagePiece_num = 6 //... ypos
            else if (iii == 3) imagePiece_num = 8 //... yneg
            else if (iii == 4) imagePiece_num = 1 //... zpos
            else if (iii == 5) imagePiece_num = 7 //... zneg

            skyBoxMaterialArray.push
            ( new THREE.MeshBasicMaterial
                (   {map: THREE.ImageUtils.loadTexture(imagePieces[imagePiece_num]),
                    side: THREE.BackSide}));

            //window.open(imagePieces[imagePiece_num], "Here is the toDataURL() cached image", "width=512, height=512");
            //alert ("Continue");
        }
    }//... end of function.

    var skyBoxMaterial = new THREE.MeshFaceMaterial( skyBoxMaterialArray );
    var skyBox = new THREE.Mesh( skyBoxGeometry, skyBoxMaterial );
    scene222.add( skyBox );

1条回答
Animai°情兽
2楼-- · 2019-09-06 09:37

The Question should be "How to make a three.js skyBox from a single image file made up of 12 equal-sized tiles".

(This answer achieved with greatly appreciated assistance from WestLangley).

Some key points:-

  • Assume the tiles in the source image are arranged and referenced thus:-

    //... Personal             Source image               Desired in-scene        Required
    //... tile numbering       [column, row]              xyz positions of 6      sequence numbers in 
    //... scheme               tile coordinates            tiles to be used.      Three.js material array 
    
    //... [ 0] [ 3] [ 6] [ 9]  [0,0] [1,0] [2,0] [3,0]   [  ] [py] [  ] [  ]      [ ] [2] [ ] [ ]     
    //... [ 1] [ 4] [ 7] [10]  [0,1] [1,1] [2,1] [3,1]   [nx] [pz] [px] [nz]      [1] [4] [0] [5]     
    //... [ 2] [ 5] [ 8] [11]  [0,2] [1,2] [2,2] [3,2]   [  ] [ny] [  ] [  ]      [ ] [3] [ ] [ ]     
    
  • The tile at the centre of the "cross" corresponds to the view looking from the skyBox origin in the Z_positive direction.

  • (That makes spinning by 180 degrees unneccessary).

  • In definition of material use... side: THREE.FrontSide

  • To prevent mirror images of the source tiles, "flip" them using... skyBox.scale.set( -1, 1, 1 )

  • To ensure that images are fully processed the function F_cutImageUp extends to include creation of the skyBox mesh

  • This works for the current latest Three.js release (R.68).

  • This code does not use MeshShaderMaterial, more powerful (but demanding) WebGL code example is available at http://threejs.org/examples/webgl_shaders_ocean.html

  • The webgl_shaders_ocean.html code example also includes a more efficient tile-cutting and sequencing algorithm.

SkyBox Generation Code (to be inserted in the THREE.js Initialisation function).

var skybox_sidelength = 10000;
var skyBoxGeometry = new THREE.BoxGeometry( skybox_sidelength, skybox_sidelength, skybox_sidelength); 

var skyBoxMaterialArray = [];

var numCols = 4, numRows = 3; //... assume any source image is tiled 4 columns(x) by 3 rows(y)
//... NB canvas origin is Top Left corner, X is left to right, Y is top to bottom

//... We use the following mapping scheme to reference the tiles in the source image:-
//...
//... Personal             [x,y] tile coordinates    xyz positions            Required tile       
//... tile numbering                                 of tiles in scene        sequence in Three.js 
//...                                                                         array 

//... [ 0] [ 3] [ 6] [ 9]  [0,0] [1,0] [2,0] [3,0]   [  ] [py] [  ] [  ]      [ ] [2] [ ] [ ]     
//... [ 1] [ 4] [ 7] [10]  [0,1] [1,1] [2,1] [3,1]   [nx] [pz] [px] [nz]      [1] [4] [0] [5]     
//... [ 2] [ 5] [ 8] [11]  [0,2] [1,2] [2,2] [3,2]   [  ] [ny] [  ] [  ]      [ ] [3] [ ] [ ]                           

var image_file = "3D_Skybox_files/Giant_A.jpg", tile_width = 512, tile_height = 512;    
var IP_image = new Image();
IP_image.onload = F_cutImageUp; 
IP_image.src = image_file; //... horizontal cross of 6 WYSIWYG tiles in a 4x3 = 12 tile layout.

function F_cutImageUp() 
{ //... cut up source image into 12 separate image tiles, numbered 0..11

    var imagePieces = [];
    var item_num = -1;

    for(var xxx = 0; xxx < numCols; ++xxx) 
    {
        for(var yyy = 0; yyy < numRows; ++yyy) 
        {
            var tileCanvas = document.createElement('canvas');
            tileCanvas.width  = tileWidth;
            tileCanvas.height = tileHeight;

            var tileContext = tileCanvas.getContext('2d');

            tileContext.drawImage( 
                IP_image, 
                xxx * tileWidth, yyy * tileHeight, 
                tileWidth, tileHeight, 
                0, 0, tileCanvas.width, tileCanvas.height);

            imagePieces.push(tileCanvas.toDataURL());

        }
    }

    //... Required sequence of tile view directions  = ["xpos", "xneg", "ypos", "yneg", "zpos", "zneg"];

    for (var iii = 0; iii < 6; iii++) //... select the right tiles for the 6 different faces of the sky box
    {

        //... we associate the centre tile (4) of the cross with the zpos direction
             if (iii == 0) imagePiece_num =  7;//... xpos
        else if (iii == 1) imagePiece_num =  1;//... xneg
        else if (iii == 2) imagePiece_num =  3;//... ypos
        else if (iii == 3) imagePiece_num =  5;//... yneg                                       
        else if (iii == 4) imagePiece_num =  4;//... zpos
        else if (iii == 5) imagePiece_num = 10;//... zneg

        skyBoxMaterialArray.push
        ( new THREE.MeshBasicMaterial
            ({      map: THREE.ImageUtils.loadTexture(imagePieces[imagePiece_num]),
                    side: THREE.FrontSide // <== not intuitive
            })
        );

        //... Just for checking image pieces are created OK
        //window.open(imagePieces[imagePiece_num], "Here is the toDataURL() cached image " + //imagePiece_num, "width=512, height=512");
        //alert ("Displayed imagePiece_num: " + imagePiece_num);

    }  //... end of tile selection

var skyBoxMaterial = new THREE.MeshFaceMaterial( skyBoxMaterialArray );                         
var skyBox = new THREE.Mesh( skyBoxGeometry, skyBoxMaterial );
skyBox.scale.set( -1, 1, 1 ); // <== not intuitive
scene.add( skyBox ); 

}//... end of F_cutImageUp function <== note function includes skyBox mesh creation.
查看更多
登录 后发表回答