zoukankan      html  css  js  c++  java
  • webgl基础:绘制多边形

    hi~ everybody. 又到了愉快的分享时刻了。

    在前面两节内容中给大家分享了如何通过 webgl 来绘制一个点,并且介绍了着色器的一些基础知识。

    当然了,仅仅只有文字的描述是不能勾起大家学习 webgl 的欲望的,还需要自己动手实现下。观察自己写的代码是如何运作的。

    好了,之前的内容仅仅是绘制一个点,可能内容上略显简单了, 今天就来介绍下如何绘制多边形。

    本节内容中涉及到较多的方法介绍,可先忽略。待有需要的时候查阅。

    1.通过js控制图形

    学习绘制多边形之前,我们先来看一个问题 -- 如何通过js来控制图形的大小、颜色……等内容

    在之前的代码中,都是将这些信息固定的写在了着色器的源码中。类似于以下内容:

    // 顶点着色器源代码
    const VERTEX_SHADER_SOURCE = '' +
          'void main() {' +
          ' gl_Position = vec4(0.0, 0.0, 0.0, 1.0);' +
          ' gl_PointSize = 10.0;' +
          '}'
    
    // 片元着色器源代码
    const FRAGMENT_SHADER_SOURCE = '' +
          'void main() {' +
          ' gl_FragColor = vec4(1.0,0.0,0.0,1.0);' +
          '}'
    

    那如何用js来控制这些信息呢?字符串模板吗?nonono,当然是不行的,因为webgl有自己的渲染管线,之前也给大家介绍过,所以通过字符串模板的形式肯定是不可以的。

    接下来就来看下,如何通过js来控制图形信息。

    1.1 动态传入信息

    动态传入图形信息总共分以下四步进行:

    • 定义一个着色器变量。变量类型与引用位置相同
    • 获取变量信息
    • 传入数据
    • 绘制图形

    实例演示:

    1. 定义一个着色器变量,
    // 这里我们需要定义一个着色器变量 a_PointSize 
    // 变量的类型是 float 因为变量的引用是在 gl_PointSize 中引用的,gl_Position 的类型是 float
    // 其他的信息固定
    const VERTEX_SHADER_SOURCE = '' +
          'attribute float a_PointSize;' + 
          'void main() {' +
          ' gl_Position = vec4(0.0,0.0,0.0,1.0);' +
          ' gl_PointSize = a_PointSize;' +
          '}'
    
    const FRAGMENT_SHADER_SOURCE = '' +
          'void main() {' +
          ' gl_FragColor = vec4(1.0,0.0,0.0,1.0);' +
          '}'
    
    2. 获取变量信息
    // 初始化着色器,并获取程序对象
    const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE);
    // 获取着色器变量
    const a_Position = gl.getAttribLocation(program, 'a_Position')
    
    // 初始化着色器方法
    function initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) {
      const vertexShader = gl.createShader(gl.VERTEX_SHADER)
      const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
    
      gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE)
      gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE)
    
      gl.compileShader(vertexShader)
      gl.compileShader(fragmentShader)
    
      const program = gl.createProgram()
    
      gl.attachShader(program, vertexShader)
      gl.attachShader(program, fragmentShader)
    
      gl.linkProgram(program)
      gl.useProgram(program)
    
      return program
    }
    

    这里将初始化着色器的方法做了一层封装。方便之后取用。

    拓展1:

    • 获取 attribute 变量可以使用 gl.getAttribLocation(program, '变量名称')
    • 获取 uniform 变量可以使用 gl.getUniformLocation(program, '变量名称')
    3. 传入数据

    当准备工作就绪之后,就可以通过 js 传入数据了。

    gl.vertexAttrib1f(a_PointSize, 20.0);
    

    这里通过 gl.vertexAttrib1f(location, v1) 方法传入一个数据。

    注意:因为是 a_PointSize变量是float类型 所以,这里需要传入的是 20.0 而不是 20

    接下来的拓展内容里,会对 gl.vertexAttrib1f 进行详细的介绍。

    4. 绘制图形
    // 添加蓝色底色
    gl.clearColor(0.0,0.0,1.0,1.0)
    gl.clear(gl.COLOR_BUFFER_BIT)
    // 绘制一个点。
    gl.drawArrays(gl.POINTS, 0, 1)
    

    拓展2:

    相对于 gl.vertexAttrib1f 来说,它还有以下几个同族函数:

    gl.vertexAttrib1f(location, x);
    gl.vertexAttrib2f(location, x, y);
    gl.vertexAttrib3f(location, x, y, z);
    gl.vertexAttrib4f(location, x, y, z, w);
    
    gl.vertexAttrib1fv(location, value);
    gl.vertexAttrib2fv(location, value);
    gl.vertexAttrib3fv(location, value);
    gl.vertexAttrib4fv(location, value);
    

    以上方法功能类似,都是属于赋值的方法。只不过是传入的参数不同。

    • location:代表的是需要修改的变量。如:a_Position

    • 方法中不是 fv 结尾的,传入单个值,传入几个就修改几个。剩下的走默认。

    • 方法中fv 结尾的,传入一个数组,数组中有几个就修改几个,剩下的走默认。

    栗子:

    如果要给一个 vec4 类型的 a_Position 的变量赋值。所有方法的设置和结果如下。

    gl.vertexAttrib1f(a_Position, 1.0); 									// [1.0, 0.0, 0.0, 1.0]
    gl.vertexAttrib2f(a_Position, 1.0, 1.0);							// [1.0, 1.0, 0.0, 1.0]
    gl.vertexAttrib3f(a_Position, 1.0, 1.0, 1.0);					// [1.0, 1.0, 1.0, 1.0]
    gl.vertexAttrib4f(a_Position, 1.0, 1.0, 1.0, 1.0);		// [1.0, 1.0, 1.0, 1.0]
    
    gl.vertexAttrib1fv(a_Position, [1.0]); 									// [1.0, 0.0, 0.0, 1.0]
    gl.vertexAttrib2fv(a_Position, [1.0, 1.0]);							// [1.0, 1.0, 0.0, 1.0]
    gl.vertexAttrib3fv(a_Position, [1.0, 1.0, 1.0]);				// [1.0, 1.0, 1.0, 1.0]
    gl.vertexAttrib4fv(a_Position, [1.0, 1.0, 1.0, 1.0]);		// [1.0, 1.0, 1.0, 1.0]
    

    好了,烦人的前缀知识分享完了,恭喜你,能看到这里说明你有很大的耐心了。希望接下来的内容能存储到你的知识体系中。

    2.绘制多边形

    2.1 相关概念介绍

    绘制多边形,需要了解两个概念:

    • 缓冲区对象:

      对于缓冲区对象的介绍有很多,当然,概念这种东西我们是不屑于记忆的, 这里来个通俗点儿的说法:缓冲区对象就是一次性给着色器传入多个点的信息,让着色器做个缓存,之后的绘制不在传入信息,而是根据设定的规则从缓存中取。

    • 类型化数组:

      注意:类型化数组不支持 pushpop 方法

      类型化数组就是一个数组中所有的数据都是相同类型的。类型化数组的分类如下:

      • Int8Array: 8位整数型
      • UInt8Array: 8位无符号整数型
      • Int16Array: 16位整数型
      • UInt16Array: 16位无符号整数型
      • Int32Array: 32位整数型
      • UInt32Array: 32位无符号整数型
      • Float32Array: 单精度32位浮点数
      • Float64Array: 双精度64位浮点数

    这两个内容至关重要,在绘制多边形甚至之后的使用过程中都少不了这两个步骤。

    2.2 绘制多边形的步骤

    • 创建缓冲区对象 -- gl.createBuffer()
    • 绑定缓冲区对象 -- gl.bindBuffer()
    • 将数据写入缓冲区对象 -- gl.bufferData()
    • 将缓冲区对象分配给一个着色器变量 --gl.vertexAttribPointer()
    • 开启attribute变量 -- gl.enableVertexAttribArray()
    1. 创建缓冲区对象 -- gl.createBuffer()
    const buffer = gl.createBuffer();
    

    此方法比较简单,没有参数,返回一个创建好的缓冲区对象。

    2. 绑定缓冲区对象 -- gl.bindBuffer()
    gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
    

    gl.bindBuffer(target, buffer);

    • target 可能出现的值
      • gl.ARRAY_BUFFER:表示使用顶点数据绘制
      • gl.ELEMENT_ARRAY_BUFFER:表示使用索引值绘制。之后的分享中会提到这个方法的使用
    3. 将数据写入缓冲区对象 -- gl.bufferData()
    // 首先创建一个类型化数组
    const data = new Float32Array([
    		-0.5,  0.5,
        -0.5, -0.5,
         0.5,  0.5,
         0.5, -0.5
    ])
    // 将数据写入缓冲区对象
    gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
    

    gl.bufferData(target, data, usage);

    • target:可能出现的值。需要与 gl.bindBuffer(target, buffer) 中的内容相同

      • gl.ARRAY_BUFFER:表示使用顶点数据绘制
      • gl.ELEMENT_ARRAY_BUFFER:表示使用索引值绘制。
    • data:写入缓冲区的数据

    • usage

      • gl.STATIC_DRAW:向缓冲区对象中写入一次,绘制多次
      • gl.STREAM_DRAW:向缓冲区对象中写入一次,绘制若干次
      • gl.DYNAMIC_DRAW:向缓冲区对象中多次写入,绘制多次
    4. 将缓冲区对象分配给一个着色器变量 --gl.vertexAttribPointer()
    // 首先获取着色器变量
    const a_Position = gl.getAttribLocation(program, 'a_Position')
    
    // 将缓冲区对象分配给一个着色器变量
    gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0);
    

    gl.vertexAttribPointer(location, size, type, normalized, stride, offset);

    • location: 指定修改的着色器变量的位置

    • size: 每次绘制需要几个顶点,定义的数据如何分配。

    • type:顶点的数据类型。与类型化数组的类型相同。

      • gl.BYTE:带符号的8位整数
      • gl.SHORT:带符号的16位整数
      • gl.UNSIGNED_BYTE:无符号8位整数
      • gl.UNSIGNED_SHORT:无符号16位整数
      • gl.FLOAT:32位浮点数
    • normalized:是否将浮点数归一化到 [0, 1] 或 [-1, 1] 之间

    • stride:相邻两个顶点间的字节数。默认为 0

    • offset:偏移几个顶点开始绘制

    5. 开启attribute变量 -- gl.enableVertexAttribArray()
    gl.enableVertexAttribArray(a_Position);
    

    gl.enableVertexAttribArray(location);

    • location: 指定修改的着色器变量的位置

    完整版栗子:

    const ctx = document.getElementById('canvas');
    const gl = ctx.getContext('webgl');
    
    const VERTEX_SHADER_SOURCE = '' +
          'attribute vec4 a_Position;' +
          'void main() {' +
          ' gl_Position = a_Position;' +
          ' gl_PointSize = 10.0;' +
          '}'
    
    const FRAGMENT_SHADER_SOURCE = '' +
          'void main() {' +
          ' gl_FragColor = vec4(1.0,0.0,0.0,1.0);' +
          '}'
    
    // 获取程序对象
    const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE);
    
    // 创建缓冲区
    const buffer = gl.createBuffer();
    // 创建类型化数组
    const data = new Float32Array([
      -0.5,  0.5,
      -0.5, -0.5,
      0.5,  0.5,
      0.5, -0.5
    ])
    // 绑定缓冲区
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
    // 向缓冲区写入数据
    gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
    // 获取 attribute 变量
    const a_PointSize = gl.getAttribLocation(program, 'a_Position')
    // 指定顶点数据
    gl.vertexAttribPointer(a_PointSize, 2, gl.FLOAT, false, 0, 0);
    // 启用 attribute 变量
    gl.enableVertexAttribArray(a_PointSize)
    // 绘制多边形
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)
    

    这样,一个精美的四边形就出现在屏幕上了。

    通过给 gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4) 传入不同的绘制方式,会有意想不到的效果哟~

    考考你:

    本文中,定义着色器变量我们使用的 attribute ,还知道其他几个变量 uniform, varying 的使用方法吗?

    好了,今天的内容就分享到这里了。Bye~

  • 相关阅读:
    bat 实现主机hostname的修改
    我的影视作品,如何在博客园发布带有CSS样式的HTML
    java实现Windows记事本
    cmd 执行Dcpromo错误:在该 SKU 上不支持 Active Directory 域服务安装向导,Windows Server 2008 R2 Enterprise 配置AD(Active Directory)域控制器
    警告: 正保存的数据被裁断到 1024 字符。 错误: 拒绝访问注册表路径。
    山西大同大学教务处教师端——可在PC端,手机端操作
    bat脚本+diskpart 脚本实现自动划分磁盘分区
    山西大同大学教务处学生端--送给学弟,学妹的礼物,可在PC端,手机端操作
    ECharts
    javascript删除数组里的对象
  • 原文地址:https://www.cnblogs.com/yancyCathy/p/14325985.html
Copyright © 2011-2022 走看看