WebGL编程指南笔记一 第一章第二章 WebGL入门 & shader

参考
【《WebGL编程指南》读书笔记】
【《WebGL编程指南》读书笔记-WebGL概述】
【《WebGL编程指南》读书笔记-WebGL入门】

关键词:

  • 顶点着色器
  • 片元着色器
  • 使用JS向着色器传递参数
一、使用Canvas画个实心蓝色矩形
//DrawRectangle.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Draw a blue rectangle (canvas version)</title>
  </head>

  <body onload="main()">
    <canvas id="example" width="400" height="400">
      Please use a browser that supports "canvas"
    </canvas>
    <script src="DrawRectangle.js"></script>
  </body>
</html>

// DrawTriangle.js (c) 2012 matsuda
function main() {  
  // Retrieve <canvas> element
  var canvas = document.getElementById('example');  
  if (!canvas) { 
    console.log('Failed to retrieve the <canvas> element');
    return false; 
  } 

  // Get the rendering context for 2DCG
  var ctx = canvas.getContext('2d');

  // Draw a blue rectangle
  ctx.fillStyle = 'rgba(0, 0, 255, 1.0)'; // Set color to blue
  ctx.fillRect(120, 10, 150, 150);        // Fill a rectangle with the color
}

二、清空绘图区

上一个示例还不能严格认为是WebGL的程序,而是<canvas>标签原本的2d方法和CanvasRenderingContext2D对象,下面进入WebGL的简单示例。

//HelloCanvas.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Clear "canvas"</title>
  </head>
 
  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
    Please use a browser that supports "canvas"
    </canvas>

    <script src="../lib/webgl-utils.js"></script>
    <script src="../lib/webgl-debug.js"></script>
    <script src="../lib/cuon-utils.js"></script>
    <script src="HelloCanvas.js"></script>
  </body>
</html>

// HelloCanvas.js (c) 2012 matsuda
function main() {
  // Retrieve <canvas> element
  var canvas = document.getElementById('webgl');

  // Get the rendering context for WebGL
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Set clear color
  gl.clearColor(0.0, 0.0, 0.0, 1.0);

  // Clear <canvas>
  gl.clear(gl.COLOR_BUFFER_BIT);
}
1.getWebGLContext(canvas)

抹平不同浏览器之间的差异,返回一个3D的绘图上下文。定义在cuon-utils.js中。也可以简单地使用这行代码:

let gl = canvas.getContext('webgl');
2.gl.clearColor

在前面的二维图形程序DrawRectangle中,颜色分量值在0到255之间。但是,由于WebGL是继承自OpenGL的,所以它遵循传统的OpenGL颜色分量的取值范围,即从0到1。RGB的值越高,颜色就越亮。类似地,第4分量值越高,颜色就越不透明。

一旦指定了背景色之后,背景色就会驻存在WebGL System中,在下一次调用gl.clearColor()方法前不会改变。换句话说,如果将来什么时候你还想用同一个颜色再清空一次绘图区,没必要再指定一次背景色。

3.gl.clear(gl.COLOR_BUFF_BIT)

此方法继承自OpenGL,参数表示清空颜色缓冲区。除了颜色缓冲区,还有深度缓冲区(gl.clearDepth)和模板缓冲区(gl.clearStencil)。
清空颜色缓冲区后,使用gl.clearColor指定的值,如果未指定,则使用默认值(0.0,0.0,0.0,0.0)

三、绘制一个矩形点

在前面的DrawRectangle中,绘制一个矩形如下:

  ctx.fillStyle = 'rgba(0, 0, 255, 1.0)'; 
  ctx.fillRect(120, 10, 150, 150);

你可能会认为WebGL也差不多,比如:

gl.drawColor(1.0,0.0,0.0,1.0);
gl.drawPoint(0,0,0,10);//点的位置和大小

不幸的是,事情没这么简单。WebGL依赖于一种新的称为着色器(shader)的绘图机制。

//HelloPoint1.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Draw a point (1)</title>
  </head>

  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
    Please use a browser that supports "canvas"
    </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 (c) 2012 matsuda
// Vertex shader program
var VSHADER_SOURCE = 
  'void main() {\n' +
// Set the vertex coordinates of the point
  '  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + 
// Set the point size
  '  gl_PointSize = 10.0;\n' +                    
  '}\n';

// Fragment shader program
var FSHADER_SOURCE =
  'void main() {\n' +
  '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // Set the point color
  '}\n';

function main() {
  // Retrieve <canvas> element
  var canvas = document.getElementById('webgl');

  // Get the rendering context for WebGL
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }

  // Specify the color for clearing <canvas>
  gl.clearColor(0.0, 0.0, 0.0, 1.0);

  // Clear <canvas>
  gl.clear(gl.COLOR_BUFFER_BIT);

  // Draw a point
  gl.drawArrays(gl.POINTS, 0, 1);
}

在代码中,出现了顶点着色器和片元着色器。它们是以字符串的形式嵌入在js文件中。

1.顶点着色器 Vertex shader

用来描述顶点特性(如位置、颜色等)

2.片元着色器 Fragment shader

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

本书后续部分将仔细研究着色器。简单地说,在三维场景中,仅仅用线条和颜色把图形画出来是远远不够的。你必须考虑,比如,光线照上去之后,或者观察者的视角发生变化,对场景会有什么影响。着色器可以高度灵活地完成这些工作,提供各种渲染效果。这也是当今计算机制作出的三维场景如此逼真和令人震撼的原因。

3.GLSL ES 着色器语言

因为着色器代码必须预先处理成单个字符串的形式,所以我们用+号将多行字符串连成一个长字符串。第一行以\n结束,这是由于当着色器内部出错时,就能获取出错的行号,这对于检查源代码中的错误很有帮助。但是,\n并不是必须的,也可不用它。

和C语言类似,必须包含一个main函数。gl_Position 和gl_PointSize 这两个变量是内置在顶点着色器中的,而且有着特殊的含义:一个表示位置,一个表示尺寸。

gl_Position 必须被赋值,gl_PointSize 并不是必须的,如果不赋值,会取默认值1.0.

GLSL ES语言是强类型的,如果你将gl_PointSize = 10.0改为gl_PointSize = 10就会报错。因为要求传入一个浮点数,而10是一个整形数。

gl_Position = vec4(0.0, 0.0, 0.0, 1.0)需要传入四个浮点数的分量,这被称为齐次坐标。

齐次坐标能够提高处理三维数据的效率,所以在三维图形系统中被大量使用。齐次坐标使用如下符号描述:(x,y,z,w),它等价于三维坐标(x/w,y/w,z/w)。所以如果齐次坐标的第4个分量是1,你就可以将它当作三维坐标来使用。w的值必须是大于等于0的。如果w趋近于0,那么它所表示的点将趋近无穷远,所以在齐次坐标系中可以有无穷的概念。齐次坐标的存在,使得用矩阵乘法来描述顶点变换成为可能,三维图形系统在计算过程中,通常使用齐次坐标来表示顶点的三维坐标。

4.initShaders()

对字符串形式的着色器进行初始化,它被定义在cuon.util.js中,将在本书第9章研究内部细节。

5.绘制操作gl.drawArrays(gl.POINTS,0,1);

第一个参数为mode,指定了绘制方式,可接收以下常量符号:

  • gl.POINTS
  • gl.LINES
  • gl.LINE_STRIP
  • gl.LINE_LOOP
  • gl.TRIANGLES
  • gl.TRIANGLE_STRIP
  • gl.TRIANGLE_FAN

第二个参数为first,指定从哪个顶点开始绘制(整形数)

第三个参数为count,指定绘制需要用到多少个顶点(整形数)

因为我们绘制的是单独的点,所以第一个参数设置成gl.POINTS;设置第二个参数为0,表示从第1个顶点开始画起(虽然只有1个顶点);设置第三个参数为1,表示仅绘制1个点

6.GLSL vs HLSL vs Cg

参考
GLSL vs HLSL vs Cg
GLSL 到 HLSL 参考
三大 Shader 编程语言(CG/HLSL/GLSL)

Shader language目前有3种主流语言:基于OpenGL的GLSL(OpenGL Shading Language,也称为GLslang),基于Direct3D的HLSL(High Level Shading Language),还有NVIDIA公司的Cg (C for Graphic)语言。

GLSL与HLSL分别提基于OpenGL和Direct3D的接口,两者不能混用,事实上OpenGL和Direct3D一直都是冤家对头,曹操和刘备还有一段和平共处的甜美时光,但OpenGL和Direct3D各自的东家则从来都是争斗不休。争斗良久,既然没有分出胜负,那么必然是两败俱伤的局面。

首先ATI系列显卡对OpenGL扩展支持不够,例如我在使用OSG(Open Scene Graphic)开源图形引擎时,由于该引擎完全基于OpenGL,导致其上编写的3D仿真程序在较老的显卡上常常出现纹理无法显示的问题。其次GLSL 的语法体系自成一家,而HLSL和Cg语言的语法基本相同,这就意味着,只要学习HLSL和Cg中的任何一种,就等同于学习了两种语言。不过OpenGL 毕竟图形API的曾经领袖,通常介绍OpenGL都会附加上一句“事实上的工业标准”,所以在其长期发展中积累下的用户群庞大,这些用户当然会选择 GLSL学习。此外,GLSL继承了OpenGL的良好移植性,一度在unix等操作系统上独领风骚(已是曾经的往事)。

微软的HLSL移植性较差,在windows平台上可谓一家独大,可一出自己的院子(还好院子够大),就是落地凤凰不如鸡。这一点在很大程度上限制了 HLSL的推广和发展。目前HLSL多半都是用于游戏领域。我可以负责任的断言,在Shader language领域,HLSL可以凭借微软的老本成为割据一方的诸侯,但,决不可能成为君临天下的霸主。这和微软现在的局面很像,就是一个被带刺鲜花簇拥着的大财主,富贵已极,寸步难行。

Unity官方手册上讲Shader程序嵌入的小片段是用Cg/HLSL编写的,从“CGPROGRAM”开始,到“CGEND”结束。所以,Unity官方主要是用Cg/HLSL编写Shader程序片段。Unity官方手册也说明对于Cg/HLSL程序进行扩展也可以使用GLSL,不过Unity官方建议使用原生的GLSL进行编写和测试。如果不使用原生GLSL,你就需要知道你的平台必须是Mac OS X、OpenGL ES 2.0以上的移动设备或者是Linux。在一般情况下Unity会把Cg/HLSL交叉编译成优化过的GLSL。因此我们有多种选择,我们既可以考虑使用Cg/HLSL,也可以使用GLSL。不过由于Cg/HLSL更好的跨平台性,更倾向于使用Cg/HLSL编写Shader程序。

7.与unity中的shader对比

参考UnityShader精要笔记一 基础示例

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/Chapter5-SimpleShader"
{
    SubShader
    {
        Pass{
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            float4 vert(float4 v:POSITION):SV_POSITION{
                // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
                return UnityObjectToClipPos(v);
            }

            fixed4 frag():SV_Target{
                return fixed4(1.0,1.0,1.0,1.0);
            }

            ENDCG
        }
    }
}

POSITION 和SV_POSITION都是CG/HLSL中的语义,是不可忽略的,POSITION告诉Unity把模型的顶点坐标填充到输入参数v中,SV_POSITION告诉unity顶点着色器的输出是裁剪空间中的顶点坐标。

UnityObjectToClipPos,是将顶点坐标从模型空间转换到剪裁空间中。

在本例中frag 函数没有任何输入,它的输出是一个fixed4 类型的变量,并使用了SV_Target语义进行限定,它等于告诉渲染器,把用户的输出颜色存储到一个渲染 目标(render target)中,这里将输出到默认的帧缓存中。片元着色器输出的颜色的每个分量范围在[0,1],其中(0,0,0)表示黑色,(1,1,1)表示白色。

现在来对比一下WEBGL的版本:

// Vertex shader program
var VSHADER_SOURCE = 
  'void main() {\n' +
  '  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // Set the vertex coordinates of the point
  '  gl_PointSize = 10.0;\n' +                    // Set the point size
  '}\n';

// Fragment shader program
var FSHADER_SOURCE =
  'void main() {\n' +
  '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // Set the point color
  '}\n';

和POSITION 和SV_POSITION类似,GLSL中也有内置变量gl_Position 和gl_PointSize。片元着色器写法类似。

四、将顶点坐标从JS传到着色器程序中

在上面的HelloPoint1例子中,点的位置写死在着色器程序中,缺乏扩展性。有两种方式可以把位置信息通过JS传给着色器:

  • attribute变量 传输的是那些与顶点相关的数据
  • uniform变量 传输那些对所有顶点都相同(或与顶点无关)的数据

本例使用attribute变量来传输顶点坐标,显然不同的顶点通常具有不同的坐标。

// HelloPint2.js (c) 2012 matsuda
// Vertex shader program
var VSHADER_SOURCE = 
  'attribute vec4 a_Position;\n' + // attribute variable
  'void main() {\n' +
  '  gl_Position = a_Position;\n' +
  '  gl_PointSize = 10.0;\n' +
  '}\n'; 

// Fragment shader program
var FSHADER_SOURCE = 
  'void main() {\n' +
  '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
  '}\n';

function main() {
  // Retrieve <canvas> element
  var canvas = document.getElementById('webgl');

  // Get the rendering context for WebGL
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }

  // Get the storage location of a_Position
  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if (a_Position < 0) {
    console.log('Failed to get the storage location of a_Position');
    return;
  }

  // Pass vertex position to attribute variable
  gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);

  // Specify the color for clearing <canvas>
  gl.clearColor(0.0, 0.0, 0.0, 1.0);

  // Clear <canvas>
  gl.clear(gl.COLOR_BUFFER_BIT);
    
  // Draw
  gl.drawArrays(gl.POINTS, 0, 1);
}
1.着色器代码
  • 声明一个attribute变量 叫a_Position(注:本书所有attribute变量都以a_前缀开始,同理uniform变量以u_前缀开始)
  • 把a_Position赋值给gl_Position
  • 这样JS就可以向a_Position传输数据了
2.JS代码
  • 拿到暴露的变量:var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); 返回的是个存储地址,为了便于理解,本书中,存储着色器变量地址的js变量名称与着色器中的变量名称保持一致。

  • 传输数据:gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);第2、3、4个参数是三个浮点型数值,即点的xyz坐标值。

3.gl.vertexAttrib3f的同族函数

你可能已经注意到,第4行的a_Position变量是vec4类型的,但是gl.vertex-Attrib3f()仅传了三个分量值。是不是漏掉了1个呢?实际上,如果你省略了第4个参数,这个方法就会默认地将第4个分量设置为1.0。

gl.vertexAttrib3f是一系列同族函数中的一个,gl.vertexAttrib1f传输1个单精度值,相应的23分量默认为0.0,4分量默认1.0。

gl.vertexAttrib4f则传输4个值。

你也可以使用这些方法的矢量版本,它们的名字以v(vector)结尾,并接受类型化数组作为参数,函数名中的数字表示数组中的元素个数,比如:

var position = new Float32Array([1.0, 2.0, 3.0, 1.0]);
gl.vertexAttrib4fv(a_Position, position);
4.改变点的大小

注意是float类型

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 a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize');
   gl.vertexAttrib1f(a_PointSize, 5.0);
五、通过鼠标点击绘点
//ClickedPoints.js
...
  // Register function (event handler) to be called on a mouse press
  canvas.onmousedown = function(ev){ click(ev, gl, canvas, a_Position); };

  // Specify the color for clearing <canvas>
  gl.clearColor(0.0, 0.0, 0.0, 1.0);

  // Clear <canvas>
  gl.clear(gl.COLOR_BUFFER_BIT);
}

var g_points = []; // The array for the position of a mouse press
function click(ev, gl, canvas, a_Position) {
  var x = ev.clientX; // x coordinate of a mouse pointer
  var y = ev.clientY; // y coordinate of a mouse pointer
  var rect = ev.target.getBoundingClientRect() ;

  x = ((x - rect.left) - canvas.width/2)/(canvas.width/2);
  y = (canvas.height/2 - (y - rect.top))/(canvas.height/2);
  // Store the coordinates to g_points array
  g_points.push(x); g_points.push(y);

  // Clear <canvas>
  gl.clear(gl.COLOR_BUFFER_BIT);

  var len = g_points.length;
  for(var i = 0; i < len; i += 2) {
    // Pass the position of a point to a_Position variable
    gl.vertexAttrib3f(a_Position, g_points[i], g_points[i+1], 0.0);

    // Draw
    gl.drawArrays(gl.POINTS, 0, 1);
  }
}

省略了部分代码,主要难点是坐标转换和使用数组记录点击位置。WebGL使用的是颜色缓冲区,绘制结束后系统将缓冲区内容显示在屏幕上,然后颜色缓冲区就会被重置,其中的内容会丢失( 这是默认操作,下一章将详细讨论)。因此,我们有必要将每次鼠标点击的位置都记录下来,每次点击后,程序都重新绘制了从第一次点击到最近一次点击中所有的点。

现在,让我们看看,如果不执行gl.clear(gl.COLOR_BUFFER_BIT);会怎么样:
首先会看到黑色的背景,第一次点击鼠标后,背景就变成了白色,然后绘制了一个红点。这是因为绘制点之后,颜色缓冲区被WebGL重置为默认的颜色(0.0,0.0,0.0,0.0)。这个默认颜色的alpha分量是0.0,因此canvas就成了透明的了,可以看到网页的背景颜色(这里还是白色)。如果你不希望这样,应当在每次绘制之前都调用gl.clear来用指定的背景色清空。

1.浏览器、canvas、webgl坐标系统相互转换

通过event可以获得鼠标点击的位置(浏览器页面坐标),该坐标需要转换到canvas坐标系统下,再转换到webgl坐标系统下


image.png
var x = ev.clientX;  // 鼠标点击处的x坐标
var y = ev.clientY;  // 鼠标点击处的y坐标
var rect = ev.target.getBoundingClientRect();

ev.target表示canvas元素,getBoundingClientRect用于获取某个元素相对于视窗的位置集合,返回包含top、left、bottom、right、width和height属性的对象。

image.png

所以,转换到canvas坐标系下就是:

x - rect.left
y - rect.top

然后,看一下WEBGL坐标系,位于屏幕中心点,并且Y轴方向与canvas坐标系相反


image.png

所以,转到WEBGL坐标系后就是:

x - rect.left - canvas.width/2
-(y - rect.top - canvas.height/2)

然后,WEBGL坐标系要归一化,区间为[-1,1]。以x值举例,最小为0,最大为canvas.width,转到webgl坐标系后,中心点变成canvas.width/2。所以,归一化只要除以canvas.width/2即可。

[x - rect.left - canvas.width/2] / (canvas.width/2)
[-(y - rect.top - canvas.height/2)] / (canvas.height/2)

这个结果再化简一下,就是代码常常看到的如下形式:

mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
六、改变点的颜色

可以用uniform变量将颜色值传给片元着色器,而不是顶点着色器。

// ColoredPoint.js (c) 2012 matsuda
// Vertex shader program
var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'void main() {\n' +
  '  gl_Position = a_Position;\n' +
  '  gl_PointSize = 10.0;\n' +
  '}\n';

// Fragment shader program
var FSHADER_SOURCE =
  'precision mediump float;\n' +
  'uniform vec4 u_FragColor;\n' +  // uniform变量
  'void main() {\n' +
  '  gl_FragColor = u_FragColor;\n' +
  '}\n';

function main() {
  // Retrieve <canvas> element
  var canvas = document.getElementById('webgl');

  // Get the rendering context for WebGL
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }

  // // Get the storage location of a_Position
  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if (a_Position < 0) {
    console.log('Failed to get the storage location of a_Position');
    return;
  }

  // Get the storage location of u_FragColor
  var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
  if (!u_FragColor) {
    console.log('Failed to get the storage location of u_FragColor');
    return;
  }

  // Register function (event handler) to be called on a mouse press
  canvas.onmousedown = function(ev){ click(ev, gl, canvas, a_Position, u_FragColor) };

  // Specify the color for clearing <canvas>
  gl.clearColor(0.0, 0.0, 0.0, 1.0);

  // Clear <canvas>
  gl.clear(gl.COLOR_BUFFER_BIT);
}

var g_points = [];  // The array for the position of a mouse press
var g_colors = [];  // The array to store the color of a point
function click(ev, gl, canvas, a_Position, u_FragColor) {
  var x = ev.clientX; // x coordinate of a mouse pointer
  var y = ev.clientY; // y coordinate of a mouse pointer
  var rect = ev.target.getBoundingClientRect();

  x = ((x - rect.left) - canvas.width/2)/(canvas.width/2);
  y = (canvas.height/2 - (y - rect.top))/(canvas.height/2);

  // Store the coordinates to g_points array
  g_points.push([x, y]);
  // Store the coordinates to g_points array
  if (x >= 0.0 && y >= 0.0) {      // 第一象限
    g_colors.push([1.0, 0.0, 0.0, 1.0]);  // Red
  } else if (x < 0.0 && y < 0.0) { // 第三象限
    g_colors.push([0.0, 1.0, 0.0, 1.0]);  // Green
  } else {                         // 其它
    g_colors.push([1.0, 1.0, 1.0, 1.0]);  // White
  }

  // Clear <canvas>
  gl.clear(gl.COLOR_BUFFER_BIT);

  var len = g_points.length;
  for(var i = 0; i < len; i++) {
    var xy = g_points[i];
    var rgba = g_colors[i];

    // Pass the position of a point to a_Position variable
    gl.vertexAttrib3f(a_Position, xy[0], xy[1], 0.0);
    // Pass the color of a point to u_FragColor variable
    gl.uniform4f(u_FragColor, rgba[0], rgba[1], rgba[2], rgba[3]);
    // Draw
    gl.drawArrays(gl.POINTS, 0, 1);
  }
}
1.精度限定词
'precision mediump float;\n'

用来指定变量的范围(最大值与最小值)和精度,本例中为中等精度。第5章将会详细讨论精度的问题。

2. gl.uniform4f

与gl.vertexAttrib3f类似,也有同族函数

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容