【图形处理】如何将一个平面图形按照角度往z轴扭曲?

来源:互联网 发布:与权志龙合照软件 编辑:程序博客网 时间:2024/05/17 03:44

前言

最近希望可以做一个小小的2.5d游戏,里面有一个场景有意思,
当卡丁车往路上跑的时候,路面要显示成:
这里写图片描述
而原本的图片素材类似于:
这里写图片描述
这个是很明显需要将平面往z轴扭曲一定角度,但是目前阶段在网上找不到html5 canvas的解决方案,没办法,只好重新操刀用切片法和投影方法来模拟这种图案了。

原理讲解

好吧。。我画了一张图,粗浅表示出肉眼看到的图形经过一定角度按照angle来旋转会出现什么形状,最后其实要求的是一个梯形。
这里写图片描述

注意,后面的实现我没有计算梯形–因为梯形只有三条边是没办法计算的,我索性按照肉眼高度和原始高度的比例来作为相对处理

demo实现

有了算法和计算过程以后,那么按照切片方式很容易实现的。

<%@ page contentType="text/html;charset=UTF-8" language="java" %><!DOCTYPE html><html><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"/>  <meta name="apple-mobile-web-app-capable" content="yes"/>  <meta name="apple-mobile-web-app-status-bar-style" content="black">  <meta content="telephone=no,email=no" name="format-detection">  <meta name="full-screen" content="true"/>  <%--竖屏--%>  <%--<meta name="screen-orientation" content="portrait"/>--%>  <%--横屏--%>  <%--<meta name="screen-orientation" content="landscape"/>--%>  <meta name="x5-fullscreen" content="true"/>  <meta name="360-fullscreen" content="true"/>  <style>    body{      padding: 0;      margin: 0;    }  </style></head><body><div class="">  <img src="images/forestRoad.jpg" id="image" >  <img src="" id="preview"></div><canvas width="600" height="600" id="canvas"></canvas>  <script>    var canvas=document.getElementById("canvas");    var ctx=canvas.getContext("2d");    var IMAGE=document.getElementById("image");    var PREVIEW=document.getElementById("preview");    IMAGE.onload=function(){      canvas.widthj=IMAGE.width;      canvas.height=IMAGE.height;    }//    ctx.drawImage(IMAGE);    //--这是切片方式来生成图片。--up down left right ,请注意 angle的意思是,angle=0的时候是不需要切片生成图片的,因为angle=0表示从正上方看图片的角度。    //angle=90的是后表示图片刚好垂直于眼神--即是说,这时候眼睛看到的是一条直线--尽量不要用到angle=90的情况。    //--用angle以后就是计算三角形的坐标了。    function transform3dImage(image,segNum,angle) {      var tmpCanvas = document.createElement("canvas");      var tmpCtx = tmpCanvas.getContext("2d");      var _img_width = image.naturalWidth;      var _img_height = image.naturalHeight;      //--计算实际的高度和宽度。      var _real_height = parseInt(_img_height * Math.cos(angle));      //--好了,已经算出了投影过去的高度,那么可以知道投影和原始图形之间的比例。      //--当然,这个比例其实就是cos(angle),就是说,没什么可以计算的,      //那么现在可以计算出最小的宽度了。      var _min_width = parseInt(_img_width * Math.cos(angle));      //根据这一个已经可以得到切片相关图形了。      tmpCanvas.width = _img_width;      tmpCanvas.height = _real_height;//      context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);//      参数值//      参数  描述//      img 规定要使用的图像、画布或视频。//sx    可选。开始剪切的 x 坐标位置。//sy    可选。开始剪切的 y 坐标位置。//swidth    可选。被剪切图像的宽度。//sheight   可选。被剪切图像的高度。//x 在画布上放置图像的 x 坐标位置。//y 在画布上放置图像的 y 坐标位置。//width 可选。要使用的图像的宽度。(伸展或缩小图像)//height    可选。要使用的图像的高度。(伸展或缩小图像)      var _seg_width = ((_img_width - _min_width) / segNum);      var _seg_height = ((_real_height) / segNum);      var _src_seg_height = (_img_height) / segNum;      console.log("当前图片尺寸:" + _img_width + " x " + _img_height + ", 投影后尺寸:最小宽度:" + _min_width + " 高度:" + _real_height + " 分片尺寸:" + _seg_width + "x" + _seg_height + "  原始图片分片单位高度:" + _src_seg_height);      for (var i = 0; i < segNum; i++) {        var _drawWidth = _min_width + (i + 1) * _seg_width;        _drawWidth = parseInt(_drawWidth);        if (_drawWidth > _img_width) {          _drawWidth = _img_width;        }        var _drawHeight = _seg_height;        var sx = 0;        var sy = parseInt(i * _src_seg_height);        var swidth = _img_width;        var sheight = parseInt(_img_height / segNum);        var dest_x = parseInt(_img_width / 2 - (_drawWidth) / 2);        var dest_y = i * _seg_height;        console.log(" 分片:" + i + " sx:" + sx + ",sy:" + sy + ",swidth:" + _img_width + ",sheight:" + sheight + ",x:" + dest_x + ",y:" + dest_y + ",drawWidth:" + _drawWidth + ",drawHeight:" + _drawHeight);        tmpCtx.drawImage(image, sx, sy, swidth, sheight, dest_x, dest_y, _drawWidth, _drawHeight);      }      return tmpCanvas;    }    function _seg1(image,segNum,angle){      var _img_width=image.naturalWidth;      var _img_height= image.naturalHeight;      //--计算实际的高度和宽度。      var _real_height=parseInt(_img_height*Math.cos(angle));      //--好了,已经算出了投影过去的高度,那么可以知道投影和原始图形之间的比例。      //--当然,这个比例其实就是cos(angle),就是说,没什么可以计算的,      //那么现在可以计算出最小的宽度了。      var _min_width=parseInt(_img_width*Math.cos(angle));      //根据这一个已经可以得到切片相关图形了。//      context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);//      参数值//      参数  描述//      img 规定要使用的图像、画布或视频。//sx    可选。开始剪切的 x 坐标位置。//sy    可选。开始剪切的 y 坐标位置。//swidth    可选。被剪切图像的宽度。//sheight   可选。被剪切图像的高度。//x 在画布上放置图像的 x 坐标位置。//y 在画布上放置图像的 y 坐标位置。//width 可选。要使用的图像的宽度。(伸展或缩小图像)//height    可选。要使用的图像的高度。(伸展或缩小图像)      var _seg_width=((_img_width-_min_width)/segNum);      var _seg_height=((_real_height)/segNum);      var _src_seg_height=(_img_height)/segNum;      console.log("当前图片尺寸:"+_img_width+" x "+_img_height+", 投影后尺寸:最小宽度:"+_min_width+" 高度:"+_real_height+" 分片尺寸:"+_seg_width+"x"+_seg_height+"  原始图片分片单位高度:"+_src_seg_height);      for(var i=0;i< segNum;i++){        var _drawWidth=_min_width+(i+1)*_seg_width;        _drawWidth=parseInt(_drawWidth);        if(_drawWidth>_img_width){          _drawWidth=_img_width;        }        var _drawHeight=_seg_height;        var sx=0;        var sy=parseInt(i*_src_seg_height);        var swidth=_img_width;        var sheight=parseInt(_img_height/segNum);        var dest_x=parseInt(_img_width/2-(_drawWidth)/2);        var dest_y=i*_seg_height;        console.log(" 分片:"+i+" sx:"+sx+",sy:"+sy+",swidth:"+_img_width+",sheight:"+sheight+",x:"+dest_x+",y:"+dest_y+",drawWidth:"+_drawWidth+",drawHeight:"+_drawHeight);        ctx.drawImage(image,sx,sy,swidth,sheight,dest_x,dest_y,_drawWidth,_drawHeight);      }    }    function draw(segNum,angle){      _seg1(IMAGE,segNum,angle);    }    function realDraw(segNum,angle){      var _canvas=transform3dImage(IMAGE,segNum,angle);      PREVIEW.src=_canvas.toDataURL("image/png");    }  </script></body></html>

图片素材就是上面那一张图片啦。
素材图片:
这里写图片描述
现在分别取角度为60,切片数量为2,3,4,50来看看不同之处。

这里写图片描述

这里写图片描述
这里写图片描述
这里写图片描述

后续

好了,后面我们将要加上按照不同边来扭曲图形的功能,不一定是底边哦。

0 0
原创粉丝点击