In order to achieve a full three-dimensional 3D panorama effect, we use Krpano
software to Ordinary fisheye pictures are rendered into 720° panoramas
Note: The code has been adjusted and cannot be guaranteed to run. It mainly explains the implementation ideas.
First download the software Krpano panorama generation software, which includes the Linux
version and the Win
version as well as a simple user manual file.
In fact, it only takes two simple steps to use. The first step is to generate the images needed to display the panorama from the uploaded images. The second step is to display the panorama according to the panorama display rules and configuration files.
[Related recommendations: The latest 10 thinkphp video tutorials]
Upload pictures and generate panoramas
The principle is very Simple, upload the pictures to the server, then use the Krpano
software to generate panoramic images from the server, and transfer the generated pictures to a unified directory.
Before you start uploading images, you need to modify the configuration file of Krpano
Krpano/templates/normal.config
as follows:
# krpano 1.19 # 引入基本设置 include basicsettings.config # 全景图类型 自动 如果可以识别自动,不能识别图片会询问处理方法 panotype=autodetect hfov=360 # 输出设置 flash=true html5=true # convert spherical/cylindrical to cubical converttocube=true converttocubelimit=360x45 # multiresolution settings multires=true maxsize=8000 maxcubesize=2048 # 输出图片路径 tilepath=%INPUTPATH%/pano/%BASENAME%.tbs-pano/3d-pano-[c].jpg # 输出预览图图片设置 preview=true graypreview=false previewsmooth=25 previewpath=%INPUTPATH%/pano/%BASENAME%.tbs-pano/3d-pano-preview.jpg # 输出缩略图图片设置 makethumb=true thumbsize=240 thumbpath=%INPUTPATH%/pano/%BASENAME%.tbs-pano/3d-pano-thumb.jpg
The upload interface code is as follows :
public function upload_3d_pic() { $file = $_FILES["imgUpload"]; $u_name =$file['name']; $u_temp_name =$file['tmp_name']; $u_size =$file['size']; // 生成 一个随机字符串 $str = null; $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123tbs456789abcdefghijklmnopqrstuvwxyz"; $max = strlen($strPol)-1; for($i=0;$i<$length;$i++){ $str.=$strPol[rand(0,$max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数 } //$md5Code 会做为文件夹的名字 跟文件的名字,要保持唯一性 $md5Code =md5_16bit(hash("sha256",$u_name.time().$rand_char)).$str; $datePath =date("Y-m-d",time()); $root_path ='./upload_3dpic/'; $url_path ='/upload_3dpic/'; //外部访问url $f_up_to_path =$root_path .'/'. $datePath.'/'.$md5Code; if(!file_exists($f_up_to_path)){ mkdir($f_up_to_path, 0777, true); } $type = strtolower(substr($u_name, strrpos($u_name, '.') + 1)); $img_file_name =$md5Code."." . $type; $saveFileName = $f_up_to_path."." . $type; $true_img_url =$url_path . $datePath.'/'.$md5Code."." . $type; //外部访问链接 if (!move_uploaded_file($u_temp_name, $saveFileName)) { $this->ajaxReturn(array("error_code"=>250,"msg"=>"图片上传失败,请稍后重试!","return"=>"move pic fail>>temp_name=".$u_temp_name.">>save file name=".$saveFileName)); } else { @rmdir($f_up_to_path); } //判断文件是否存在 if(file_exists($saveFileName)){ //如果存在 则生成 全景图 $this->create_pano_pic($saveFileName); // 如果 此时没有生成图片 需要删除上传图片并报错 平面图可能生成不了图片 $dirName = dirname($saveFileName) . '/pano' . '/' . $md5Code . '.tbs-pano'; if ( !file_exists($dirName) ) { unlink($saveFileName); // 删除文件 $this->ajaxReturn(array('error_code'=>250,"msg"=>"上传图片不能生成全景图")); } //移动全景图到指定的目录 图片在哪里全景图将会生成在那个目录 $mvres = $this->mv_to_pano_path($saveFileName,$img_file_name); if ( $mvres === false ) { $this->ajaxReturn(array('error_code'=>250,"msg"=>"移动文件失败")); } }else{ $this->ajaxReturn(array('error_code'=>250,"msg"=>"img not exists!",'img_url'=>$true_img_url)); } // 移动后的缩略图路径 $thumb_url = $url_path . 'TreeDPic/' . $md5Code . '/pano/' . $md5Code . '.tbs-pano/3d-pano-thumb.jpg'; $this->ajaxReturn(array( 'error_code'=>0, 'msg'=>"sucess", 'img_url'=>$true_img_url, "pano_name"=>$md5Code, 'thumb_url'=>$thumb_url) ); } /*** * @param string $img_path * @return string * 将当前传入的图片 渲染成为全景图 */ private function create_pano_pic($img_path="") { if(empty($img_path)){ return $img_path; } if(!file_exists($img_path)){ return "图片不存在!"; } //软件注册码 $r_code ="Krpano的注册码"; $pano_path=C("KRPANO_PATH"); //krpano 路径 自己配置 $pano_tools ="krpanotools"; //krpano 生成图片的命令 $dealFlat = ''; // 处理 非球面图 if(PHP_OS == 'WINNT'){ $pano_path=$pano_path."Win"; $pano_tools ="krpanotools32.exe"; } else { // 上传平面图时 直接跳过图片生成 否则会一直等待 $dealFlat = 'echo -e "0\n" | '; } $kr_command = $dealFlat . $pano_path . "/".$pano_tools." makepano -config=" . $pano_path . "/templates/normal.config "; try{ //在生成图片之前 先注册一下码,要不生成的全景图会有水印 exec( $pano_path . '/'.$pano_tools.' register ' .$r_code); $kr_command =$kr_command.$img_path; //执行生成图片命令 exec($kr_command, $log, $status); } catch (\Exception $e){ $this->ajaxCallMsg(250,$e->getMessage()); } return true; } /** * @param $pano_img_path * @return string * 全景图生成后再调用这个方法,把全景图移到对应的目录供 xml 文件获取内容 */ private function mv_to_pano_path($pano_img_path,$img_name){ $ig_name =explode(".",$img_name)[0]; $root_path = './upload_3dpic/'; if(!file_exists($pano_img_path) ||empty($pano_img_path)){ $this->up_error_log($pano_img_path.'》》图片路径文件不存在'); return ''; } $now_path =dirname($pano_img_path);//获取当前文件目录 if ($dh = @opendir($now_path)){ //打开目录 while (($file = readdir($dh)) !== false){ //循环获取目录的 文件 if (($file != '.') && ($file != '..')) { //如果文件不是.. 或 . 则就是真实的文件 if($file=="pano"){ //全景图切片目录 $t_d_path =$root_path .'TreeDPic/'. $ig_name; if(!file_exists($t_d_path)){ //不存在就创建 @mkdir($t_d_path, 0777, true); } if(file_exists($t_d_path.'/'.$file)){ //判断是否已经存在 当前名字的 全景图 文件 return false; }else{ //否则就 把 当前上传的生成 的全景文件切片,移动到指定的目录 rename($now_path.'/'.$file,$t_d_path.'/'.$file); } }else if ($file !==$img_name){ //删除不是 原图片的文件 if(is_dir($file)){ $this->deleteDir($now_path.'/'.$file); }else{ @unlink($now_path.'/'.$file); } }else{ return false; } } } closedir($dh); }else{ return false; } } /** * @param $dir * @return bool * 删除文件夹及文件 */ private function deleteDir($dir) { if (!$handle = @opendir($dir)) { return false; } while (false !== ($file = readdir($handle))) { if ($file !== "." && $file !== "..") { //排除当前目录与父级目录 $file = $dir . '/' . $file; if (is_dir($file)) { $this->deleteDir($file); } else { @unlink($file); } } } @rmdir($dir); }
At this point, we can already upload images through the upload interface and render the images into panoramas through Krpano
.
Display panorama
To display the image, we must generate the necessaryxml
configuration file according to theKrpano
rules.
We will generate a panorama based on the unique code generated from the uploaded image.
// 解析XML文件 public function panorama_xml(){ $code =I("code"); $cutNum =intval(I("cutNum")); $url_path = '/upload_3dpic/'; // 切割模式分为 6图 和 12图 if(!in_array($cutNum,array(6,12))){ $this->error(); } $this->echoSixXml($url_path,$code); } private function echoSixXml($url_path,$code=""){ echo "<krpano version=\"1.19\" title=\"Virtual Tour\"> <!-- the skin --> <!-- <include url=\"/3dpic/pano/sixDefaultXml/\" />--> <!-- 视图设置 <view hlookat=\"0\" vlookat=\"0\" maxpixelzoom=\"1.0\" fovmax=\"150\" limitview=\"auto\" /> --> <skin_settings maps=\"false\" maps_type=\"google\" maps_bing_api_key=\"\" maps_google_api_key=\"\" maps_zoombuttons=\"false\" gyro=\"true\" webvr=\"true\" webvr_gyro_keeplookingdirection=\"false\" webvr_prev_next_hotspots=\"true\" littleplanetintro=\"false\" title=\"true\" thumbs=\"true\" thumbs_width=\"120\" thumbs_height=\"80\" thumbs_padding=\"10\" thumbs_crop=\"0|40|240|160\" thumbs_opened=\"false\" thumbs_text=\"false\" thumbs_dragging=\"true\" thumbs_onhoverscrolling=\"false\" thumbs_scrollbuttons=\"false\" thumbs_scrollindicator=\"false\" thumbs_loop=\"false\" tooltips_buttons=\"false\" tooltips_thumbs=\"false\" tooltips_hotspots=\"false\" tooltips_mapspots=\"false\" deeplinking=\"false\" loadscene_flags=\"MERGE\" loadscene_blend=\"OPENBLEND(0.5, 0.0, 0.75, 0.05, linear)\" loadscene_blend_prev=\"SLIDEBLEND(0.5, 180, 0.75, linear)\" loadscene_blend_next=\"SLIDEBLEND(0.5, 0, 0.75, linear)\" loadingtext=\"loading...\" layout_width=\"100%\" layout_maxwidth=\"814\" controlbar_width=\"-24\" controlbar_height=\"40\" controlbar_offset=\"20\" controlbar_offset_closed=\"-40\" controlbar_overlap.no-fractionalscaling=\"10\" controlbar_overlap.fractionalscaling=\"0\" design_skin_images=\"vtourskin.png\" design_bgcolor=\"0x2D3E50\" design_bgalpha=\"0.8\" design_bgborder=\"0\" design_bgroundedge=\"1\" design_bgshadow=\"0 4 10 0x000000 0.3\" design_thumbborder_bgborder=\"3 0xFFFFFF 1.0\" design_thumbborder_padding=\"2\" design_thumbborder_bgroundedge=\"0\" design_text_css=\"color:#FFFFFF; font-family:Arial;\" design_text_shadow=\"1\" /> <scene name=\"{$code}\" title=\"{$code}\" onstart=\"\" thumburl=\"{$url_path}TreeDPic/{$code}/pano/{$code}.tbs-pano/3d-pano-thumb.jpg\" lat=\"\" lng=\"\" heading=\"\"> <view hlookat=\"0.0\" vlookat=\"0.0\" fovtype=\"MFOV\" fov=\"120\" maxpixelzoom=\"2.0\" fovmin=\"70\" fovmax=\"140\" limitview=\"range\" vlookatmin=\"-58.156\" vlookatmax=\"58.156\" /> <preview url=\"{$url_path}TreeDPic/{$code}/pano/{$code}.tbs-pano/3d-pano-preview.jpg\" /> <image type=\"CUBE\" multires=\"true\" tilesize=\"512\"> <cube url=\"{$url_path}TreeDPic/{$code}/pano/{$code}.tbs-pano/3d-pano-%s.jpg\" /> </image> </scene> <!--<preview url=\"{$url_path}TreeDPic/{$code}/pano/{$code}.tbs-pano/preview.jpg\" />--> <image> <cube url=\"{$url_path}TreeDPic/{$code}/pano/{$code}.tbs-pano/3d-pano-%s.jpg\" /> </image> </krpano>"; }
scene
does not render the current rendering, but when we choose between multiple panoramas, DOM.call("toggle_item_hotspots ();");
Automatically triggered.
Set the route and method of displaying the page:
public function panorama(){ //先 获取id (md5值) $code =trim(I("code")); //图片切割方式 6图(采集的是6图) 和12图(比较复杂建议生成图片 用6图 配置切割) $cutNum =intval(I("cutNum")); $this->assign("codeVal",$code); $this->assign("cutNum",$cutNum); $this->display(); }
In the corresponding view file:
<!DOCTYPE html> <html> <head> <title>土拨鼠全景漫游图 - {$pageData.title}</title> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, viewport-fit=cover" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black" /> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <meta http-equiv="x-ua-compatible" content="IE=edge" /> <link rel="stylesheet" href="{$Think.TBS_STATIC}/common/css/new_base.css?v=1560493706" /> <link rel="stylesheet" href="/res/impression/vtour/pc/krpano.css"/> <style> @-ms-viewport { width:device-width; } @media only screen and (min-device-width:800px) { html { overflow:hidden; } } html { height:100%; } body { height:100%; overflow:hidden; margin:0; padding:0; font-family:Arial, Helvetica, sans-serif; font-size:16px; color:#FFFFFF; background-color:#000000; } .loading{ /* display: none; */ width: 100%; height: 100%; position: absolute; top: 0; left: 0; z-index: 3; background-color: #fff; color:#333; z-index: 100; } .loadingimg { width: 184px; height: 108px; position: absolute; top: 50%; left: 50%; -webkit-transform: translateX(-50%) translateY(-50%); -moz-transform: translateX(-50%) translateY(-50%); -ms-transform: translateX(-50%) translateY(-50%); transform: translateX(-50%) translateY(-50%); text-align: center; } .loadingimg img { width: 100%; height: 100%; } .poiner { display: inline-block; width: 16px; vertical-align: bottom; overflow: hidden; /* animation: poiner 3s infinite step-start; */ } </style> </head> <body> <script src="vtour/tour.js"></script> <p class="loading"> <p class="loadingimg"> <img src="{$Think.TBS_STATIC}/impression/vtour/img/loading.png"> <p>加载中</p> </p> </p> <p id="pano" style="width:100%;height:100%;"> </p> </body> <script> // var krpano = null; embedpano({ swf: "{$Think.TBS_STATIC}/impression/vtour/tour.swf?v={$Think.CDNTIME}", xml: "/3dpic/panoxml/{$cutNum}_{$codeVal}", target: "pano", html5: "auto", mobilescale: 1.0, passQueryParameters: true, }); </script> <script type="text/javascript" src="vtour/krpano.js"></script> </html>
Modify the path of the corresponding static resource file to suit your project. At this time Our panorama can already be seen.