我的webgl学习之路(三)用webgl画线

我的webgl学习之路(三)用webgl画线

 

我们都知道两个点决定一条线段;那么我们怎么画两个点呢?甚至是多个点呢?用gl.vertexAttrib3f(a_Position,1.0,0.0,0.0)循环?实在太麻烦了;那么不得不说的缓冲区对象;

 

缓冲区:它是webgl提供的一种很方便的机制,它可以一次地向着色器传入多个顶点数据。缓冲区对象是webgl系统中的一块内存区域,我们可以一次性地向缓冲区对象中填充大量的顶点数据,然后将这些数据保存在其中,供顶点着色器使用。

 

代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>多个点绘制—缓冲区</title>
    <script id="vs" type="x-shader/x-vertex">
        attribute vec4 a_Position;
        void main(void){
            gl_PointSize = 20.0;
            gl_Position = a_Position;
        }
    </script>
    <script id="fs" type="x-shader/x-fragment">
        void main(void){
            gl_FragColor =vec4(1.0,0.0,0.0,1.0);
        }
    </script>
</head>
<body>
<canvas id="canvas" style="background-color:black"></canvas>

<script>
    onload =function () {
        var canvas = document.getElementById("canvas");
        canvas.width = 500;
        canvas.height = 500;
        var gl = canvas.getContext("webgl");

        /*清空画板上的颜色,并初始化颜色*/
       
gl.clearColor(0.0, 0.0, 0.0, 1.0);
        //设定canvas初始化时候的深度
       
gl.clearDepth(1.0);
        //清空画面上的颜色
       
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

        //顶点着色器和片段着色器生成
       
var v_shader= create_shader("vs");
        var f_shader = create_shader("fs");

        // 程序对象的生成和连接
       
var program= create_program(v_shader,f_shader);
        //获取a_Position变量的存储位置
       
var a_Position= gl.getAttribLocation(program,"a_Position");

        //创建缓冲区
   
    
var vertices= new Float32Array([
            0.0,0.5,-0.5,
            -0.5,0.5,-0.5
       
]);
        var n = 3;//点的个数

       
//创建缓冲区
       
var vertexBuffer= gl.createBuffer();
        if(!vertexBuffer){
            console.log("Failed to createthe buffer object");//缓冲区创建失败
           
return -1;
        }

        //将缓冲区绑定到目标对象
       
gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer);

        //向缓冲区写入数据
       
gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW);

        //将缓冲区对象分配给a_Position; 参数一:传入数据;参数二:指定每个顶点传入多少个数(2表示只取两个数传入,剩下的两个数0.01.0补上;1.0是透明度,跟vertexAttrib2f()类似)
       
gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0);

        //连接a_Position变量与分配给它的缓冲区对象
       
gl.enableVertexAttribArray(a_Position);

        gl.clearColor(0.0,0.0,0.0,1.0);
        gl.clear(gl.COLOR_BUFFER_BIT);
        //开始绘制,显示器显示结果;参数二:从哪个点开始绘制;参数三:绘制几个点
       
gl.drawArrays(gl.POINTS, 0, n);

        functioncreate_program(v_shader, f_shader) {
            varprogram = gl.createProgram();

            gl.attachShader(program, v_shader);
            gl.attachShader(program, f_shader);

            gl.linkProgram(program);
            gl.useProgram(program);
            returnprogram;
        }

        functioncreate_shader(id) {
            //用来保存着色器的变量
           
var shader;

            //根据idHTML中获取指定的script标签
           
var scriptElement = document.getElementById(id);

            //如果指定的script标签不存在,则返回
           
if (!scriptElement) {
                return;
            }

            // 判断script标签的type属性
           
switch (scriptElement.type){

                // 顶点着色器的时候
               
case 'x-shader/x-vertex':
                    shader = gl.createShader(gl.VERTEX_SHADER);//生成顶点着色器
                   
break;

                // 片段着色器的时候
               
case 'x-shader/x-fragment':
                    shader = gl.createShader(gl.FRAGMENT_SHADER);//生成片元着色器
                   
break;
                default :
                    return;
            }

            //将标签中的代码分配给生成的着色器
           
gl.shaderSource(shader, scriptElement.text);

            //编译着色器
           
gl.compileShader(shader);

            //判断一下着色器是否编译成功
           
if (gl.getShaderParameter(shader,gl.COMPILE_STATUS)) {

                // 编译成功,则返回着色器
        
       
return shader;
            } else{

                // 编译失败,弹出错误消息
               
alert(gl.getShaderInfoLog(shader));
            }
        }
        functioncreate_program(vs, fs) {
            //程序对象的生成
           
var program = gl.createProgram();

            //向程序对象里分配着色器
           
gl.attachShader(program, vs);
            gl.attachShader(program, fs);

            //将着色器连接
           
gl.linkProgram(program);

            //判断着色器的连接是否成功
           
if (gl.getProgramParameter(program,gl.LINK_STATUS)) {

                // 成功的话,将程序对象设置为有效
               
gl.useProgram(program);

                // 返回程序对象
               
return program;
            } else{

                // 如果失败,弹出错误信息
               
alert(gl.getProgramInfoLog(program));
            }
        }
    }
</script>
</body>
</html>

 

相比上一节中,代码并没有多多少;gl_drawArrays(gl.POINTS,0,n);n为3,这里是我们要绘制点的个数;

 

 

使用缓冲区对象的流程:

1、创建缓冲区对象(gl.createBuffer());

2、把缓冲区对象(gl.bindBuffer())绑定到目标对象上(gl.ARRAY_BUFFER);

3、将数据写入缓冲区对象(gl.bufferData());

4、将缓冲区对象分配给attribute变量(gl.vertexAttribPointer());

5、开启attribute变量(gl.enableVertexAttribrray());

Vertices 是我们保存的顶点数据,我们并没有把z坐标传入;

 

流程如下图;

我的webgl学习之路(三)用webgl画线

 

gl.bindBuffer(参数一,参数二)

参数一:它有两个基本的参数:gl.ARRAY_BUFFER :表示缓冲区对象中包含了顶点的数据;和gl.ELEMENT_ARRAY_BUFFER:表示缓冲区对象中包含了顶点索引(有些重复的点我们就可以用索引重复利用,减少数据过大),

参数二:需要绑定的缓冲区对象

 

gl.bufferData();

       参数一:目标对象,gl.ARRAY_BUFFERgl.ELEMENT_ARRAY_BUFFER

       参数二:顶点数据,

参数三:绘制方式;gl.STATIC_DRAW:只会向缓冲区对象中写入一次数据,但需要绘制很多次;gl.STREAM_DRAW只会向缓冲区对象中写入数据,然后会绘制若干次;gl.DYNAMIC_DRAW会向缓冲区对象中多次写入数据,并绘制很多次;为什么会绘制很多次,那是因为它会对你传入的数据一个顶点一个的取然后绘制;

 

gl.vertexAttribPointer();

       参数一:指定分配attribute变量的存储位置;

       参数二:指定缓冲区中每个顶点的分量个数(14)。若sizeattribute变量需要的分量数小,缺失分量将按照与gl.vertexAttrib[1234]f()相同的规则补齐;如:size1,那么第2,3会自动补为0.0,第41.0

       参数三:用一下类型之一来指定数据格式;无符号字节Uint8Arraygl.SHORT短整形;gl.UNSIGNED_SHORT无符号短整型;gl.INT整型;gl.UNSIGNED_INT无符号整型;gl.FLOAT浮点型;

       参数四:truefalse,表明是否将非浮点型的数据归一化到[-1,1]的区间,

       参数五:指定相邻两个顶点间的字节数,默认为0

       参数六:从什么位置开始存储;

 

 

如果你想绘制线你可以把gl.drawArrays();参数一改成gl.LINES或者gl.LINE_STRIPgl.LINE_LOOP试试效果是什么样的;

//LINES:线段;两个点绘制一条线段
//LINE_STRIP:一条连接的线;绘制方式(v0,v1),(v1,v2),(v2,v3)
//LINE_LOOP:封闭的连线,起点和终点连接在一起;

 

它的整个执行如下图:

我的webgl学习之路(三)用webgl画线

 

你可以试着改一些数据,让它变动,你就更加明白是怎么回事了;