优雅的学习webgl(2)—webgl中的着色器和缓冲区


    在以前一章中咱们了解了webgl中的一些常识,也尝试绘制了第一个webgl程序,接下来这一张会介绍一些webgl中的基本概念,包含顶点着色器、片元着色器、缓冲区git

  • 什么是顶点着色器和片元着色器
  • 缓冲区

这个系列的源码地址为:源码的地址为: https://github.com/forthealll...github

1、什么是顶点着色器和片元着色器

    咱们在第一篇文章中初略的介绍了着色器,在这里咱们在详细讲讲什么是着色器。web

1.顶点着色器

    顶点着色器保存了所想绘制的图形的顶点的信息,包括了顶点的坐标,以及顶点的大小。咱们结合GLSL语言,来详细的讲讲顶点着色器。数组

attribute vec4 a_position;
 
void main() {
   gl_Position = a_position;
   gl_PointSize = 10.0;
}

    顶点着色器基本上具备两个系统的变量gl_Position和gl_PointSize,分别表示顶点的位置和顶点的大小。而且图形中的每个顶点都会调用一次顶点着色器。顶点着色器可使用attribute来修饰的全局变量。浏览器

上述例子中的:缓存

attribute vec4 a_position;

    a_position变量就是一个attribute修饰符修饰的全局变量,其变量的值能够在其余地方被赋值。咱们能够在咱们的js代码中:性能

var positionLoc = gl.getAttribLocation(someShaderProgram, "a_position");

    经过gl实例来获取某个程序对象上的attribute修饰符修饰的变量a_position的地址,也能够给这个a_position变量赋新的值。webgl

gl.vertexAttribPointer(positionLoc,newValue)

这里的全局变量听起来很拗口,其实全局的意思就是:spa

不只仅只能在着色器中被使用,能够在着色器外被捕获或者谁用的变量,在GLSE语言中都是全局变量。插件

在顶点着色器中使用的所有变量除了用attribute修饰外,还能够经过uniform来修饰。好比咱们须要在移动某个图形(给某个图形的每一个顶点一个偏移)

//顶点着色器
attribute vec4 a_position;
uniform vec4 u_offset;
 
void main() {
   gl_Position = a_position + u_offset;
}
//顶点着色器之外的逻辑代码
var offsetLoc = gl.getUniformLocation(someProgram, "u_offset");
gl.uniform4fv(offsetLoc, [1, 0, 0, 0]);  // 向右偏移一半屏幕宽度

    咱们经过uniform这个全局变量,给顶点着色器赋值了一个偏移量。

在顶点着色器中attribute修饰的变量和uniform修饰的变量均可以做为全局变量在着色器外被取值和赋值。那么二者以前有什么区别?

attribute修饰的全局变量能够读取缓冲区中的值

至于什么是缓冲区,咱们下节中会具体降到,简单来讲缓冲区的存在使得能够在一次绘制中绘制出图形全部的顶点,而不须要手动的逐顶点绘制.

2.片元着色器

    在前面一章中咱们讲到片元着色器决定了图形的颜色,那么片元着色器是如何动做的呢。简单来讲:

从顶点着色器中了如何装配图形,当顶点着色器画出了图形的轮廓后,片元着色器将图形分解成一个个小的片断,而且肯定每个片断的颜色。最后渲染出结果。

    其实从顶点着色器到片元着色器,而后从片元着色器到渲染到浏览器的过程比较复杂,具体能够分为以下几步:

  • 初始化顶点着色器
  • 根据顶点着色器中的顶点值,开始图形装配
  • 图形装配完成后,开始光栅化
  • 光栅化后每个片元均可以用内置变量gl.FragCoord来表示
  • 从片元(Frag)相对应的gl.FragCoord中读取信息并对每一个片元生成独立的颜色信息gl.FragColor,并保存到颜色缓冲区
  • 当全部的片元都有有颜色信息后,将颜色缓冲区的图形渲染到浏览器中

    用文字表述可能不够直观,咱们来用图形表示:

Lark20191206-202215

    咱们以渲染一个正方形为例,在顶点着色器中咱们只给出了4个顶点的坐标,光栅化后生成了9个小片元,光栅化后的小片元进入片元着色器中被处理,最后渲染到浏览器中。

    咱们在上一节中发如今片元着色器中咱们仅仅传入了4个顶点的颜色,可是经过片元着色器,最后渲染出来的图形是一个彩色的正方形。

                Lark20191206-203133

    咱们从顶点着色器中能够传递4个顶点的颜色信息给片元着色器,片元着色器接受这4个顶点的颜色信息,经过内插能够拿到每一个片元的信息,最后渲染到浏览器中。

    咱们来了解一下内插的过程,首先咱们能够经过varying修饰的变量将值从顶点着色器传递到片元着色器。

//顶点着色器

attribute vec4 a_color
varying vec4 v_color
void main() {
   v_color = a_color
}

//片元着色器

varying vec4 v_color
void main(){
   gl.FragColor = v_color
}

    咱们经过声明两个同名的用varying修饰的变量v_color,就能够实现将值从顶点着色器传递到片元着色器。

    为何咱们须要将值从顶点传递到片元着色器呢?

由于片元着色器的颜色信息等可能与顶点的坐标有关,此外顶点着色器是惟一能够从缓存中读取值的地方.

咱们接着来看插值的过程示意图:

Lark20191209-172415

内插发生在光栅化的时候,能够根据顶点的颜色值,内插获得全部片元的颜色值。简单距离好比两个顶点的颜色分别是(0,0,0)和(0,0,1),两个顶点间有10个片元,那么内插获得的几个片元的颜色分别为(0,0,0.1),(0,0,0.2)...(0,0,0.9)

    总之:经过顶点着色器和片元着色器咱们就能绘出各类图案,顶点着色器和片元着色器是webgl绘图的基础.

2、缓冲区

    缓冲区的做用很简单,就是缓冲图形的信息,在一次绘制中将图形绘制出来。咱们接下来一步步来看缓冲区,咱们前面画了一个点:

const vsSource = `
    attribute vec4 a_Position;
    void main() {
      gl_Position = a_Position;
      gl_PointSize = 10.0;
    }
  `;

  // Fragment shader program

  const fsSource = `
    void main() {
      gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
  `;
  const shaderProgram = initShaderProgram(gl, vsSource, fsSource)
  gl.clearColor(0.0, 0.0, 0.0, 1.0);  // Clear to black, fully opaque
  gl.clearDepth(1.0);                 // Clear everything
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
  gl.useProgram(shaderProgram);
  //开始绘制
  a_Position = gl.getAttribLocation(shaderProgram,'a_Position')
  gl.vertexAttrib3f(a_Position,0.0,0.0,0.0);
  gl.drawArrays(gl.POINTS,0,1);

上述代码的运行结果为:

Lark20191209-175444

若是咱们要绘制3个点,那么须要修改上述的代码:

//开始绘制
  a_Position = gl.getAttribLocation(shaderProgram,'a_Position')
  gl.vertexAttrib3f(a_Position,0.0,0.0,0.0);
  gl.drawArrays(gl.POINTS,0,1);

  a_Position = gl.getAttribLocation(shaderProgram,'a_Position')
  gl.vertexAttrib3f(a_Position,0.0,0.1,0.0);
  gl.drawArrays(gl.POINTS,0,1);

  a_Position = gl.getAttribLocation(shaderProgram,'a_Position')
  gl.vertexAttrib3f(a_Position,0.0,-0.1,0.0);
  gl.drawArrays(gl.POINTS,0,1);

咱们须要连续绘制3次才能出现3个点,绘制的结果以下图所示:

Lark20191209-182151

    对于复杂的图形,咱们不可能一个点一个点的去绘制,若是咱们须要一次性的绘制多个点,那么就须要使用缓冲区,咱们能够读取缓冲区内的顶点信息,而后一次性的绘制出来。

    缓冲区是webgl系统中的一块存储区,能够在缓冲区对象保存绘制图形所须要的顶点信息。

使用缓冲区须要一下的五个步骤:

  1. 建立缓冲区(gl.createBuffer())
  2. 绑定缓冲区(gl.bindBuffer())
  3. 将数据写入缓冲区对象(gl.bufferData())
  4. 将缓冲区对象分配给一个attribute变量(gl.vertexAttribPointer())
  5. 开启attribute变量(gl.enableVertexAttribArray())

    遵循上述的5个步骤,咱们来使用缓冲区一次性绘制三个点,修改上面的代码:

const shaderProgram = initShaderProgram(gl, vsSource, fsSource)
  gl.clearColor(0.0, 0.0, 0.0, 1.0);  // Clear to black, fully opaque
  gl.clearDepth(1.0);                 // Clear everything
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
  gl.useProgram(shaderProgram);
  //开始绘制
  let vertices = new Float32Array([0.0,0.0,0.0,0.1,0.0,-0.1])  ---(1)
  //建立缓冲区
  let vertexBuffer = gl.createBuffer()                 ----(2)
  //将缓冲区对象绑定到目标
  gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer)          ----(3)
  //向缓冲区对象写入数据
  gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW)      ---(4)
  let a_Position = gl.getAttribLocation(shaderProgram,'a_Position')
  //将缓冲区对象分配给a_Position
  gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0)     ---(5)
  //开启attribute变
  gl.enableVertexAttribArray(a_Position);
  gl.drawArrays(gl.POINTS,0,3)

上述的代码一样的是绘制3个点,可是是在一次绘制中就完成了。具体的代码能够查看:https://github.com/forthealll...

来看几个须要注意的地方,首先在(1)中的Float32Array是一个类型化数组,类型化数组是JavaScript操做二进制数据的一个接口,并不须要引入其余任何插件。
webgl中须要浏览器和显卡进行通讯,为了知足JavaScript与显卡之间大量的、实时的数据交换,它们之间的数据通讯必须是二进制的,而不能是传统的文本格式。像C语言那样,直接操做字节,而后将4个字节的32位整数,以二进制形式原封不动地送入显卡,脚本的性能就会大幅提高。

let vertices = new Float32Array([0.0,0.0,0.0,0.1,0.0,-0.1])

此外就是将缓冲区对象分配给a_Position的(5)方法gl.vertexAttribPointer,该方法的接受6个参数,具体参数的意思这里不会去讲,值得注意的是这个方法将缓冲区的值传递给了顶点着色器中的attribute变量,并声明了如何在缓冲区中依次如何取值的方法。

gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0)

    这里这6个参数的意思是,缓冲区的attribute变量的引用a_Position,每次从缓冲区取2个点,缓冲区值的类型是FLOAT类型,false表示不会归一化,0表示从缓冲区数组的第0数组开始取,最后一个0表示间隔为0.

    上述就是缓冲区的作用,上述的缓冲区只保存了顶点信息,实际上缓冲区能够保存顶点的坐标信息,颜色信息,光照信息,矩阵变换信息等等,同时缓冲区也能够有多个,顶点着色器可使用attribute变量来读取缓冲区的值,同时片元着色器能够经过varying变量来接受从顶点着色器传递过来的值,从而在依次绘制中就能够获得完整的结果。

    最后总结一下缓冲区和顶点着色器、片元着色器之间的通讯:

Lark20191209-203239

相关文章
相关标签/搜索