WebGL学习(一)-- 使用WebGL绘制一个点

2,083 阅读17分钟

什么是WebGL

能做什么

WebGL,是一项用来在网页上绘制和渲染复杂三维模型,并允许用户与之交互的技术。

传统意义上,只有高配置的计算机或者专用的游戏机才能渲染三维图形,WebGL技术结合了HTML5和JavaScript,允许开发者在网页上创建和渲染三维图形。

从传统意义上,为了显示三维图形,开发者需要使用C或者C++语言,辅以专业的计算机图形库,如OpenGL和Direct3D,来开发一个独立的应用程序。现在有了WebGL,我们只需要向已经熟悉的HTML和JavaScript中添加一些额外的三维图形学的代码,就可以在网页上显示三维图形了。

WebGL是内嵌在浏览器中的,不必安装插件和库就可以直接是使用它,而且因为它是基于浏览器的,可以在多个平台上运行WebGL程序的。

起源

在个人计算机上使用最广泛的两张三维图形渲染技术是Direct3D和OpenGL。

Direct3D 是微软 DirectX 技术的一部分,是一套由微软控制的编程接口API,主要用在window平台;OpenGL 由于其开放和免费的特性,在多种平台上都有广泛的使用。

OpenGL最初由SGI开发,并在1992年发布为开源标准。多年以来,OpenGL 发展了数个版本。虽然WebGL是根植于OpenGL,但实际上它是从 OpenGL 的一个特殊版本中派生出来的。OpenGL ES 经历了两个版本,WebGL是基于 OpenGL ES 2.0 的。它在添加新特性的同时,移除了许多陈旧无用的旧特性,这使它在保留轻量级的同时,仍具有足够的能力来渲染出精美的三维图形。

从 OpenGL 2.0 版本开始,OpenGL 支持了一项非常重要的特性,即可编程着色器方法。着色器方法又称着色器,使用一种类似于C的编程语言实现了精美的视觉效果。OpenGL ES 2.0 基于OpenGL 着色器语言,因此后者又被称为 OpenGL ES 着色器语言,缩写为 GLSL ES。WebGL基于 OpenGL ES 2.0,也使用 GLSL ES 编写着色器。

WebGL程序的结构

WebGL页面包含三种语言,HTML5 超文本语言, JavaScript语言 和 GLSL ES三种语言。然而因为通常 GLSL ES 是 以字符串的形式 在JavaScript中编写的,实际上 WebGL程序也只需要用到HTML文件和JavaScript文件。

WebGL入门

WebGL和Canvas

HTML5引入了canvas标签,允许JavaScript动态地绘制图形。canvas标签提供了一些简单的绘图函数,用来绘制点,线,矩形等。

canvas标签的简单使用

使用canvas画一个红色的矩形。

<html>
    <head></head>
    <body onload = "main()">
        <canvas id = "example" width = "400" height = "400"></canvas>
        <script src= "draw.js"></script>
    </body>
</html>


// draw.js
/*
绘制2D图形数据:
1. 获取<canvas>元素
2. 向该元素请求二维图形的 ' 绘图上下文'
3. 在绘图上下文调用相应的绘图函数,以绘制二维图形
*/
function main(){
    // 获取canvas元素
    var canvas = document.getElementById('example');
    if(!canvas) {
        console.log('fail to getCanvas');
        return
    }
    // 获取绘制二维图形的绘图上下文
    // 获取2d的绘图上下文 必须指定 '2d'类型
    var ctx = canvas.getContext('2d');
    // 设置填充颜色
    ctx.fillStyle = 'rgba(255,0,0,1.0)';
    // 使用填充颜色 填充矩形 
    // 参数 顺序 为  x,y,w,h
    ctx.fillRect(120,10,150,150);
}

canvas的坐标系统

canvas的坐标系统横轴为X轴,正方向朝右,纵轴为y轴,正方向朝下。

坐标轴的原点 落在左上方。

浏览器坐标和canvas坐标系统的转换:

var rect = canvas.getBoundingRect(); // 获取canvas区域的矩形位置和范围
// rect.left 为矩形相对浏览器左侧的偏移
// rect.top 为矩形相对浏览器顶部的偏移
// 假设浏览器的坐标是 x1,y1,canvas的坐标是x2,y2
var x1,y1,x2,y2;
x1 = rect.left + x2;
y1 = rect.top + y2

最短的WebGL程序

开始编写一个最短的WebGL程序,这个程序的功能 就是 使用背景色清空了canvas标签的绘图区。

<html>
    <head></head>
    <body onload = "main()">
        <canvas id = "example" width = "400" height = "400"></canvas>
        <script src="../lib/webgl-utils.js"></script>
    	<script src="../lib/webgl-debug.js"></script>
    	<script src="../lib/cuon-utils.js"></script>
        <script src= "helloWebGL.js"></script>
    </body>
</html>
// helloWebGL.js
// 除了引用了helloWebGL.js这个文件,还引入了封装好了的工具库函数,目前的话,只需要知道每个函数是用来做什么的,在后续的学习中,会不断深入去了解这些。
function main(){
    // 获取canvas元素
  var canvas = document.getElementById('example');
  // 获取WebGL上下文。 getWebGLContext 是工具库函数中的一个,解决了获取webGL上下文时 所存在的一些兼容性问题。正常情况下,我们应该使用 canvas.getContext()函数来获取。
  var gl = getWebGLContext(canvas);
    if(!gl) {
    console.log('fail')
    return 
  }
  // 设置清空的背景色,参考 canvas.fillColor()
  // 一旦设置好了背景色之后,背景色 就会常驻于WebGL系统中,在下一次调用gl.clearColor()方法前不会改变。意味着,如果将来什么时候你还想用同一个颜色再清空一次绘图区,不需要再指定一次背景色。
  gl.clearColor(0,0,0,1)
  // 使用设置好的 清空背景色 清空canvas区域
  gl.clear(gl.COLOR_BUFFER_BIT);
}

gl.clearColor() 函数的说明:

  1. 功能: 指定绘图区域的背景色

  2. 参数

    • red 指定红色值 0.0-1.0
    • green 指定绿色值 0.0-1.0
    • blue 指定蓝色值 0.0-1.0
    • opacity 指定透明度 0.0-1.0

    如果任何值小于0或者大于1.0 那么就会被截断于0.0 或者1.0

    为什么是0.0-1.0?

    因为WebGL是继承自OpenGL的,所以遵循OpenGL颜色分量的取值范围。

**gl.clear() **函数的说明:

  1. 功能:将指定缓冲区 设置为预定的值,如果清空的是颜色缓冲区,那么将使用gl.clearColor() 指定的参数的值

  2. 参数预设的值:

    • gl.COLOR_BUFFER_BIT 指定颜色缓存

    • gl.DEPTH_BUFFER_BIT 指定深度缓冲区

    • gl.STENCIL_BUFFER_BIT 指定模板缓冲区

  3. 返回值:无

  4. 错误: INVALID_VALUE 缓冲区不是上面三种类型

绘制一个点

之前的部分,我们学习了如何建立一个WebGL程序,如何使用一些简单的WebGL函数。这部分,我们将进一步学习,如何绘制一个点: 在原点(0.0,0.0,0.0)处绘制一个10个像素大的红色的点。

<html>
  <head>
    <title>HelloPoint1</title>
    
  </head>
  <body onload = 'main()'>
    <canvas id = 'example' width = "400" height="400"></canvas>
    <script src="../lib/webgl-utils.js"></script>
    <script src="../lib/webgl-debug.js"></script>
    <script src="../lib/cuon-utils.js"></script>
    <script src="HelloPoint1.js"></script>
  </body>
</html>

// HelloPoint1.js
//顶点着色器程序
var VSHADER_SOURCE =
    'void main() {\n' +
    'gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + //设置坐标
    'gl_PointSize = 10.0;\n' + //设置尺寸
    '}\n';
//片元着色器程序
var FSHADER_SOURCE=
    'void main(){\n'+
    'gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);\n'+ //设置颜色
    '}\n';
function main() {
    // 顶点着色器程序
  var canvas = document.getElementById('example');
  // 获取WebGL绘图上下文
  var gl = getWebGLContext(canvas);
      // 初始化着色器
  if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)) {
    console.log('fail To initialize')
  }
  // 设置背景色
  gl.clearColor(0.0,0.0,0.0,1.0);
  // 清空 canvas
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.drawArrays(gl.POINTS,0,1);
}

接下来,我们对这段代码进行详细的分析。

着色器

在绘制二维或者三维图形时,WebGL依赖一种新的称为 着色器 的绘图机制。所有的WebGL程序都必须使用它。着色器不仅强大,而且复杂,仅仅通过一条简单的绘图命令是不能操作它的。

WebGL需要两种着色器,顶点着色器和片元着色器。

着色器使用类似于C的 OpenGL ES(GLSL ES) 着色器语言来编写。因为着色器程序代码必须预先被处理成单个字符的形式,所以使用 + 号将多行字符串连成一个长字符串。

顶点着色器:

顶点着色器是用来描述顶点特定(如位置,颜色等)的程序。顶点是指二维或者三维图像的端点或者顶点。下面这是顶点着色器的代码,使用了 GLSL ES 语言来编写。

顶点着色器 指定了点的位置和尺寸。

片元着色器:

片元着色器是 进行逐 片元 处理过程如 光照 的程序。 片元 是一个WebGL术语,可以理解为像素。

片元着色器 指定了点的颜色。

初始化着色器

大部分WebGL程序 都遵循下面的流程来渲染二维或者三维图形:

  1. 获取 canvas标签的DOM元素
  2. 获取WebGL 绘图上下文
  3. 初始化着色器
  4. 设置canvas背景色
  5. 清除canvas
  6. 绘图

这里初始化着色器,我们使用辅助函数 initShaders 对字符串形式的着色器进行初始化。

      // 初始化着色器
  if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)) {
    console.log('fail To initialize')
  }

**initShaders函数说明:**initShaders(gl,vshader,fshader)

  1. 参数:

    • gl 指定渲染上下文
    • vshader 指定顶点着色器程序代码 字符串
    • fshader 指定片元着色器程序代码 字符串
  2. 返回值

    • true 初始化成功
    • false 初始化失败

目前最重要的是,WebGL程序包含运行在浏览器种的JavaScript和运行在WebGL系统的着色器程序这两个部分。

顶点着色器

顶点着色器的代码如下,顶点着色器 指定了点的位置和尺寸。

//顶点着色器程序
var VSHADER_SOURCE =
    'void main() {\n' +
    'gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + //设置坐标
    'gl_PointSize = 10.0;\n' + //设置尺寸
    '}\n';

和C语言程序一样,必须包含一个main函数。void表示这个函数不会有返回值。

首先将点的位置赋值给 gl_Position 这个变量,然后把尺寸的值赋值给 gl_PointSize

gl_Position 和 gl_PointSize 这两个变量是内置在顶点着色器内部的,而且有着特殊的含义:

gl_Position 表示顶点的位置,gl_PointSize 表示 点的尺寸。

其中gl_Position变量必须被赋值,否则着色器就无法正常工作。gl_PointSize 并不是必须的,默认值是1.0。

GLSL ES 中的数据类型:

  1. float 表示浮点数

  2. vec4 表示由四个浮点数 组成的矢量

着色器提供了内置函数将 X,Y,Z坐标组成的三个浮点数 ,转化成vec4变量 。

​ vec4函数vec(v0,v1,v2,v3) 返回 由v0,v1,v2,v3组成的vec4对象。

齐次坐标:

由4个分量组成的矢量 被称为 齐次坐标

(x,y,z,w) 齐次坐标等价于 三维坐标 (x/w,y/w,z/w)

w的值必须是大于0,如果趋近为0 ,所表示的点就将趋近于无穷远。

三维图形系统再计算过程中,通常使用齐次坐标来表示顶点的三维坐标。

片元着色器

片元着色器的代码如下。片元着色器的作用 就是处理片元,使其显示在屏幕上。

//片元着色器程序
var FSHADER_SOURCE=
    'void main(){\n'+
    'gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);\n'+ //设置颜色
    '}\n';

从main函数开始执行。

片元着色器将点的颜色赋值 给 gl_FragColor变量,该变量是片元着色器的内置变量,控制着像素在屏幕上的最终颜色。

gl_FragColor 颜色值也是vec4类型,包含4个浮点数分量,分别代表RGBA值。

绘制操作

绘制操作的代码如下。

gl.drawArrays(gl.POINTS,0,1);

在此之前,还做了clearColor和 clear 两个操作,之前已经解释过这两个操作了。

gl.drawArrays 是一个强大的函数,可以用来绘制各种图形。

**gl.drawArrays 函数说明:**gl.drawArrays(mode,first,count)

  1. 参数

    • mode 指定绘制的方式 可接收以下常量符号 gl.POINTS, gl.LINES, gl.LINE_STRIP,gl.LINE_LOOP,gl.TRIANGLES,gl.TRIANGLE,gl.TRIANGLE_FAN
    • first 指定从哪个顶点开始绘制 整型(从0开始)
    • count 指定绘制需要用到多少个顶点 整型
  2. 返回值 无

  3. 错误 INVALID_ENUM 传入的mode参数 不是前述参数之一

程序调用gl.drawArrays() 时,顶点着色器 将被执行count次,每次处理一个顶点。

示例代码中,着色器 只执行一次,调用并逐行执行内部的main函数,赋值位置和点的尺寸。

顶点着色器 指定完之后,片元着色器 就会开始执行,调用main函数,将颜色值给gl_FragColor。

最后一个红色的10个像素大的点就被绘制在了(0.0,0.0,0.0,1.0)处。

WebGL坐标系统

通常,在WebGL中,面向计算机屏幕时,X轴方向是水平的 正方向向右,Y轴方向是垂直的 正方向为下,而Z轴垂直屏幕 正方向为外。通常WebGL使用右手坐标系。

WebGL的坐标系 和canvas绘图区的坐标系不同,需要将前者映射到后者。

默认情况下:

  • canvas 的中心点 (0.0,0.0,0.0)

  • canvas 的上边缘和下边缘 (1.0,0.0,0.0) (-1.0,0.0,0.0)

  • canvas 的左边缘和右边缘 (0.0,1.0,0.0) (0.0,-1.0,0.0)

绘制一个点 版本2

之前的WebGL程序中,点的位置信息是写死在顶点着色器的程序中的,这一部分,我们来学习如果将位置信息从JavaScript程序中 传给顶点着色器。

我们先看完整的程序代码。

// 顶点着色器
var VSHADER_SOURCE = 
  ' attribute vec4 a_Position;\n' +
  ' attribute float a_PointSize;\n' +
  'void main() { \n ' +
  'gl_Position = a_Position;\n' + 
  'gl_PointSize = a_PointSize; \n' +
  '}\n';
var FSHADER_SOURCE=
    'void main(){\n'+
    'gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);\n'+ //设置颜色
    '}\n';
function main() {
  var canvas = document.getElementById('example');
  var gl = getWebGLContext(canvas);
  if (!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)) {
    
  }
  // 获取attribute 变量的的存储位置
  var a_Position = gl.getAttribLocation(gl.program,'a_Position');
  var a_PointSize = gl.getAttribLocation(gl.program,'a_PointSize');
  if(a_Position < 0) {
    console.log('fail to  get a_postion');
    return 
  }
  if(a_PointSize < 0) {
    console.log('fail to  get a_PointSize');
    return 
  }
  // 将顶点位置传输给attribute 变量
  gl.vertexAttrib3f(a_Position,0.0,0.0,0.0);
  gl.vertexAttrib1f(a_PointSize,50);  
  
  // 设置 canvas背景色
  gl.clearColor(0.0,0.0,0.0,1.0);

  // 清空canvas
  gl.clear(gl.COLOR_BUFFER_BIT);
  
  // 绘制一个点
  gl.drawArrays(gl.POINT,0,1);
}

从JavaScript程序中,将位置等信息传给顶点着色器,有两种方式。通过使用attribute变量或者 使用uniform变量。使用哪一个变量取决于传输数据的本身,。

attribute变量传输的是那些与顶点相关的数据,uniform变量 传输的是那些对于所有顶点都一样的数据。

attribute变量

我们先看顶点着色器的程序代码,如下。

var VSHADER_SOURCE = 
  ' attribute vec4 a_Position;\n' +
  ' attribute float a_PointSize;\n' +
  'void main() { \n ' +
  'gl_Position = a_Position;\n' + 
  'gl_PointSize = a_PointSize; \n' +
  '}\n';

attribute变量是一种 GLSL ES变量,被用来从外部向顶点着色器内部传递数据,只有顶点着色器能够使用它。

attribute变量声明

// 格式 <存储限定符> <类型> <变量名> 
attribute vec4 a_Position;

关键词 attribute 被称为 存储限定符 表示接下来的变量 是一个 attribute变量,attribute变量必须声明成一个全局变量,数据将从 着色器 外部传给该变量。

我们约定所有的attribute变量都以a_ 开头,方便辨认变量类型。

声明之后 我们将 a_Position 变量赋值给 gl_Position。

获取attribute变量存储地址

要想从JavaScript程序中向着色器中传递数据,需要向WebGL系统请求该变量的存储地址。我们使用 gl.getAttribLocation()来获取该变量的存储地址。

gl.getAttribLocation()函数说明

参数:

  1. 是一个程序对象,包含了顶点着色器和片元着色器。之后会详细讨论这个。但是必须在 initShaders之后才能访问到这个对象。
  2. 是要获取存储地址的attribute变量的名称。

返回值:

attribute变量的存储地址。 大于等0 -1表示 指定的attribute变量不存在,或者其命名具有gl_或者webgl_前缀。

错误:INVALID_OPERATION 程序对象未能成功链接,INVALID_VALUE name 参数的长度大于 attribute变量名 的最大长度 默认256字节。

这一步的代码如下:

// 获取attribute 变量的的存储位置
  var a_Position = gl.getAttribLocation(gl.program,'a_Position');
  var a_PointSize = gl.getAttribLocation(gl.program,'a_PointSize');
  if(a_Position < 0) {
    console.log('fail to  get a_postion');
    return 
  }
  if(a_PointSize < 0) {
    console.log('fail to  get a_PointSize');
    return 
  }

对attribute变量赋值

拿到变量的存储地址之后,需要使用该变量向着色器中传入值,我们使用gl.vertexAttrib3f() 函数来完成。

gl.vertexAttrib3f( v0,v1,v2) 函数说明

将数据v0,v1,v2 传给 由location参数 指定的attribute变量

参数 v0,v1,v2 是三个浮点型的坐标,即下x,y,z的坐标值。

返回值: 无

错误 INVALID_OPERATION 没有当前的program对象 ; INVALID_VALUE location大于等于attribute 变量的最大数目 默认为8.

函数被调用后,三个值被传给顶点着色器的a_Position 变量。

总结:

**着色器部分做的两件事: **

  1. 在全局中声明 attribute 变量 格式为 存储限定符 attribute + 变量类型 vec4 + 变量名 a_Position
  2. 将声明好的变量 赋值给 gl_Position

JavaScript程序中做的事情:

  1. 获取attribute变量的存储地址 gl.getAttribLocation(gl.program,a_Position)
  2. 得到存储地址之后,将值传给 顶点着色器中 attribute变量 gl.vertexAttrib3f(location,v0,v1,v2)

注意:

a_Position 变量是vec4 类型,gl.vertexAttrib3f只传了3个参数。实际上如果省略了第4个参数,这个方法会默认将第4个分量设置为1.0。

齐次坐标的第4个分量 1.0 使齐次坐标 与三维坐标对应起来,所以1.0 是一个安全的第4分量。

gl.vertexAttrib3f(location,v0,v1,v2)的同族函数

gl.vertexAttrib1f 传递1个精度 传递的参数 会被赋值给v0,v1,v2 会被设置为0.0, v3 会被设置为 1.0

gl.vertexAttrib2f 传递2个精度

gl.vertexAttrib3f 传递3个精度

gl.vertexAttrib4f 传递4个精度

WebGL 相关函数的命名规范 :

基础函数名 参数个数 参数类型

gl.vertexAttrib 基础函数名

3 参数个数

f 参数类型 f 表示浮点数 i表示整数

绘制一个点版本3

之前的WebGL程序中,点的颜色信息是写死在顶点着色器的程序中的,这一部分,我们来学习如果将颜色从JavaScript程序中 传给片元着色器。

我们先看下完整的程序代码。

var VSHADER_SOURCE = 
  'attribute vec4 a_Position;\n' +
  'void main() {\n' +
  'gl_Position = a_Position;\n' +
  'gl_PointSize = 10.0 ;\n' +
  '}\n';
//片元着色器程序
var FSHADER_SOURCE=
    'precision mediump float;\n'+
    'uniform vec4 u_FragColor;\n' +
    'void main(){\n'+
    'gl_FragColor = u_FragColor;\n'+ //设置颜色
    '}\n';
function main(){
  // 获取canvas元素
  var canvas = document.getElementById('example');
  // 获取WebGL上下文
  var gl = getWebGLContext(canvas);
  console.log(gl);
  // 初始化着色器
  if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)) {
    console.log('fail');
  }
  // 获取 a_Position 变量的存储位置
  var a_Position = gl.getAttribLocation(gl.program,'a_Position');
   // 获取 u_FragColor 变量的存储位置
   var u_FragColor = gl.getUniformLocation(gl.program,'u_FragColor');
  // 
  gl.clearColor(0.0,0.0,0.0,1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  canvas.onmousedown = function(ev) {
    click(ev,gl,canvas,a_Position);
  }
  var g_points = []; // 鼠标点击的位置数组
  var g_colors =[]; // 存储颜色数组的值
  function click(ev,gl,canvas,a_Position) {
    var x = ev.clientX;
    var y = ev.clientY;
    var rect = ev.target.getBoundingClientRect();
    x = ((x-rect.left) - canvas.height/2)/(canvas.height/2);
    y = (canvas.width/2 - (y-rect.top))/(canvas.width/2);
    
    g_points.push([x,y]);
    if(x> 0.0 && y>= 0.0) {
      g_colors.push([1.0,0.0,0.0,1.0])
    }
    if(x< 0.0 && y< 0.0) {
      g_colors.push([1.0,1.0,0.0,1.0])
    }else {
      g_colors.push([1.0,1.0,1.0,1.0]) // 白色
    }
    
    
    gl.clear(gl.COLOR_BUFFER_BIT);

    var len = g_points.length;

    for(var i = 0; i< len;i++) {
      // 传递attribute变量 到 变量中 
      var rgba = g_colors[i];
      var xy = g_points[i];
      gl.vertexAttrib3f(a_Position,xy[0],xy[1],0);
      // 传递点的颜色 传输到 u_FragColor
      gl.uniform4f(u_FragColor,rgba[0],rgba[1],rgba[2],rgba[3])
      // 绘制点
      gl.drawArrays(gl.POINTS,0,1);
    }
  }
}

本例要实现的效果是根据鼠标点击的位置绘制出点,并且根据点的位置设置不同的颜色。

这里涉及到两个问题,一个是获取鼠标点击的位置在webGL中的坐标,一个是如何动态修改点的颜色。

坐标转换

鼠标点击的位置保存在事件对象ev中,该对象传给了click函数。可以通过访问ev.clientX 和ev.clientY来获取位置坐标。

由于两个原因不能直接使用这两个坐标值:

  1. 鼠标点击位置的坐标 是在 浏览器客户区 的坐标,而不是在 canvas的坐标。

  2. canvas的坐标系统与WebGL的坐标系统,其原点方向位置和Y轴的正方向不同。 canvas坐标系 中,Y轴的正常向下,WebGL中 Y轴的正方向向上。

浏览器坐标 => Canvas坐标

通过事件对象 获取 浏览器的坐标

 var x = ev.clientX;

 var y = ev.clientY;

 var rect = ev.target.getBoundingClientRect();
// rect.left 表示的是 canvas区域相对浏览器左侧的偏移
// rect.top 表示的是 canvas区域相对于浏览器顶部的偏移
 // 假设 canvas坐标是 x1,y1, 客户区坐标是 x,y
 x1+ rect.left = x
 y1+ rect.top = y
 // x-rect.x, y-rect.y 就可以将 客户区坐标转换成canvas坐标x1,y1。

Canvas坐标 => WebGL坐标

WebGL坐标的两个特点:

  1. WebGL坐标的原点是在Canvas坐标的(canvas.width/2, canvas.height/2)

  2. WebGL坐标区间 是从-1.0到1.0

// 根据坐标和区间长度对应关系
canvas.width / 2 = x1/x2
=> x2 = x1 / (canvas.width / 2)
=> x2 = (x-rect.x)/ (canvas.width / 2)
// Y轴转换类似。

uniform变量

只有顶点着色器才能使用attribute变量,使用片元着色器时,就需要使用uniform变量。

我们先看下片元着色器代码。

//片元着色器程序
var FSHADER_SOURCE=
    // 精度限定词  precision mdiump float; 这里使用的是 中等精度。后续会详细介绍。
    'precision mediump float;\n'+
    'uniform vec4 u_FragColor;\n' +
    'void main(){\n'+
    'gl_FragColor = u_FragColor;\n'+ //设置颜色
    '}\n';

声明

  //存储限定符 类型 变量名
  uniform  vec4 u_FragColor;

获取uniform变量的存储地址

我们这里使用gl.getUniformLocation(program,name) 来获取 变量的存储地址。

var u_FragColor = gl.getUniformLocation(gl.program,'u_FragColor');

gl.getUniformLocation(program,name)函数说明:

参数:

  1. program 指定包含顶点着色器 和 片元着色器程序对象
  2. name 指定想要获取 其储存位置变量的位置

返回值:

  1. no-null
  2. null 指定的uniform变量不存在,或者其命名具有gl_或者webgl_ 前缀。(注意: 和gl.getAttribLocation()方法不同,找不到 是返回-1)。

错误:INVALID_OPERATION 程序未能链接; INVALID_VALUE name参数的长度 大于uniform变量名的最大长度 默认256字节。

向uniform变量赋值

我们这里使用gl.uniform4f() 函数来完成,函数功能,参数 类似于 gl.vertexAttrib3f()。

 gl.uniform4f(u_FragColor,rgba[0],rgba[1],rgba[2],rgba[3])

gl.uniform4f()的同族函数:

  1. gl.unifrom1f()

  2. gl.unifrom2f()

  3. gl.unifrom3f()

  4. gl.unifrom4f()