01.shader-最简单的webgl程序

887 阅读5分钟

全部章节

01.shader-最简单的webgl程序

02.buffer-在一个着色器程序中绘制多个点

03.drawArrays-绘制其他平面图形

04.drawElements-绘制一个正方体

05.texture-使用材质贴图

06.frameBuffer-将webgl绘制的正方体作为材质

点的实现

效果:

01_result.png

代码

<canvas id="cvs" style="width: 100px; height: 100px" height="100px" width="100px"></canvas>

<script>
  // 改变position的xyz查看点的位置变化,理解webgl的右手坐标系
  const VS = `
    void main() {
      gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
      gl_PointSize = 10.0;
    }
  `;
  const FS = `
    void main() {
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
  `;
</script>

<script>
  const cvs = document.getElementById('cvs')
  const gl = cvs.getContext('webgl')
  initShader(gl, VS, FS)
  gl.clearColor(0.0, 0.0, 0.0, 1.0)
  gl.clear(gl.COLOR_BUFFER_BIT)
  gl.drawArrays(gl.POINT, 0, 1)

  function initShader(gl, vs, fs) {
    const program = createProgram(gl, vs, fs)
    gl.useProgram(program)
    gl.program = program
    return true
  }

  function createProgram(gl, vs, fs) {
    const vShader = loadShader(gl, gl.VERTEX_SHADER, vs)
    const fShader = loadShader(gl, gl.FRAGMENT_SHADER, fs)
    const program = gl.createProgram();
    gl.attachShader(program, vShader)
    gl.attachShader(program, fShader)
    gl.linkProgram(program)
    return program
  }

  function loadShader(gl, type, source) {
    const shader = gl.createShader(type)
    gl.shaderSource(shader, source)
    gl.compileShader(shader)
    return shader
  }
  
</script>

解释

着色器代码

const VS = `
void main() {
  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
  gl_PointSize = 10.0;
}
`;
const FS = `
void main() {
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;

简单理解一下两个着色器代表的含义。

顶点着色器(VS)描述了在三维空间里点的信息,如点的位置,点的大小等。
片元着色器(FS)描述了某个位置上的颜色信息。

假设要表示正方形,首先是通过顶点着色器定义了四个角的位置,然后通过片元着色器定义面的颜色就可以绘制出一个正方形。当然这里说的很简单,有些情况也需要更多的点来实现一个正方形,这里只是为了大家大致的去了解一下两个着色器大致是做什么的。

顶点着色器

先看一下顶点着色器中的代码,直接看main函数内的语句,gl_Position和gl_PointSize是webgl中内置变量,下面片元着色器中的gl_FragColor也是,webgl中还有许多其他的内置变量,都是以gl开头的。

gl_Position

顾名思义,这个变量代表了点的位置信息,vec4是webgl中的一种数据类型,可以简单理解成一个拥有四个数的数组,也存在vec2和vec3代表了只存在两位和三位数的矢量。

vec4的前三个参数就代表了xyz,比如程序中gl_Position被赋值的前三位就对应了xyz,所以最终点呈现在了canvas的正中间,原点的位置。
如果我们把赋值改成vec4(0.1,-0.2,0.0,1.0),点的显示会在中心偏右下的位置,这是因为webgl中的坐标系是右手坐标系,x轴向右,y轴向上,z轴向前。
第四个参数会影响点在画布上的最终显示,会在后续会在矩阵中解释。

gl_PointSize

该变量的意义就是点的大小,是一个浮点类型

片元着色器

本例中的片元着色器代码就比较好理解了,gl_FragColor就代表了该点的颜色值,而vec4中的四个参数就代表了rgba四个值,需要注意的是,值的区间是0.0-1.0而不是0-255

js代码

本例中的着色器代码是最少的,主要是介绍一下gl_Position和gl_FragColor,这两个在着色器中最主要的东西。 接下来我们看一下js代码。

获取webgl上下文

const cvs = document.getElementById('cvs')
const gl = cvs.getContext('webgl')

这一步很容易理解,相信大家有时候也会获取canvas的2d上下文,getContext('2d') 当然getContext还有其他的参数,'webgl2','bitmaprenderer'

本系列中只需要webgl的上下文即可。

初始化着色器程序

initShader(gl, VS, FS)
function initShader(gl, vs, fs) {
  const program = createProgram(gl, vs, fs)
  gl.useProgram(program)
  gl.program = program
  return true
}

function createProgram(gl, vs, fs) {
  const vShader = loadShader(gl, gl.VERTEX_SHADER, vs)
  const fShader = loadShader(gl, gl.FRAGMENT_SHADER, fs)
  const program = gl.createProgram();
  gl.attachShader(program, vShader)
  gl.attachShader(program, fShader)
  gl.linkProgram(program)
  return program
}

function loadShader(gl, type, source) {
  const shader = gl.createShader(type)
  gl.shaderSource(shader, source)
  gl.compileShader(shader)
  return shader
}

代码只是去实现了过程,没有做兼容性处理(还是存在部分浏览器不支持webgl) 大致的过程为

  1. 创建并编译顶点着色器
  2. 创建并编译片元着色器
  3. 创建着色器程序
  4. 引用顶点着色器和片元着色器
  5. 使用着色器程序

过程还是简单易懂的,首先就是两个着色器代码在js中是字符串形式的,需要把它们编译成gpu可用的代码,然后用一个着色器程序program,关联顶点和片元着色器。

在一个webgl上下文中,不止可以使用一对顶点和片元着色器代码。

我们可以再写一对着色器代码,并在程序最后再用一次initShader和drawArrays,可以实现同时绘制一个红色的点和一个蓝色的点。虽然实现这样的功能有更好的写法,但是这样实现仅仅是为了理解可以使用多个着色器程序。

PS: gl.program = program这行代码其实是不好的,它是为了暂存一下刚定义的着色器程序,不同着色器程序之间的切换就是使用gl.useProgram(program),而参数就是这个program。但是gl对象中原本是没有program这个属性的,所以在ts中会报错,好的办法是自己用一个变量去储存。

绘制点

gl.clearColor(0.0, 0.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.POINT, 0, 1)

本例中只剩下这些代码还没有解释过,他们的功能都是在canvas上绘制。

clearColor和clear

可以理解为,用什么颜色去清空画布 如在clearColor中我们传入的参数是0.0, 0.0, 0.0, 1.0,那么就是用黑色去清空画布。 然后调用clear去用刚才定义的颜色去清空画布

drawArrays

drawArrays是去绘制图形的,本例中的意思是根据当前使用的着色器程序,去绘制一个点。具体的参数意思和可选参数会在后续章节中涉及

总结

本章节中,我们介绍了一个最简单的webgl程序的实现过程,介绍了顶点和片元着色器的作用,以及gl_Poisition和gl_FragColor。
然后介绍了如何用js去把着色器程序运行起来,并呈现在canvas上。

后续本系列会带来五个章节的内容,讲解webgl的大部分基础api,最终实现如下的效果。

06_result.png

相关代码gitee