zoukankan      html  css  js  c++  java
  • webgl基础:顶点到片元的联动

    继前期分享了 初入webgl 的一些内容之后,相信大家已经对webgl有了一个初步的认识,今天再来分享一些基础内容,已加深大家对于它的认知。

    众所周知,3d 的内容是比较多的,也很容易让人看的很迷惑。所以,此系列文章的分享理念就一个 -- 说透彻,说清楚,说明白。

    好了,话不多说,进入今天的分享内容。

    1. 前言:

    分享之前先来回顾一下两个内容:

    • webgl 中有几种类型的变量?
    • 如何使用缓冲区对象向着色器传递数据?

    也希望大家带着这两个问题来阅读本篇文章。

    2. varying变量的使用

    前边的文章中介绍了 webgl 中的数据类型,现在我们来重温一下:

    • attribute:影响单个顶点
    • uniform:影响全部顶点
    • varying:由顶点向片元传递数据

    在之前文章的实例中也使用了 attribute、uniform 这两个变量。相信对于这两个变量的使用都有了一定程度的认识。但是对于最后一个 varying 变量,之前的文章中从未使用过。

    由介绍可得知,varying 变量的主要作用就是从顶点着色器向片元着色器传值。具体怎么使用呢?莫慌,一步步来看下面的实现:

    2.1 绘制一个点,(老代码,回顾一下。无新知识)

    const VERTEX_SHADER_SOURCE = '' +
          'attribute vec4 apos;' +
          'void main () {' +
          ' gl_Position = apos;' +
          ' gl_PointSize = 10.0;' +
          '}'
    
    const FRAGMENT_SHADER_SOURCE = '' +
          'precision lowp float;' +
          'varying vec4 vColor;' +
          '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([
      -1.0, -1.0,
      1.0, -1.0,
      0.0, 1.0,
    ])
    
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
    gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
    
    const apos = gl.getAttribLocation(program, 'apos');
    gl.vertexAttribPointer(apos, 2, gl.FLOAT, false, 0, 0)
    gl.enableVertexAttribArray(apos)
    
    gl.drawArrays(gl.TRIANGLES, 0, 3)
    

    这段代码相信大家已经非常熟悉,在画布中绘制一个红色的三角形,对于其中用到的方法也可以查下之前的文章。这里就不赘述了。

    2.2 使用 varying 传递数据

    使用 varying 传递数据需要分以下几步:

    • 顶点着色器中创建一个变量,用于获取数据。可暂时先用 apos。将位置信息作为颜色传入到片元着色器中

    • 顶点着色器中创建 varying变量

    • 将数据 apos 赋值给 varying 变量

    • 片元着色器中指定精度

    • 片元着色器中创建同名varying 变量

    • 使用 varying 变量

    注意:

    varying 变量的类型与 attribute 的类型相同。也是四分量的浮点数 -- vec4

    了解了以上步骤之后,来看下代码怎么写:

    栗子:

    const VERTEX_SHADER_SOURCE = '' +
          'attribute vec4 apos;' + // 用于获取数据的 apos 变量
          'varying vec4 vColor;' + // 用于传输数据的 varying 变量
          'void main () {' +
          ' gl_Position = apos;' +
          ' gl_PointSize = 10.0;' +
          ' vColor = apos;' +    // 将位置数据传输到片元着色器中
          '}'
    
    const FRAGMENT_SHADER_SOURCE = '' +
          'precision lowp float;' +  // 指定浮点数精度为低精度
          'varying vec4 vColor;' +  // 创建同名的 varying 变量
          'void main() {' +
          ' gl_FragColor = vColor;' + // 使用 varying 变量
          '}' +
          ''
    

    再次运行后可以看到一个彩色的三角形。这里我们就将位置信息当做颜色信息传入到了片元着色器中。记得一定要指定精度。否则会出现渲染错误。

    2.3 传入指定的颜色数据

    在第二步中,通过传入位置信息来显示对应位置的颜色。接下来来看看,如何通过 varying 变量传入指定的颜色信息。

    1. 首先修改着色器源代码。

    const VERTEX_SHADER_SOURCE = '' +
          'attribute vec4 apos;' +
          'attribute vec4 aColor;' + // 新增颜色信息变量
          'varying vec4 vColor;' +
          'void main () {' +
          ' gl_Position = apos;' +
          ' gl_PointSize = 10.0;' +
          ' vColor = aColor;' + // 将颜色信息传入到 varying 变量中
          '}'
    
    const FRAGMENT_SHADER_SOURCE = '' +
          'precision lowp float;' +
          'varying vec4 vColor;' +
          'void main() {' +
          ' gl_FragColor = vColor;' +
          '}' +
          ''
    

    2. 修改缓冲区数据。

    // 在每个位置后新增颜色信息。vec4 的数据。
    const data = new Float32Array([
      -1.0, -1.0, 1.0, 1.0, 0.0, 1.0,
      1.0, -1.0, 0.0, 1.0, 1.0, 1.0,
      0.0, 1.0, 1.0, 0.0, 1.0, 1.0,
    ])
    

    3. 修改数据传入

    绘制三角形的时候只有一个 apos 变量。现在新增了一个,所以需要修改数据的传入方式。修改后的代码如下

    const apos = gl.getAttribLocation(program, 'apos');
    gl.vertexAttribPointer(apos, 2, gl.FLOAT, false, data.BYTES_PER_ELEMENT * 6, 0)
    gl.enableVertexAttribArray(apos)
    
    const aColor = gl.getAttribLocation(program, 'aColor');
    gl.vertexAttribPointer(aColor, 4, gl.FLOAT, false, data.BYTES_PER_ELEMENT * 6, data.BYTES_PER_ELEMENT * 2);
    gl.enableVertexAttribArray(aColor)
    

    这里可以这样理解:

    • data 缓冲区中,每 6 个元素表示一个顶点信息。
    • apos 需要两个,从每个顶点的第1个数据开始查找。
    • aColor 需要四个,从每个顶点的第 3 个数据开始查找。

    同时对于代码中所涉及到的新知识点 gl.vertexAttribPointer、data.BYTES_PER_ELEMENT两个内容,也来详细介绍下。

    1.对于 gl.vertexAttribPointer 之前的文章里有过介绍,可能介绍的不太详细。再来详细介绍下:

    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可简单理解为一个点需要多少个数据。
    • offset:偏移几个顶点开始绘制

    这里也会注意到,最后的 stride 和 offset 两个参数都做了修改。而且是通过 data.BYTES_PER_ELEMENT 来实现的。

    2. data.BYTES_PER_ELEMENT 表示数组中每个元素所占的字节数。主要有以下几个分类:
    数组类型:           占的字节数
    Int8Array;         // 1
    Uint8Array;        // 1
    Uint8ClampedArray; // 1
    Int16Array;        // 2
    Uint16Array;       // 2
    Int32Array;        // 4
    Uint32Array;       // 4
    Float32Array;      // 4
    Float64Array;      // 8
    

    不出意外的话,经过这些操作,你会得到一个色彩鲜艳的彩色三角形。

    3. 总结

    接下来回顾一下文章开始提出的问题:

    3.1 webgl 中有几种类型的变量?

    有三种。

    • attribute:影响单个顶点
    • uniform:影响全部顶点
    • varying:由顶点向片元传递数据

    3.2 如何使用缓冲区对象向着色器传递数据?

    1. 创建 buffer : gl.createBuffer()
    2. 创建缓冲区数据:new Float32Array()
    3. 绑定缓冲区: gl.bindBuffer
    4. 传入缓冲区数据: gl.bufferData
    5. 获取变量:const 获取到的变量 = gl.getAttribLocation(program, '变量名称')
    6. 给变量赋值:gl.vertexAttribPointer(location, size, type, normalized, stride, offset)
    7. 激活变量:gl.enableVertexAttribArray(获取到的变量)

    4. 完整代码

    <!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>
    </head>
    <body>
      <div>
        <canvas id="canvas" width="300" height="300" style="border: 1px solid #666666;"></canvas>
      </div>
      <script>
        const canvas = document.getElementById('canvas')
        const gl = canvas.getContext('webgl')
    
        const VERTEX_SHADER_SOURCE = '' +
          'attribute vec4 apos;' +
          'attribute vec4 aColor;' +
          'varying vec4 vColor;' +
          'void main () {' +
          ' gl_Position = apos;' +
          ' gl_PointSize = 10.0;' +
          ' vColor = aColor;' +
          '}'
    
        const FRAGMENT_SHADER_SOURCE = '' +
          'precision lowp float;' +
          'varying vec4 vColor;' +
          'void main() {' +
          ' gl_FragColor = vColor;' +
          '}' +
          ''
    
        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)
    
        const buffer = gl.createBuffer()
        const data = new Float32Array([
          -1.0, -1.0, 1.0, 1.0, 0.0, 1.0,
          1.0, -1.0, 0.0, 1.0, 1.0, 1.0,
          0.0, 1.0, 1.0, 0.0, 1.0, 1.0,
        ])
    
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
        gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
    
        const apos = gl.getAttribLocation(program, 'apos');
        gl.vertexAttribPointer(apos, 2, gl.FLOAT, false, data.BYTES_PER_ELEMENT * 6, 0)
        gl.enableVertexAttribArray(apos)
    
        /*
          Int8Array;         // 1
          Uint8Array;        // 1
          Uint8ClampedArray; // 1
          Int16Array;        // 2
          Uint16Array;       // 2
          Int32Array;        // 4
          Uint32Array;       // 4
          Float32Array;      // 4
          Float64Array;      // 8
        */
        const aColor = gl.getAttribLocation(program, 'aColor');
        gl.vertexAttribPointer(aColor, 4, gl.FLOAT, false, data.BYTES_PER_ELEMENT * 6, data.BYTES_PER_ELEMENT * 2);
        gl.enableVertexAttribArray(aColor)
    
        gl.drawArrays(gl.TRIANGLES, 0, 3)
      </script>
    </body>
    </html>
    
  • 相关阅读:
    用Total Commander for Android管理应用程序
    我的zsh简单设置
    C# Newtonsoft.Json 使用
    Wireshark 抓包 test
    C# 调用API test
    C# 委托 的语法 之一
    C# 对象初始化器 和数组初始化语法
    C 语言 数据类型长度
    vue 使用 test
    test
  • 原文地址:https://www.cnblogs.com/yancyCathy/p/14465593.html
Copyright © 2011-2022 走看看