zoukankan      html  css  js  c++  java
  • 初级入门 --- web GL绘制点

    万丈高楼平地起。

    01基础知识

    一、相关术语

    • 图元 :WebGL 能够绘制的基本图形元素,包含三种:线段三角形
    • 片元:可以理解为像素,像素着色阶段是在片元着色器中。
    • 裁剪坐标系:裁剪坐标系是顶点着色器中的 gl_Position 内置变量接收到的坐标所在的坐标系。
    • 设备坐标系:又名 NDC 坐标系,是裁剪坐标系各个分量对 w 分量相除得到的坐标系,特点是 x、y、z 坐标分量的取值范围都在 【-1,1】之间,可以将它理解为边长为 2 的正方体,坐标系原点在正方体中心。

    二、GLSL



    • gl_Position:内置变量,用来设置顶点的裁剪坐标系坐标,包含 X, Y, Z,W 四个坐标分量。顶点着色器接收到这个坐标之后,对它进行透视除法,即将各个分量同时除以 W,转换成 NDC 坐标,NDC 坐标每个分量的取值范围都在【-1, 1】之间,GPU 获取这个属性值作为顶点的最终位置进行绘制。
    • gl_PointSize:内置变量,用来设置顶点大小。只有在绘制图元是的时候才会生效。
    • gl_FragColor:片元(像素)颜色,包含 R, G, B, A 四个颜色分量,且每个分量的取值范围在【0,1】之间,GPU 获取这个值作为像素的最终颜色进行着色。
    • gl_FragColor:内置变量,用来设置像素颜色。
    • attribue 变量:只能在顶点着色器中定义。
    • uniform 变量:既可以在顶点着色器中定义,也可以在片元着色器中定义。
    • 最后一种变量类型 varing 变量:它用来从顶点着色器中往片元着色器传递数据。使用它我们可以在顶点着色器中声明一个变量并对其赋值,经过插值处理后,在片元着色器中取出插值后的值来使用。
    • vec4:4 维向量,包含四个浮点元素的容器类型,vec 是 vector(向量)的单词简写,vec4 代表包含 4 个浮点数的向量。此外,还有 vec2vec3 等类型,代表包含2个或者3个浮点数的容器。
    • precision:精度设置限定符,使用此限定符设置完精度后,之后所有该数据类型都将沿用该精度,除非单独设置。
    • 运算符:向量的对应位置进行运算,得到一个新的向量。
      • vec * 浮点数:vec2(x, y) * 2.0 = vec(x * 2.0, y * 2.0)。
      • vec2 * vec2:vec2(x1, y1) * vec2(x2, y2) = vec2(x1 * x2, y1 * y2)。
      • 加减乘除规则基本一致。但是要注意一点,如果参与运算的是两个 vec 向量,那么这两个 vec 的维数必须相同。


    GLSL 中 gl_Position 所接收的坐标所在坐标系是裁剪坐标系 ,不同于我们的浏览器窗口坐标系。所以当我们赋予 gl_Position 位置信息的时候,需要对其进行转换才能正确显示。


    三、JAVASCRIPT程序如何向连接着色器程序


    • createShader:创建着色器对象
    • shaderSource:提供着色器源码
    • compileShader:编译着色器对象
    • createProgram:创建着色器程序
    • attachShader:绑定着色器对象
    • linkProgram:链接着色器程序
    • useProgram:启用着色器程序


    四、JAVASCRIPT如何往着色器中传递数据


    • getAttribLocation:找到着色器中的 attribute 变量地址。
    • getUniformLocation:找到着色器中的 uniform 变量地址。
    • vertexAttrib2f:给 attribute 变量传递两个浮点数。
    • uniform4f:给uniform变量传递四个浮点数。


    五、WebGL 绘制函数


    • drawArrays: 用指定的图元进行绘制。

    gl.drawArrays 是执行绘制的 API,上面示例中的第一个参数 gl.POINTS 代表我们要绘制的是点图元,第二个参数代表要绘制的顶点的起始位置,第三个参数代表顶点绘制个数。


    六、WebGL 图元


    • gl.POINTS: 将绘制图元类型设置成点图元

    02代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>WebGL 绘制点</title>
    <script type="x-shader/x-vertex" id="vertexShader">
    //设置浮点数精度为中等精度
    precision mediump float;
    // 接收点在canvas坐标系上的坐标(x,y)
    attribute vec2 a_Position;
    // 接受canvas 的宽高尺寸
    attribute vec2 a_Screen_Size;
    void main(){
    //start 将屏幕坐标系转化为裁剪坐标(裁剪坐标系)
    vec2 position = (a_Position / a_Screen_Size) * 2.0 - 1.0;
    position = position * vec2(1.0, -1.0);
    gl_Position = vec4(position, 0.0, 1.0);
    //end 将屏幕坐标系转化为裁剪坐标(裁剪坐标系)
    //声明要绘制的点的大小。
    gl_PointSize = 10.0;
    }
    </script>
    <script type="x-shader/x-fragment" id="fragmentShader">
    //设置浮点数精度为中等精度
    precision mediump float;
    //接收 JavaScript 传过来的颜色值(RGBA)。
    uniform vec4 u_Color;
    void main(){
    //将普通的颜色表示转化为 WebGL 需要的表示方式,即将【0-255】转化到【0,1】之间。
    vec4 color = u_Color / vec4(255, 255, 255, 1);
    gl_FragColor = color;
    }
    </script>
    </head>
    <body>
    <canvas id="canvas"></canvas>
    </body>
    </html>
    <script>
    function createShaderFromScript(gl, mode, id) {
    // 获取着色器源码
    var shaderSource = document.getElementById(id).innerHTML;
    // 创建着色器对象
    var shader = gl.createShader(mode);
    // 将源码分配给着色器对象
    gl.shaderSource(shader,shaderSource);
    // 编译着色器
    gl.compileShader(shader);

    return shader;
    }
    function createProgram(gl, vertexShader, fragmentShader) {
    var program = gl.createProgram();
    // 将顶点着色器挂载在着色器程序上。
    gl.attachShader(program, vertexShader);
    //将片元着色器挂载在着色器程序上。
    gl.attachShader(program, fragmentShader);
    //链接着色器程序
    gl.linkProgram(program);

    return program;
    }

    var canvas = document.getElementById('canvas');
    var gl = canvas.getContext('webgl') || canvas.getContext("experimental-webgl");

    <!--region 创建着色器对象-->
    var vertexShader = createShaderFromScript(gl, gl.VERTEX_SHADER, 'vertexShader');
    var fragmentShader = createShaderFromScript(gl, gl.FRAGMENT_SHADER, 'fragmentShader')
    <!--endregion-->



    <!--region 创建着色器程序-->
    //创建着色器程序
    let program = createProgram(gl, vertexShader, fragmentShader);

    // 使用刚刚创建好的着色器程序
    gl.useProgram(program);
    <!--endregion-->

    //找到顶点着色器中的变量a_Position
    var a_Position = gl.getAttribLocation(program, 'a_Position');
    //找到顶点着色器中的变量a_Screen_Size
    var a_Screen_Size = gl.getAttribLocation(program, 'a_Screen_Size');
    //找到片元着色器中的变量u_Color
    var u_Color = gl.getUniformLocation(program, 'u_Color');
    //为顶点着色器中的 a_Screen_Size 传递 canvas 的宽高信息
    gl.vertexAttrib2f(a_Screen_Size, canvas.width, canvas.height);
    //存储点击位置的数组。
    var points = [];
    function randomColor(){
    return {
    r: Math.ceil(Math.random() * 255),
    g: Math.ceil(Math.random() * 255),
    b: Math.ceil(Math.random() * 255),
    a: Math.ceil(Math.random()),
    };
    }
    canvas.addEventListener('click',e=>{
    var x = e.pageX;
    var y = e.pageY;

    var color = randomColor();

    points.push({x,y,color});
    gl.clearColor(0, 0, 0, 1.0);
    //用上一步设置的清空画布颜色清空画布。
    gl.clear(gl.COLOR_BUFFER_BIT);

    for (let i = 0; i < points.length; i++) {
    let color = points[i].color;
    gl.uniform4f(u_Color, color.r, color.g, color.b, color.a);
    //为顶点着色器中的 a_Position 传递顶点坐标。
    gl.vertexAttrib2f(a_Position, points[i].x, points[i].y);

    // 绘制点
    gl.drawArrays(gl.POINTS, 0, 1);
    }
    })

    //设置清空画布颜色为黑色。
    gl.clearColor(0.0,0.0,0.0,1.0);
    //用上一步设置的清空画布颜色清空画布。
    gl.clear(gl.COLOR_BUFFER_BIT);

    </script>


    vec2 position = (a_Position / a_Screen_Size) * 2.0 - 1.0

    上面这句代码用来将浏览器窗口坐标转换成裁剪坐标,之后通过透视除法,除以 w 值(此处为 1 )转变成设备坐标(NDC坐标系)。这个算法首先将(x,y) 转化到【0, 1】区间,再将 【0, 1】之间的值乘以 2 转化到 【0, 2】区间,之后再减去 1 ,转化到 【-1, 1】之间的值,即 NDC 坐标

  • 相关阅读:
    vue通过input选取图片,jq的ajax向服务器上传img
    IDEA常用快捷键
    JavaScript预解析
    jQuery实现颜色打字机
    MVC超链接调用控制器内的方法
    jQuery实现鼠标移入切换图片
    聚类算法
    并行K-Means
    [Err] 1055
    地图匹配实践
  • 原文地址:https://www.cnblogs.com/yaoyinglong/p/12250865.html
Copyright © 2011-2022 走看看