58 WebGL在平面绘制透视纹理效果
来源:互联网 发布:万达电商 淘宝卖货的 编辑:程序博客网 时间:2024/06/05 16:05
问题原因
我公司里有一个项目,需要能够一个矩形的四个顶点能够随意移动,而且上面还绘制的纹理。然后,我按照以前的方式书写,问题来了,由于一个矩形是由两个三角形组成的,然后就出现下面喜闻乐见的效果:
我们会发现,两个三角形的交汇处,有一道折线,所以比较尴尬了。
而我们需要的是什么呢:
我们需要的是上面这种,具有透视效果,而且图形不会有折线的效果。
即使是随意的扭曲,也不会出现折线,这就是所谓的透视纹理绘制。
思路
- 首先,我需要先获取到当前绘制的那一点的坐标x,y两个轴的,然后计算这一个点和四个矩形顶点的位置关系,计算出这个点需要绘制的纹理图片的位置。关系图如下:
查看图形,其实我们唯一不知道的就是过图形的线和两个点切割获得的比例,通过高中的学的图形知识,将五个点代进去,计算一下,最后出来的就是这个点。
由于(c1-v1)/(v0-v1) = (c2-v3)/(v2-v3)这个比例保证每一个点在纹理坐标中确定需要绘制的纹理。
然后通过两个点确定一条直线,根据两点方程(x-c1.x)/(c2.x-c1.x) = (y-c1.y)/(c2.y-c1.y)然后将所有的值代入进入,再通过一元二次方程的解析,最后得出比例。
以下是js版本的代码:
function calc(p1,p2,p3,p4,p0) { /* * p1-------p3 * | | * | | * | | * p2-------p4 *假设四个点的坐标分别是(x1,y1)、(x2,y2)、(x3,y3)、(x4,y4) * 则下式成立平行 * (x1-x2)/(y1-y2)=(x3-x4)/(y3-y4) * */ /* * 根据两点式方程 * (y-y1)/(y2-y1) = (x-x1)/(x2-x1) * 得出 * (y-y1)(x2-x1) = (y2-y1)(x-x1) * yx2-yx1-y1x2+y1x1 = y2x-y2x1-y1x+y1x1 * yx2-yx1-y1x2+y1x1-(y2x-y2x1-y1x+y1x1) = 0; * yx2-yx1-y1x2+y1x1-y2x+y2x1+y1x-y1x1 = 0; * yx2-yx1-y1x2-y2x+y2x1+y1x = 0;\ * x2(y-y1)+x1(y2-y)+x(y1-y2)=0 * * //两个点的位置为 * p12Xlen = p2.x-p1.x; * p12Ylen = p2.y-p1.y; * p34Xlen = p4.x-p3.x; * p34Ylen = p4.y-p3.y; * p12.x = x1 = p1.x+p12Xlen*s; * p12.y = y1 = p1.y+p12Ylen*s; * p34.x = x2 = p3.x+p34Xlen*s; * p34.y = y2 = p3.y+p34Ylen*s; * x = p0.x; * y = p0.y; * * 由此得出 * x2(y-y1) = (p3.x+p34Xlen*s)(p0.y-(p1.y+p12Ylen*s)); * x2(y-y1) = (p3.x + p34Xlen*s)(p0.y - p1.y - p12Ylen*s); * x2(y-y1) = p3.x*p0.y-p3.x*p1.y-p3.x*p12Ylen*s+p0.y*p34Xlen*s-p1.y*p34Xlen*s-p12Ylen*p34Xlen*s*s; * x2(y-y1) = p3.x*p0.y-p3.x*p1.y+(-p3.x*p12Ylen+p0.y*p34Xlen-p1.y*p34Xlen)*s-p12Ylen*p34Xlen*s*s; * * x1(y2-y) = (p1.x+p12Xlen*s)*(p3.y+p34Ylen*s-p0.y); * x1(y2-y) = (p1.x+p12Xlen*s)*(p3.y-p0.y+p34Ylen*s); * x1(y2-y) = p1.x*p3.y-p1.x*p0.y+p1.x*p34Ylen*s+p3.y*p12Xlen*s-p0.y*p12Xlen*s+p12Xlen*p34Ylen*s*s; * x1(y2-y) = p1.x*p3.y-p1.x*p0.y+(p1.x*p34Ylen+p3.y*p12Xlen-p0.y*p12Xlen)*s+p12Xlen*p34Ylen*s*s; * * x(y1-y2) = p0.x(p1.y+p12Ylen*s-(p3.y+p34Ylen*s)); * x(y1-y2) = p0.x(p1.y+p12Ylen*s-p3.y-p34Ylen*s); * x(y1-y2) = p0.x(p1.y-p3.y+p12Ylen*s-p34Ylen*s); * x(y1-y2) = p0.x(p1.y-p3.y)+p0.x(p12Ylen-p34Ylen)*s; * * 得出 * * A = p12Xlen*p34Ylen-p12Ylen*p34Xlen; * * B = p1.x*p34Ylen+p3.y*p12Xlen-p0.y*p12Xlen-p3.x*p12Ylen+p0.y*p34Xlen-p1.y*p34Xlen+p0.x(p12Ylen-p34Ylen); * * C = p3.x*p0.y-p3.x*p1.y+p1.x*p3.y-p1.x*p0.y+p0.x(p1.y-p3.y) * * Ass + Bs +C = 0; * */ var p12Xlen = p2.x-p1.x; var p12Ylen = p2.y-p1.y; var p34Xlen = p4.x-p3.x; var p34Ylen = p4.y-p3.y; var a = p12Xlen*p34Ylen-p12Ylen*p34Xlen; var b = p1.x*p34Ylen+p3.y*p12Xlen-p0.y*p12Xlen-p3.x*p12Ylen+p0.y*p34Xlen-p1.y*p34Xlen+p0.x*(p12Ylen-p34Ylen); var c = p3.x*p0.y-p3.x*p1.y+p1.x*p3.y-p1.x*p0.y+p0.x*(p1.y-p3.y); //在两条线平行的情况下 if(a === 0){ return -c/b; } //在两条线不平行的情况下 获取数组里面再0到1范围内的 return [(-b+Math.sqrt(b*b-4.0*a*c))/(2*a),(-b-Math.sqrt(b*b-4.0*a*c))/(2*a)]; }
案例代码
以下是可以运行的案例代码,把文件放到服务里面,设置好图片,就可以直接查看效果:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Title</title> <style> body { margin: 0; text-align: center; } #canvas { display: block; } #div{ width:800px; height:800px; position: relative; margin: 0 auto; } </style></head><body onload="main()"><div id="div"> <canvas id="canvas" height="800" width="800"></canvas></div></body><script src="https://johnson2heng.github.io/webgl-demo/lib/webgl-utils.js"></script><script src="https://johnson2heng.github.io/webgl-demo/lib/webgl-debug.js"></script><script src="https://johnson2heng.github.io/webgl-demo/lib/cuon-utils.js"></script><script src="https://johnson2heng.github.io/webgl-demo/lib/cuon-matrix.js"></script><script> /*第一部分顶点着色器接收顶点的纹理坐标,传递给片元着色器*/ var VSHADER_SOURCE = "" + "attribute vec4 a_Position;\n" +// "varying vec2 v_Position;\n" + "void main(){\n" + " gl_Position = a_Position;\n" + " v_Position = vec2(a_Position);\n" + "}\n"; var FSHADER_SOURCE = "" + "precision mediump float;\n" +// "uniform sampler2D u_Sampler;\n" +// "varying vec2 v_Position;\n" + "uniform vec2 u_point0;\n" + "uniform vec2 u_point1;\n" + "uniform vec2 u_point2;\n" + "uniform vec2 u_point3;\n" + "float calc(vec2 p1,vec2 p2,vec2 p3,vec2 p4,vec2 p0){\n" + //如果不是平行的两条直线 " float p12Xlen = p2.x-p1.x;\n" + " float p12Ylen = p2.y-p1.y;\n" + " float p34Xlen = p4.x-p3.x;\n" + " float p34Ylen = p4.y-p3.y;\n" + " float a = p12Xlen*p34Ylen-p12Ylen*p34Xlen;\n" + " float b = p1.x*p34Ylen+p3.y*p12Xlen-p0.y*p12Xlen-p3.x*p12Ylen+p0.y*p34Xlen-p1.y*p34Xlen+p0.x*(p12Ylen-p34Ylen);\n" + " float c = p3.x*p0.y-p3.x*p1.y+p1.x*p3.y-p1.x*p0.y+p0.x*(p1.y-p3.y);\n" + //两条线都垂直于x轴或者y轴的情况下 " if(a == 0.0){\n" + " return -c/b;\n" + " }\n" + //两条线不平行的情况下 " float endA = (-b+sqrt(b*b-4.0*a*c))/(2.0*a);\n" + " float endB = (-b-sqrt(b*b-4.0*a*c))/(2.0*a);\n" + " if(endA > 0.0 && endA < 1.0){\n" + " return endA;\n" + " }\n" + " if(endB > 0.0 && endB < 1.0){\n" + " return endB;\n" + " }\n" + " else {\n" + " return -c/b;\n" + " }\n" + "}\n" + ""+ //两个点的位置,第一个calc(v0,v2,v1,v3,p0) 第二个calc(v1,v0,v3,v2,p0) "void main(){\n" + " gl_FragColor = texture2D(u_Sampler,vec2(calc(u_point0,u_point2,u_point1,u_point3,v_Position),calc(u_point1,u_point0,u_point3,u_point2,v_Position)));\n" +// "}\n"; //四个顶点的位置 var v0 = {x:-0.5,y:0.5}; var v1 = {x:-0.5,y:-0.5}; var v2 = {x:0.5,y:0.5}; var v3 = {x:0.5,y:-0.5}; //图片的地址设置 var imgSrc = "../image/door/001.png"; var gl; /*第二部分 main()方法 初始化着色器,设置顶点信息,调用配置纹理方法*/ function main() { var canvas = document.getElementById("canvas"); gl = getWebGLContext(canvas); if(!gl){ console.log("你的电脑不支持WebGL!"); return; } if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)){ console.log("初始化着色器失败!"); return; } //设置顶点的相关信息 var n = initVertexBuffers(gl); if(n < 0){ console.log("无法获取到点的数据"); return; } //配置纹理 if(!initTextures(gl,n)){ console.log("无法配置纹理"); return; } } /*第三部分 initVertexBuffers() 设置顶点坐标和纹理坐标 调用initTextures()进行下一步处理*/ function initVertexBuffers(gl) { /* * v0-------v2 * | | * | | * | | * v1-------v3 * * [v0.x,v0.y,v1.x,v1.y,v2.x,v2.y,v3.x,v3.y] * config.Positions[0].Config 数组由八个数据组成,代表四个点的位置,距离中心点的偏移量 * */ var verticesSizes = new Float32Array([ //四个顶点的位置 v0.x,v0.y, v1.x,v1.y, v2.x,v2.y, v3.x,v3.y ]); initArrayBuffer(gl, verticesSizes, 2, gl.FLOAT, "a_Position"); //将四个顶点位置传入 var point0 = gl.getUniformLocation(gl.program, "u_point0"); var point1 = gl.getUniformLocation(gl.program, "u_point1"); var point2 = gl.getUniformLocation(gl.program, "u_point2"); var point3 = gl.getUniformLocation(gl.program, "u_point3"); if(point0 < 0){ alert("无法获取到存储的位置"); } gl.uniform2f(point0,v0.x,v0.y); gl.uniform2f(point1,v1.x,v1.y); gl.uniform2f(point2,v2.x,v2.y); gl.uniform2f(point3,v3.x,v3.y); var indexSize = new Uint8Array([0,1,3,0,2,3]); //将索引写入缓冲区对象 var indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indexSize, gl.STATIC_DRAW); return indexSize.length; } //创建变量缓冲区 function initArrayBuffer(gl, data, num, type, attribute) { //创建缓冲区对象 var buffer = gl.createBuffer(); if (!buffer) { console.log("无法创建缓冲区对象"); return -1; } //绑定缓冲区对象并写入数据 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); //获取顶点位置变量位置 var a_attribue = gl.getAttribLocation(gl.program, attribute); if (a_attribue < 0) { console.log("无法获取顶点位置的存储变量"); return -1; } //对位置的顶点数据进行分配,并开启 gl.vertexAttribPointer(a_attribue, num, type, false, 0, 0); gl.enableVertexAttribArray(a_attribue); } /*第四部分 initTextures() 创建纹理对象 并调用纹理绘制方法*/ function initTextures(gl,n) { var texture = gl.createTexture();//创建纹理对象 if(!texture){ console.log("无法创建纹理对象"); return; } //获取u_Sampler的存储位置 var u_Sampler = gl.getUniformLocation(gl.program,"u_Sampler"); if(u_Sampler < 0){ console.log("无法获取变量的存储位置"); return; } //创建Image对象,并绑定加载完成事件 var image = new Image(); image.onload = function () { loadTexture(gl,n,texture,u_Sampler,image); //初始化交互事件 initMoveBtn(); }; image.src = imgSrc; return true; } function initMoveBtn() { var canvas = document.querySelector("#canvas"); var div = document.querySelector("#div"); var canvasWidth = canvas.width; var canvasHeight = canvas.height; addBtn(v0); addBtn(v1); addBtn(v2); addBtn(v3); function addBtn(obj) { var x = (obj.x+1)/2; var y = 1-(obj.y+1)/2; var btn = document.createElement("div"); btn.style.cssText = "height:10px; width:10px; transform:translate(-50%,-50%); position:absolute; background:green; cursor:pointer;"; btn.style.left = x *canvasWidth+"px"; btn.style.top = y * canvasHeight +"px"; div.appendChild(btn); var downX,downY,btnX,btnY; btn.addEventListener("mousedown",function (event) { //鼠标按下时的位置 downX = event.clientX; downY = event.clientY; //按下时btn的位置 btnX = parseFloat(btn.style.left); btnY = parseFloat(btn.style.top); document.addEventListener("mousemove",move,true); document.addEventListener("mouseup",up,true); },true); function move(event) { event.preventDefault(); var moveX = event.clientX; var moveY = event.clientY; var x = moveX-downX + btnX; var y = moveY - downY + btnY; btn.style.left = x+"px"; btn.style.top = y + "px"; obj.x = x/canvasWidth*2-1; obj.y = (1-y/canvasHeight)*2-1; //重新绘制 reload(); } function up() { document.removeEventListener("mousemove",move,true); document.removeEventListener("mouseup",up,true); } } } /*第五部分 设置纹理相关信息供WebGL使用,并进行绘制*/ function loadTexture(gl,n,texture,u_Sampler,image) { //对纹理图像进行y轴反转 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,1); //开启0号纹理单元 gl.activeTexture(gl.TEXTURE0); //向target绑定纹理对象 gl.bindTexture(gl.TEXTURE_2D,texture); //配置纹理参数 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); //配置纹理图像 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); //将0号纹理传递给着色器 gl.uniform1i(u_Sampler,0); draw(n); } //重新绘制的方法 function reload(){ var n = initVertexBuffers(gl); //绘制 draw(n); } function draw(n) { //绘制 gl.clearColor(0.0,0.0,0.0,1.0); gl.clear(gl.COLOR_BUFFER_BIT); gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); }</script></html>
阅读全文
0 0
- 58 WebGL在平面绘制透视纹理效果
- webgl--平面阴影效果
- OpenGL---在平面上贴纹理
- WebGL自学课程(5):使用一张贴图纹理绘制地球
- OpenGL_ES 创建一个平面 绘制纹理 响应输入事件
- 如何在ChemDraw中绘制透视图形
- WebGL 写入纹理
- 透视效果
- 绘制纹理
- 【UWP通用应用开发】编辑文本、绘制图形、3D透视效果及绘制时钟实战
- WEBGL学习【二】平面图形
- 八、WebGL入门,纹理贴图
- WebGL入门 颜色与纹理
- 20 WebGL使用纹理贴图
- webgl学习笔记五-纹理
- WEBGL学习【五】纹理贴图
- WebGL-三角形绘制
- 使用WebGL绘制地球
- 增强for循环
- cordova 安卓开发环境搭建(Mac OSX
- 01:统计数字字符个数
- 直播平台的数据库架构演变
- git tag创建、远程推送以及回退
- 58 WebGL在平面绘制透视纹理效果
- 神经网络的代价函数到底怎么在算
- Laravel图片处理库Intervention/image
- java 基本类型
- CodeForces
- element表格中的v-show
- 利用开源ASN1C库实现asn.1的编解码
- Oracle11g 临时表空间收缩
- Python关于Threading暂停恢复解决办法