目前很多浏览器原生支持WEBGL,只要你的代码在浏览其中打开,不用服务器,就可以看到你画的模型了。
目前我做了一个非常简单的画图工具,先画2D图形(结束画图按‘c'键),画好后直接拉伸成立体图形。新版本已经能对齐模型与2D图形,并可以获取本地文本文件的文本值(计划用来加载模型文件)。全部用原生javascript加WEBGL,有需要学习这方面技能的可以参考
全部代码见下面,将代码存到test.html文件中,浏览器中直接打开文件就可以看到效果:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>已经有拉伸出来的正交投影模型,接下来要将中心转向面的选取和模型的平移,旋转</title>
<style>
.tabx{border-collapse:collapse;border-style: solid;border-width: 1px;text-align:center;}
.tdx{border-style: solid;border-width: 1px;}
</style>
</head>
<body>
<button type="reset" onclick="circle()" name="btn" value="dd">circle</button>
<button type="button" id="btn" name="btns">SaveAs</button>
<button type="button" id="btne" name="btns">Extrude</button>
<canvas id="canvas"></canvas>
</br>
<table>
<tr><td>齐次坐标有四个值:X,Y,Z,1,数据类型是vec4</td></tr>
<tr><td>OPENGL使用右手坐标系</td></tr>
<tr><td>4*4矩阵的数据类型是mat4</td></tr>
<tr><td>单位矩阵一条对角线上的值为1,其余值全为0</td></tr>
<tr><td>任何值乘以单位矩阵都不会改变</td></tr>
<tr><td sty<td>右边矩阵是左边矩阵的转置,函数是transpose(mat4)</td></tr>
</table>
</br>
<table class='tabx'>
<tr><td style='color:red;'>A00</td><td>A01</td><td>A02</td><td>A03</td> <td rowspan='4' class='tdx'>transpose</td><td style='color:red;'>A00</td><td style='color:red;'>A10</td><td style='color:red;'>A20</td><td style='color:red;'>A30</td></tr>
<tr><td style='color:red;'>A10</td><td>A11</td><td>A12</td><td>A13</td> <td>A01</td><td>A11</td><td>A21</td><td>A31</td></tr>
<tr><td style='color:red;'>A20</td><td>A21</td><td>A22</td><td>A23</td> <td>A02</td><td>A12</td><td>A22</td><td>A32</td></tr>
<tr><td style='color:red;'>A30</td><td>A31</td><td>A32</td><td>A33</td> <td>A03</td><td>A13</td><td>A23</td><td>A33</td></tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 矩阵加法 </td><td>A+a</td><td>B+b</td><td>C+c</td><td>D+d</td><td rowspan='4' class='tdx'> = </td><td>A</td><td>B</td><td>C</td><td>D</td><td rowspan='4' class='tdx'> + </td><td>a</td><td>b</td><td>c</td><td>d</td></tr>
<tr><td>E+e</td><td>F+f</td><td>G+g</td><td>H+h</td> <td>E</td><td>F</td><td>G</td><td>H</td> <td>e</td><td>f</td><td>g</td><td>h</td></tr>
<tr><td>I+i</td><td>J+j</td><td>K+k</td><td>L+l</td> <td>I</td><td>J</td><td>K</td><td>L</td> <td>i</td><td>j</td><td>k</td><td>l</td></tr>
<tr><td>M+m</td><td>N+n</td><td>O+o</td><td>P+p</td> <td>M</td><td>N</td><td>O</td><td>P</td> <td>m</td><td>n</td><td>o</td><td>p</td></tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 点与矩阵相乘 </td><td>A</td><td>B</td><td>C</td><td>D</td><td rowspan='4' class='tdx'> * </td><td>X</td><td rowspan='4' class='tdx'> = </td><td>AX+BY+CZ+D</td></tr>
<tr><td>E</td><td>F</td><td>G</td><td>H</td> <td>Y</td> <td>EX+FY+GZ+H</td></tr>
<tr><td>I</td><td>J</td><td>K</td><td>L</td> <td>Z</td> <td>IX+JY+KZ+L</td></tr>
<tr><td>M</td><td>N</td><td>O</td><td>P</td> <td>1</td> <td>MX+NY+OZ+P</td></tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 矩阵乘法 </td><td>A</td><td>B</td><td>C</td><td>D</td><td rowspan='4' class='tdx'> * </td><td>a</td><td>b</td><td>c</td><td>d</td><td rowspan='4' class='tdx'> = </td><td>Aa+Be+Ci+Dm</td><td>Ab+Bf+Cj+Dn</td><td>Ac+Bg+Ck+Do</td><td>Ad+Bh+Cl+Dp</td></tr>
<tr><td>E</td><td>F</td><td>G</td><td>H</td> <td>e</td><td>f</td><td>g</td><td>h</td> <td>Ea+Fe+Gi+Hm</td><td>Eb+Ff+Gj+Hn</td><td>Ec+Fg+Gk+Ho</td><td>Ed+Fh+Gl+Hp</td></tr>
<tr><td>I</td><td>J</td><td>K</td><td>L</td> <td>i</td><td>j</td><td>k</td><td>l</td> <td>Ia+Je+Ki+Lm</td><td>Ib+Jf+Kj+Ln</td><td>Ic+Jg+Kk+Lo</td><td>Id+Jh+Kl+Lp</td></tr>
<tr><td>M</td><td>N</td><td>O</td><td>P</td> <td>m</td><td>n</td><td>o</td><td>p</td> <td>Ma+Ne+Oi+Pm</td><td>Mb+Nf+Oj+Pn</td><td>Mc+Ng+Ok+Po</td><td>Md+Nh+Ol+Pp</td></tr>
</table>
</br>
<table>
<tr><td>如果Point2=M1*(M2*(M3*Point1))</td></tr>
<tr><td>则Point2=(M1*M2*M3)*Point1,这可以显著减少矩阵运算量</td></tr>
<tr><td>对于M*M-1=M-1*M=单位矩阵,M-1叫做逆矩阵,公式为mat4.inverse()</td></tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 平移矩阵 glm::translate(x,y,z)</td><td>X+TX</td><td rowspan='4' class='tdx'> = </td><td>1</td><td>0</td><td>0</td><td>TX</td><td rowspan='4' class='tdx'> * </td><td>X</td></tr>
<tr><td>Y+TY</td><td>0</td><td>1</td><td>0</td><td>TY</td> <td>Y</td> </tr>
<tr><td>Z+TZ</td><td>0</td><td>0</td><td>1</td><td>TZ</td> <td>Z</td> </tr>
<tr><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td> <td>1</td> </tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 缩放矩阵 glm::scale(x,y,z)</td><td>X*SX</td><td rowspan='4' class='tdx'> = </td><td>SX</td><td>0</td><td>0</td><td>0</td><td rowspan='4' class='tdx'> * </td><td>X</td></tr>
<tr><td>Y*SY</td><td>0</td><td>SY</td><td>0</td><td>0</td> <td>Y</td> </tr>
<tr><td>Z*SZ</td><td>0</td><td>0</td><td>SZ</td><td>0</td> <td>Z</td> </tr>
<tr><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td> <td>1</td> </tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 缩放矩阵用来切换坐标系</td><td>1</td><td>0</td><td>0</td><td>0</td></tr>
<tr><td>0</td><td>1</td><td>0</td><td>0</td> </tr>
<tr><td>0</td><td>0</td><td>-1</td><td>0</td> </tr>
<tr><td>0</td><td>0</td><td>0</td><td>1</td> </tr>
</table>
</br>
<table>
<tr><td colspan='9'>旋转需要指定旋转轴和旋转角度,围绕任何轴的旋转都可以表示为绕X,Y,Z轴旋转的组合,旋转角度被称为欧拉角</td></tr>
<tr><td colspan='9'>3D空间旋转轴不经过原点时,1.平移旋转轴,使它经过原点,2.绕X,Y,Z旋转,3.复原步骤1中的平移</td></tr>
<tr><td colspan='9'>计算旋转推荐使用四元数,用来消除3D应用中的一些瑕疵</td></tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 绕X轴旋转矩阵 glm::rotate()</td><td>X'</td><td rowspan='4' class='tdx'> = </td><td>1</td><td>0</td><td>0</td><td>0</td><td rowspan='4' class='tdx'> * </td><td>X</td></tr>
<tr><td >Y'</td><td>0</td><td>cosθ</td><td>-sinθ</td><td>0</td> <td>Y</td> </tr>
<tr><td>Z'</td><td>0</td><td>sinθ</td><td>cosθ</td><td>0</td> <td>Z</td> </tr>
<tr><td >1</td><td>0</td><td>0</td><td>0</td><td>1</td> <td>1</td> </tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 绕Y轴旋转矩阵 glm::rotate()</td><td>X'</td><td rowspan='4' class='tdx'> = </td><td>cosθ</td><td>0</td><td>sinθ</td><td>0</td><td rowspan='4' class='tdx'> * </td><td>X</td></tr>
<tr><td>Y'</td><td>0</td><td>1</td><td>0</td><td>0</td> <td>Y</td> </tr>
<tr><td>Z'</td><td>-sinθ</td><td>0</td><td>cosθ</td><td>0</td> <td>Z</td> </tr>
<tr><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td> <td>1</td> </tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 绕Z轴旋转矩阵 glm::rotate()</td><td>X'</td><td rowspan='4' class='tdx'> = </td><td>cosθ</td><td>-sinθ</td><td>0</td><td>0</td><td rowspan='4' class='tdx'> * </td><td>X</td></tr>
<tr><td>Y'</td><td>sinθ</td><td>cosθ</td><td>0</td><td>0</td> <td>Y</td> </tr>
<tr><td>Z'</td><td>0</td><td>0</td><td>1</td><td>0</td> <td>Z</td> </tr>
<tr><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td> <td>1</td> </tr>
</table>
</br>
<table>
<tr><td>向量表示大小和方向,没有特定位置。</td></tr>
<tr><td>一般用空间中的单个点来表示向量,向量的大小是原点到该点的距离,方向是原点到该点的方向。</td></tr>
<tr><td>点和向量没有很大的区别,vec3/vec4既可以表示点,也可以表示向量</td></tr>
<tr><td>向量的加减法,如果A(u,v,w),B(x,y,z),A+B=(u+x,v+y,w+z),A-B=(u-x,v-y,w-z)。</td></tr>
<tr><td>向量归一化normalize(变长度为1):A/sqrt(u*u+v*v+w*w)。</td></tr>
<tr><td>向量点积dot:A●B=ux+vy+wz。最大的用处是计算两个向量夹角:A●B=|A||B|cos(θ)</td></tr>
<tr><td>向量叉积cross:AxB=(uz-wy,wx-uz,uy-vx)。叉积的一个重要特点:新生成一个向量,该向量正交与前面两个向量所定义的平面</td></tr>
<tr><td>法向量的计算:假设一个三角形有三个逆时针方向的顶点v1,v2,v3,向量P=v2-v1;向量q=v3-v1;外法向量N=PxQ。</td></tr>
</table>
</br>
<table>
<tr><td>一:透视矩阵P:用于将3D空间的点变换至近剪裁平面上合适的位置,需要先计算q,A,B,C,然后构建透视矩阵。</td></tr>
<tr><td>q=1/tan(FieldOfView/2);A=q/aspectRadio;B=(Znear+Zfar)/(Znear-Zfar); C=2*(Znear*Zfar)/(Znear-Zfar)。</td></tr>
<tr><td>二:模型矩阵M:将模型对象(局部空间)定位及定向在世界空间的矩阵称为模型矩阵M。</td></tr>
<tr><td>三: 视图矩阵V:OpenGL有一个固定在原点,看向Z轴负方向的相机。有一个点,要想得到相机观察到的视图。</td></tr>
<tr><td>需要一:点向负的相机位置平移</td></tr>
<tr><td>需要二:点向负的相机旋转的欧拉角旋转</td></tr>
<tr><td>视觉空间中的点Pc=V(旋转和平移矩阵的乘积)*Pw(原来的点)</td></tr>
<tr><td>通常,V矩阵与模型矩阵M合并为一个模型-视图矩阵MV=V*M</td></tr>
<tr><td>这样,我们只要把局部空间的点乘以MV,就可以得到相机空间的点了</td></tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 透视矩阵 glm::perspective()</td><td>A</td><td>0</td><td>0</td><td>0</td><td rowspan='4' class='tdx'> 正射投影矩阵</td><td>2/(R-L)</td><td>0</td><td>0</td><td>-(R+L)/(R-L)</td></tr>
<tr><td>0</td><td>q</td><td>0</td><td>0</td> <td>0</td><td>2/(T-B)</td><td>0</td><td>-(T+B)/(T-B)</td></tr>
<tr><td>0</td><td>0</td><td>B</td><td>C</td> <td>0</td><td>0</td><td>1/(Zfar-Znear)</td><td>-Znear/(Zfar-Znear)</td></tr>
<tr><td>0</td><td>0</td><td>-1</td><td>0</td> <td>0</td><td>0</td><td>0</td><td>1</td></tr>
</table>
</br>
<image src="./lookat.webp">
<table>
<tr><td>OpenGL本身没有摄像机(Camera)的概念,但我们可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机,产生一种我们在移动的感觉,而不是场景在移动。</td></tr>
<tr><td>R-右向量,相机坐标系x在世界坐标系的位置。</td></tr>
<tr><td>U-上向量,相机坐标系y在世界坐标系的位置。</td></tr>
<tr><td>D-方向向量,相机坐标系z在世界坐标系的位置。</td></tr>
<tr><td>P-相机在世界坐标系的位置。</td></tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> Lookat矩阵=</td><td>Rx</td><td>Ry</td><td>Rz</td><td>0</td><td rowspan='4' class='tdx'>*</td><td>1</td><td>0</td><td>0</td><td>-Px</td></tr>
<tr><td>Ux</td><td>Uy</td><td>Uz</td><td>0</td> <td>0</td><td>1</td><td>0</td><td>-Py</td></tr>
<tr><td>Dx</td><td>Dy</td><td>Dz</td><td>0</td> <td>0</td><td>0</td><td>1</td><td>-Pz</td></tr>
<tr><td>0</td><td>0</td><td>0</td><td>1</td> <td>0</td><td>0</td><td>0</td><td>1</td></tr>
</table>
</br>
<table>
<tr><td>lookat矩阵的计算程序:</td></tr>
<tr><td>void lookat(Vec3f eye, Vec3f center, Vec3f up) {</td></tr>
<tr><td> Vec3f z = (eye-center).normalize();</td></tr>
<tr><td> Vec3f x = cross(up,z).normalize();</td></tr>
<tr><td> Vec3f y = cross(z,x).normalize();</td></tr>
<tr><td> Matrix Minv = Matrix::identity();</td></tr>
<tr><td> Matrix Tr = Matrix::identity();</td></tr>
<tr><td> for (int i=0; i<3; i++) {</td></tr>
<tr><td> Minv[0][i] = x[i];</td></tr>
<tr><td> Minv[1][i] = y[i];</td></tr>
<tr><td> Minv[2][i] = z[i];</td></tr>
<tr><td> Tr[i][3] = -center[i];</td></tr>
<tr><td> }</td></tr>
<tr><td> ModelView = Minv*Tr;</td></tr>
<tr><td>}</td></tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='2' class='tdx'>GLSL中以列为单位读入数据,前四个数据会放入第一列</td><td>1</td><td>0</td><td>0</td><td>Tx</td></tr>
<tr><td>0</td><td>1</td><td>0</td><td>Ty</td></tr>
<tr><td rowspan='2' class='tdx'>mat4 transMatrix=mat4(1,0,0,0, 0,1,0,0, 0,0,1,0, Tx,Ty,Tz,1)</td><td>0</td><td>0</td><td>1</td><td>Tz</td></tr>
<tr><td>0</td><td>0</td><td>0</td><td>1</td></tr>
</table>
</br>
<script>
var vShaderSource = 'attribute vec2 a_Position;'+
'attribute vec2 a_Screen_Size;'+
'uniform mat4 Pmatrix;'+
'uniform mat4 Vmatrix;'+
'uniform mat4 Mmatrix;'+
'attribute vec3 color;'+//the color of the point
'varying vec3 vColor;'+
'void main(void) { '+//pre-built function
//视图坐标(0~800,0~600)转换为 OPENGL坐标(-1.0,1.0)之间的值
'vec2 xyposition = (a_Position / a_Screen_Size) * 2.0 - 1.0;'+
'vec4 position=vec4(xyposition,0.0,1.0);'+
'gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position);'+
'gl_PointSize = 10.0;'+
'vColor = color;'+
'}';
var fShaderSource = 'precision mediump float;'+
'varying vec3 vColor;'+
'void main(void) {'+
'gl_FragColor = vec4(vColor, 1.);'+
'}';
let canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;//当前的宽度
canvas.height = window.innerHeight;//当前的高度
let gl = canvas.getContext('webgl');
//创建顶点着色器
let vShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vShader,vShaderSource);
gl.compileShader(vShader);
//创建片元着色器
let fShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fShader,fShaderSource);
gl.compileShader(fShader);
//创建一个完整的着色器
let propgram = gl.createProgram();
gl.attachShader(propgram,vShader);
gl.attachShader(propgram,fShader);
gl.linkProgram(propgram);
gl.useProgram(propgram);
let positions =[];//设置一个数据
let vectexs =[];//设置一个数据
let x0=0;
let y0=0;
//设置缓冲区存放玩家点击的点的坐标
let buffer = gl.createBuffer();
//绑定缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
//取到我们需要使用的变量
let a_Position = gl.getAttribLocation(propgram,'a_Position');
gl.enableVertexAttribArray(a_Position);
//每次取两个数据
let size = 2;
//每个数据的类型是32位浮点型
let type = gl.FLOAT;
//不需要归一化数据
let normalize = false;
// 每次迭代运行需要移动数据数 * 每个数据所占内存 到下一个数据开始点。
let stride = 0;
// 从缓冲起始位置开始读取
let offset = 0;
// 将 a_Position 变量获取数据的缓冲区指向当前绑定的 buffer。
gl.vertexAttribPointer(a_Position, size, type, normalize, stride, offset)
//片元色器
let u_Color = gl.getUniformLocation(propgram,'u_Color');
gl.uniform3f(u_Color,1.0,1.0,0.0);
//顶点着色器
//处理顶点着色器中的变量
let a_Screen_Size = gl.getAttribLocation(propgram,'a_Screen_Size');//取到屏幕的大小
gl.vertexAttrib2f(a_Screen_Size,canvas.width,canvas.height);//设置参数
//获取地址
var _PM = gl.getUniformLocation(propgram, "Pmatrix");
var _VM = gl.getUniformLocation(propgram, "Vmatrix");
var _MM = gl.getUniformLocation(propgram, "Mmatrix");
//正交投影
var p_matrix = orth_projection(-1,1,-1,1,1,100);
//模型举证
var m_matrix = [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ];
//视图矩阵
var v_matrix = [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ];
//绑定
gl.uniformMatrix4fv(_PM, false, p_matrix);
gl.uniformMatrix4fv(_VM, false, v_matrix);
gl.uniformMatrix4fv(_MM, false, m_matrix);
//背景色
gl.clearColor(1,1,1,1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
//捕获按钮事件
var handler=function(e){
var x = e.pageX;
var y = e.pageY;
positions .push(x,y);
vectexs.push(x,y)
if(positions.length == 2){
x0=x;
y0=y;
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(positions),gl.STATIC_DRAW);
gl.clearColor(1,1,1,1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, positions.length / 2);
}else
{
positions .push(x,y);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(positions),gl.STATIC_DRAW);
gl.clearColor(1,1,1,1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.LINES, 0, positions.length / 2);
}
};
canvas.addEventListener('mouseup',handler,false);
document.addEventListener('keydown',e=>{
if(e.key=='c')
{
drawconvex();
}
});
function drawconvex()
{
positions.push(x0,y0);
var pos=[];
var poss=[]
for(var m=0;m<positions.length/2;m++)
{
pos=[positions[2*m],positions[2*m+1]];
poss.push(pos);
}
if(convex(poss,positions.length)==1)
{
//alert("凸集");
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(positions),gl.STATIC_DRAW);
this.show(gl);
//vertexs里面存储了所有顶点的信息,可以利用来拉伸出立方体
//如P1,P2,P3,P4,P5,P6;先画P1,P2,P3;P1,P3,P4;P1,P4,P5;P1,P5,P6四个三角形
//再画对面P11,P12,P13;P11,P13,P14;P11,P14,P15;P11,P15,P16四个三角形
//再画旁边P1,P2,P11;P12,P2,P11;P2,P3,P12;P13,P3,P12;P3,P4,P13;P14,P4,P13;
//P4,P5,P14;P15,P5,P14;P5,P6,P15;P16,P6,P15;P6,P1,P16;P11,P1,P16;十二个三角形
var vers =[];
var cors=[];
for(var m=0;m<vectexs.length/2;m++)
{
vers.push(vectexs[2*m],vectexs[2*m+1],-100);
cors.push(1/(m+1),0,1);
vers.push(vectexs[2*m],vectexs[2*m+1],100);
cors.push(1/(m+1),1,0);
}
var inds=[];
//准备顶点的顺序
for(var m=1;m<vers.length/6-1;m++)
{
inds.push(0,2*m,2*m+2);
}
var btm=vers.length/3;
for(var m=0;m<vers.length/6;m++)
{
inds.push(2*m % btm,(2*m+1) % btm,(2*m+2)% btm);
inds.push((2*m+1)% btm,(2*m+2)% btm,(2*m+3)% btm);
}
for(var m=1;m<vers.length/6-1;m++)
{
inds.push(1,2*m+1,2*m+3);
}
var _Pmatrix;
var _Vmatrix;
var _Mmatrix;
var index_buffer;
// Create and store data into vertex buffer
var vertex_buffer = gl.createBuffer ();
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vers), gl.STATIC_DRAW);
// Create and store data into color buffer
var color_buffer = gl.createBuffer ();
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cors), gl.STATIC_DRAW);
// Create and store data into index buffer
//attribute变量和uniform变量,一次只能传输一个顶点的信息
//缓冲区对象可以一次性的向着色器传入多个顶点的数据
//步骤一:创建buffer
index_buffer = gl.createBuffer ();
//步骤二:绑定buffer
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
//步骤三:数据写入buffer
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(inds), gl.STATIC_DRAW);
/*=================== SHADERS =================== */
var vertCode = 'attribute vec3 position;'+
'attribute vec3 a_Screen_Size;'+
'uniform mat4 Pmatrix;'+
'uniform mat4 Vmatrix;'+
'uniform mat4 Mmatrix;'+
'attribute vec3 color;'+//the color of the point
'varying vec3 vColor;'+
'void main(void) { '+//pre-built function
'vec3 outposition = (position / a_Screen_Size) * 2.0 - 1.0;'+
'gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(outposition, 1.0);'+
'vColor = color;'+
'}';
var fragCode = 'precision mediump float;'+
'varying vec3 vColor;'+
'void main(void) {'+
'gl_FragColor = vec4(vColor, 1.);'+
'}';
var vertShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShader, vertCode);
gl.compileShader(vertShader);
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShader, fragCode);
gl.compileShader(fragShader);
var shaderprogram = gl.createProgram();
gl.attachShader(shaderprogram, vertShader);
gl.attachShader(shaderprogram, fragShader);
gl.linkProgram(shaderprogram);
var a_Screen_Size = gl.getAttribLocation(propgram,'a_Screen_Size');//取到屏幕的大小
gl.vertexAttrib3f(a_Screen_Size,canvas.width,canvas.height,100);//设置参数
/*======== Associating attributes to vertex shader =====*/
_Pmatrix = gl.getUniformLocation(shaderprogram, "Pmatrix");
_Vmatrix = gl.getUniformLocation(shaderprogram, "Vmatrix");
_Mmatrix = gl.getUniformLocation(shaderprogram, "Mmatrix");
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
var _position = gl.getAttribLocation(shaderprogram, "position");
//步骤四:buffer对象分配给一个attribute变量
gl.vertexAttribPointer(_position, 3, gl.FLOAT, false,0,0);
//步骤五:开启attribute变量
gl.enableVertexAttribArray(_position);
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
var _color = gl.getAttribLocation(shaderprogram, "color");
gl.vertexAttribPointer(_color, 3, gl.FLOAT, false,0,0) ;
gl.enableVertexAttribArray(_color);
gl.useProgram(shaderprogram);
//透视矩阵
//40-y方向视角,canvas.width/canvas.height-宽高比,1-Znear,100-Zfar
//var proj_matrix = get_projection(40, canvas.width/canvas.height, 1, 100);
//正交投影
var proj_matrix = orth_projection(-1,1,-1,1,1,100);
//模型举证
var mo_matrix = [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ];
//视图矩阵
var view_matrix = [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ];
view_matrix[14] = view_matrix[14]-6;
/*================= Mouse events ======================*/
var AMORTIZATION = 0.95;
var drag = false;
var old_x, old_y;
var dX = 0, dY = 0;
var THETA = 0,
PHI = 0;
var time_old = 0;
var mouseDown = function(e) {
drag = true;
old_x = e.pageX, old_y = e.pageY;
e.preventDefault();
return false;
};
var mouseUp = function(e){
drag = false;
};
var mouseMove = function(e) {
if (!drag) return false;
dX = (e.pageX-old_x)*2*Math.PI/canvas.width,
dY = (e.pageY-old_y)*2*Math.PI/canvas.height;
THETA+= dX;
PHI+=dY;
old_x = e.pageX, old_y = e.pageY;
e.preventDefault();
};
canvas.removeEventListener('mouseup',handler,false);
canvas.addEventListener("mousedown", mouseDown, false);
canvas.addEventListener("mouseup", mouseUp, false);
canvas.addEventListener("mouseout", mouseUp, false);
canvas.addEventListener("mousemove", mouseMove, false);
var animate = function(time) {
var dt = time-time_old;
if (!drag) {
dX *= AMORTIZATION, dY*=AMORTIZATION;
THETA+=dX, PHI+=dY;
}
//set model matrix to I4
mo_matrix[0] = 1, mo_matrix[1] = 0, mo_matrix[2] = 0,
mo_matrix[3] = 0,
mo_matrix[4] = 0, mo_matrix[5] = 1, mo_matrix[6] = 0,
mo_matrix[7] = 0,
mo_matrix[8] = 0, mo_matrix[9] = 0, mo_matrix[10] = 1,
mo_matrix[11] = 0,
mo_matrix[12] = 0, mo_matrix[13] = 0, mo_matrix[14] = 0,
mo_matrix[15] = 1;
rotateY(mo_matrix, THETA);
rotateX(mo_matrix, PHI);
time_old = time;
gl.enable(gl.DEPTH_TEST);
// gl.depthFunc(gl.LEQUAL);
gl.clearColor(1, 1, 1, 0.9);
gl.clearDepth(1.0);
gl.viewport(0.0, 0.0, canvas.width, canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.uniformMatrix4fv(_Pmatrix, false, proj_matrix);
gl.uniformMatrix4fv(_Vmatrix, false, view_matrix);
gl.uniformMatrix4fv(_Mmatrix, false, mo_matrix);
//绑定index_buffer
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
//用index_buffer中的顶点画模型
gl.drawElements(gl.TRIANGLES, inds.length, gl.UNSIGNED_SHORT, 0);
window.requestAnimationFrame(animate);
}
animate(0);
}else if(convex(poss,positions.length)==-1)
{
alert("凹集");
}else if(convex(poss,positions.length)==0)
{
alert("曲线不符合要求,无法计算");
}
}
const buttonext = document.getElementById('btne');
buttonext.onclick = () => {
drawconvex();
}
// 获取按钮
const button = document.getElementById('btn')
// 给按钮添加点击事件
button.onclick = () => {
// 要保存的字符串
var stringData ='您输入的坐标是:';
for(var m=0;m<vectexs.length;m++)
{
if(m % 2==1)
{
stringData=stringData+vectexs[m]+";";
}else
{
stringData=stringData+vectexs[m]+"-";
}
}
// dada 表示要转换的字符串数据,type 表示要转换的数据格式
const blob = new Blob([stringData], {
type: "text/plain;charset=utf-8"
})
// 根据 blob生成 url链接
const objectURL = URL.createObjectURL(blob)
// 创建一个 a 标签Tag
const aTag = document.createElement('a')
// 设置文件的下载地址
aTag.href = objectURL
// 设置保存后的文件名称
aTag.download = "文本文件.txt"
// 给 a 标签添加点击事件
document.body.appendChild(aTag)
let evt = document.createEvent("MouseEvents");
evt.initEvent("click", true, true);
aTag.dispatchEvent(evt);
document.body.removeChild(aTag);
}
/*=========================rotation================*/
function rotateX(m, angle) {
var c = Math.cos(angle);
var s = Math.sin(angle);
var mv1 = m[1], mv5 = m[5], mv9 = m[9];
m[1] = m[1]*c-m[2]*s;
m[5] = m[5]*c-m[6]*s;
m[9] = m[9]*c-m[10]*s;
m[2] = m[2]*c+mv1*s;
m[6] = m[6]*c+mv5*s;
m[10] = m[10]*c+mv9*s;
}
function rotateY(m, angle) {
var c = Math.cos(angle);
var s = Math.sin(angle);
var mv0 = m[0], mv4 = m[4], mv8 = m[8];
m[0] = c*m[0]+s*m[2];
m[4] = c*m[4]+s*m[6];
m[8] = c*m[8]+s*m[10];
m[2] = c*m[2]-s*mv0;
m[6] = c*m[6]-s*mv4;
m[10] = c*m[10]-s*mv8;
}
function orth_projection(Lpos,Rpos,Tpos,Bpos,Znear,Zfar)
{
return [2/(Rpos-Lpos),0,0,0,
0,2/(Tpos-Bpos),0,0,
0,0,1/(Zfar-Znear),0,
-(Rpos+Lpos)/(Rpos-Lpos),-(Tpos+Bpos)/(Tpos-Bpos),-Znear/(Zfar-Znear),1];
}
function get_projection(angle, a, zMin, zMax) {
var ang = Math.tan((angle*.5)*Math.PI/180);//angle*.5
return [
1/ang/a, 0 , 0, 0,
0, 1/ang, 0, 0,
0, 0, -(zMax+zMin)/(zMax-zMin), -1,
0, 0, (-2*zMax*zMin)/(zMax-zMin), 0
];
}
function abs(x,y)
{
if(x>y)
{
return x-y;
}else
{
return y-x;
}
}
function circle()
{
alert("Hi");
//r*cos(60*m),r*sin(60*m)
}
function convex(p,n){
var j,k,z;
var flag=0;
if(n<3){
return 0;
}
for(var i=0;i<n/2-1;i++)
{
j=(i+1)%(n/2-1);
k=(i+2)%(n/2-1);
z=(p[j][0]-p[i][0])*(p[k][1]-p[j][1]);
z-=(p[j][1]-p[i][1])*(p[k][0]-p[j][0]);
if(z<0)
{
flag|=1;
}else if(z>0)
{
flag|=2;
}
if(flag==3)
{
return -1;
}
}
if(flag!=0)
{
return 1;
}else
{
return 0;
}
}
// this.show(gl);
function show(gl) {
gl.clearColor(1,1,1,1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.LINES, 0, positions.length / 2);
}
//随机取到相关的颜色
function randomColor() {
return {
r: Math.random() * 255,
g: Math.random() * 255,
b: Math.random() * 255,
a: 1 * 1
};
}
</script>
</body>
</html>
下面理出一些基本的概念,方便大家研究理解,请在浏览器中打开浏览:
<html>
<head>
<meta charset="UTF-8">
<title>基本概念</title>
<style>
.tabx{border-collapse:collapse;border-style: solid;border-width: 1px;text-align:center;}
.tdx{border-style: solid;border-width: 1px;}
</style>
</head>
<body>
<table>
<tr><td>齐次坐标有四个值:X,Y,Z,1,数据类型是vec4</td></tr>
<tr><td>OPENGL使用右手坐标系</td></tr>
<tr><td>4*4矩阵的数据类型是mat4</td></tr>
<tr><td>单位矩阵一条对角线上的值为1,其余值全为0</td></tr>
<tr><td>任何值乘以单位矩阵都不会改变</td></tr>
<tr><td sty<td>右边矩阵是左边矩阵的转置,函数是transpose(mat4)</td></tr>
</table>
</br>
<table class='tabx'>
<tr><td style='color:red;'>A00</td><td>A01</td><td>A02</td><td>A03</td> <td rowspan='4' class='tdx'>transpose</td><td style='color:red;'>A00</td><td style='color:red;'>A10</td><td style='color:red;'>A20</td><td style='color:red;'>A30</td></tr>
<tr><td style='color:red;'>A10</td><td>A11</td><td>A12</td><td>A13</td> <td>A01</td><td>A11</td><td>A21</td><td>A31</td></tr>
<tr><td style='color:red;'>A20</td><td>A21</td><td>A22</td><td>A23</td> <td>A02</td><td>A12</td><td>A22</td><td>A32</td></tr>
<tr><td style='color:red;'>A30</td><td>A31</td><td>A32</td><td>A33</td> <td>A03</td><td>A13</td><td>A23</td><td>A33</td></tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 矩阵加法 </td><td>A+a</td><td>B+b</td><td>C+c</td><td>D+d</td><td rowspan='4' class='tdx'> = </td><td>A</td><td>B</td><td>C</td><td>D</td><td rowspan='4' class='tdx'> + </td><td>a</td><td>b</td><td>c</td><td>d</td></tr>
<tr><td>E+e</td><td>F+f</td><td>G+g</td><td>H+h</td> <td>E</td><td>F</td><td>G</td><td>H</td> <td>e</td><td>f</td><td>g</td><td>h</td></tr>
<tr><td>I+i</td><td>J+j</td><td>K+k</td><td>L+l</td> <td>I</td><td>J</td><td>K</td><td>L</td> <td>i</td><td>j</td><td>k</td><td>l</td></tr>
<tr><td>M+m</td><td>N+n</td><td>O+o</td><td>P+p</td> <td>M</td><td>N</td><td>O</td><td>P</td> <td>m</td><td>n</td><td>o</td><td>p</td></tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 点与矩阵相乘 </td><td>A</td><td>B</td><td>C</td><td>D</td><td rowspan='4' class='tdx'> * </td><td>X</td><td rowspan='4' class='tdx'> = </td><td>AX+BY+CZ+D</td></tr>
<tr><td>E</td><td>F</td><td>G</td><td>H</td> <td>Y</td> <td>EX+FY+GZ+H</td></tr>
<tr><td>I</td><td>J</td><td>K</td><td>L</td> <td>Z</td> <td>IX+JY+KZ+L</td></tr>
<tr><td>M</td><td>N</td><td>O</td><td>P</td> <td>1</td> <td>MX+NY+OZ+P</td></tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 矩阵乘法 </td><td>A</td><td>B</td><td>C</td><td>D</td><td rowspan='4' class='tdx'> * </td><td>a</td><td>b</td><td>c</td><td>d</td><td rowspan='4' class='tdx'> = </td><td>Aa+Be+Ci+Dm</td><td>Ab+Bf+Cj+Dn</td><td>Ac+Bg+Ck+Do</td><td>Ad+Bh+Cl+Dp</td></tr>
<tr><td>E</td><td>F</td><td>G</td><td>H</td> <td>e</td><td>f</td><td>g</td><td>h</td> <td>Ea+Fe+Gi+Hm</td><td>Eb+Ff+Gj+Hn</td><td>Ec+Fg+Gk+Ho</td><td>Ed+Fh+Gl+Hp</td></tr>
<tr><td>I</td><td>J</td><td>K</td><td>L</td> <td>i</td><td>j</td><td>k</td><td>l</td> <td>Ia+Je+Ki+Lm</td><td>Ib+Jf+Kj+Ln</td><td>Ic+Jg+Kk+Lo</td><td>Id+Jh+Kl+Lp</td></tr>
<tr><td>M</td><td>N</td><td>O</td><td>P</td> <td>m</td><td>n</td><td>o</td><td>p</td> <td>Ma+Ne+Oi+Pm</td><td>Mb+Nf+Oj+Pn</td><td>Mc+Ng+Ok+Po</td><td>Md+Nh+Ol+Pp</td></tr>
</table>
</br>
<table>
<tr><td>如果Point2=M1*(M2*(M3*Point1))</td></tr>
<tr><td>则Point2=(M1*M2*M3)*Point1,这可以显著减少矩阵运算量</td></tr>
<tr><td>对于M*M-1=M-1*M=单位矩阵,M-1叫做逆矩阵,公式为mat4.inverse()</td></tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 平移矩阵 glm::translate(x,y,z)</td><td>X+TX</td><td rowspan='4' class='tdx'> = </td><td>1</td><td>0</td><td>0</td><td>TX</td><td rowspan='4' class='tdx'> * </td><td>X</td></tr>
<tr><td>Y+TY</td><td>0</td><td>1</td><td>0</td><td>TY</td> <td>Y</td> </tr>
<tr><td>Z+TZ</td><td>0</td><td>0</td><td>1</td><td>TZ</td> <td>Z</td> </tr>
<tr><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td> <td>1</td> </tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 缩放矩阵 glm::scale(x,y,z)</td><td>X*SX</td><td rowspan='4' class='tdx'> = </td><td>SX</td><td>0</td><td>0</td><td>0</td><td rowspan='4' class='tdx'> * </td><td>X</td></tr>
<tr><td>Y*SY</td><td>0</td><td>SY</td><td>0</td><td>0</td> <td>Y</td> </tr>
<tr><td>Z*SZ</td><td>0</td><td>0</td><td>SZ</td><td>0</td> <td>Z</td> </tr>
<tr><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td> <td>1</td> </tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 缩放矩阵用来切换坐标系</td><td>1</td><td>0</td><td>0</td><td>0</td></tr>
<tr><td>0</td><td>1</td><td>0</td><td>0</td> </tr>
<tr><td>0</td><td>0</td><td>-1</td><td>0</td> </tr>
<tr><td>0</td><td>0</td><td>0</td><td>1</td> </tr>
</table>
</br>
<table>
<tr><td colspan='9'>旋转需要指定旋转轴和旋转角度,围绕任何轴的旋转都可以表示为绕X,Y,Z轴旋转的组合,旋转角度被称为欧拉角</td></tr>
<tr><td colspan='9'>3D空间旋转轴不经过原点时,1.平移旋转轴,使它经过原点,2.绕X,Y,Z旋转,3.复原步骤1中的平移</td></tr>
<tr><td colspan='9'>计算旋转推荐使用四元数,用来消除3D应用中的一些瑕疵</td></tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 绕X轴旋转矩阵 glm::rotate()</td><td>X'</td><td rowspan='4' class='tdx'> = </td><td>1</td><td>0</td><td>0</td><td>0</td><td rowspan='4' class='tdx'> * </td><td>X</td></tr>
<tr><td >Y'</td><td>0</td><td>cosθ</td><td>-sinθ</td><td>0</td> <td>Y</td> </tr>
<tr><td>Z'</td><td>0</td><td>sinθ</td><td>cosθ</td><td>0</td> <td>Z</td> </tr>
<tr><td >1</td><td>0</td><td>0</td><td>0</td><td>1</td> <td>1</td> </tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 绕Y轴旋转矩阵 glm::rotate()</td><td>X'</td><td rowspan='4' class='tdx'> = </td><td>cosθ</td><td>0</td><td>sinθ</td><td>0</td><td rowspan='4' class='tdx'> * </td><td>X</td></tr>
<tr><td>Y'</td><td>0</td><td>1</td><td>0</td><td>0</td> <td>Y</td> </tr>
<tr><td>Z'</td><td>-sinθ</td><td>0</td><td>cosθ</td><td>0</td> <td>Z</td> </tr>
<tr><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td> <td>1</td> </tr>
</table>
</br>
<table class ='tabx'>
<tr><td rowspan='4' class='tdx'> 绕Z轴旋转矩阵 glm::rotate()</td><td>X'</td><td rowspan='4' class='tdx'> = </td><td>cosθ</td><td>-sinθ</td><td>0</td><td>0</td><td rowspan='4' class='tdx'> * </td><td>X</td></tr>
<tr><td>Y'</td><td>sinθ</td><td>cosθ</td><td>0</td><td>0</td> <td>Y</td> </tr>
<tr><td>Z'</td><td>0</td><td>0</td><td>1</td><td>0</td> <td>Z</td> </tr>
<tr><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td> <td>1</td> </tr>
</table>
</br>
<table>
<tr><td>向量表示大小和方向,没有特定位置。</td></tr>
<tr><td>一般用空间中的单个点来表示向量,向量的大小是原点到该点的距离,方向是原点到该点的方向。</td></tr>
<tr><td>点和向量没有很大的区别,vec3/vec4既可以表示点,也可以表示向量</td></tr>
<tr><td>向量的加减法,如果A(u,v,w),B(x,y,z),A+B=(u+x,v+y,w+z),A-B=(u-x,v-y,w-z)。</td></tr>
<tr><td>向量归一化normalize(变长度为1):A/sqrt(u*u+v*v+w*w)。</td></tr>
<tr><td>向量点积dot:A●B=ux+vy+wz。最大的用处是计算两个向量夹角:A●B=|A||B|cos(θ)</td></tr>
<tr><td>向量叉积cross:AxB=(uz-wy,wx-uz,uy-vx)。叉积的一个重要特点:新生成一个向量,该向量正交与前面两个向量所定义的平面</td></tr>
<tr><td>法向量的计算:假设一个三角形有三个逆时针方向的顶点v1,v2,v3,向量P=v2-v1;向量q=v3-v1;外法向量N=PxQ。</td></tr>
</table>
</body>
</html>