WebGL 基础概念

简介: WebGL 基础概念

参考文章:https://webglfundamentals.org/webgl/lessons/zh_cn/webgl-fundamentals.html#toc

根据学习的路线,个人弄的 webgl 的 demo,有需要的可以下载查看:【webgl-demo】



着色器


WebGL 仅仅是一个光栅化引擎,它可以根据你的代码绘制出点,线和三角形。


   WebGL 在电脑的 GPU 中运行。因此你需要使用能够在 GPU 上运行的代码。 这样的代码需要提供成对的方法。并且使用一种和C或C++类似的强类型的语言 GLSL。 (GL着色语言)。每一对组合起来称作一个 program(着色程序)。


两种着色器

每对方法中一个叫顶点着色器, 另一个叫片断着色器


1.顶点着色器:计算顶点的位置。

无论你的画布有多大,裁剪空间的坐标范围永远是 -1 到 1

// 一个属性值,将会从缓冲中获取数据
attribute vec4 a_position;
// 所有着色器都有一个main方法
void main() {
  // gl_Position 是一个顶点着色器主要设置的变量
  gl_Position = a_position;
}

如果用 JavaScript 代替 GLSL, 可以类似

// *** 伪代码!! ***
var positionBuffer = [
  0, 0, 0, 0,
  0, 0.5, 0, 0,
  0.7, 0, 0, 0,
];
var attributes = {};
var gl_Position;
drawArrays(..., offset, count) {
  var stride = 4;
  var size = 4;
  for (var i = 0; i < count; ++i) {
     // 从positionBuffer复制接下来4个值给a_position属性
     const start = offset + i * stride;
     attributes.a_position = positionBuffer.slice(start, start + size);
     runVertexShader();// 运行顶点着色器
     ...
     doSomethingWith_gl_Position();
}



2.片断着色器:计算出当前绘制图元中每个像素的颜色值。

WebGL中的颜色值范围从 0 到 1 。


比如:vec4(1, 0, 0.5, 1):r, g, b, alpha (红,绿,蓝,阿尔法)

vec4是一个有四个浮点数据的数据类型。

  • 1 代表红色值
  • 0 代表绿色值
  • 0.5 代表蓝色值
  • 最后一个1表示阿尔法通道值
// 片断着色器没有默认精度,所以我们需要设置一个精度
// mediump是一个不错的默认值,代表“medium precision”(中等精度)
precision mediump float;
void main() {
  // gl_FragColor是一个片断着色器主要设置的变量
  gl_FragColor = vec4(1, 0, 0.5, 1); // 返回“红紫色”
}



四种方法


几乎整个 WebGL API 都是关于如何设置这些成对方法的状态值以及运行它们。


   对于想要绘制的每一个对象,都需要先设置一系列状态值,然后通过调用 gl.drawArrays 或 gl.drawElements 运行一个着色方法对,使得你的着色器对能够在GPU上运行。这些方法对所需的任何数据都需要发送到GPU。


发送方式:

1.属性(Attributes)和缓冲


   缓冲:发送到 GPU 的一些二进制数据序列

       通常情况下缓冲数据包括位置,法向量,纹理坐标,顶点颜色值等

       可以存储任何数据


   属性:用来指明怎么从缓冲中获取所需数据并将它提供给顶点着色器

       寻找属性值位置(和全局属性位置)应该在初始化的时候完成,而不是在渲染循环中。

       例如你可能在缓冲中用三个32位的浮点型数据存储一个位置值

       对于一个确切的属性你需要告诉它从哪个缓冲中获取数据,获取什么类型的数据(三个32位的浮点数据), 起始偏移值是多少,到下一个位置的字节数是多少


   缓冲不是随意读取的

       事实上顶点着色器运行的次数是一个指定的确切数字

       每一次运行属性会从指定的缓冲中按照指定规则依次获取下一个值


2.全局变量(Uniforms)

   全局变量在着色程序运行前赋值,在运行过程中全局有效。


3.纹理(Textures)

   纹理:一个数据序列,可以在着色程序运行中随意读取其中的数据。

       大多数情况存放的是图像数据

       纹理仅仅是数据序列, 也可以随意存放除了颜色数据以外的其它数据。


4.可变量(Varyings)

   可变量:一种顶点着色器给片断着色器传值的方式,依照渲染的图元是点, 线还是三角形,顶点着色器中设置的可变量会在片断着色器运行中获取不同的插值。




WebGL 的简单用法


WebGL的主要任务就是设置好状态并为 GLSL 着色程序提供数据。


使用 WebGL 只需要给它提供两个东西:

  • 裁剪空间中的坐标值:由顶点着色器提供
  • 颜色值:由片断着色器提供



注意

【webgl-utils.js】:它包含了编译和链接着色器的方法。下面的例子里将会使用。


初始化

1.在 HTML 中的 canvas(画布)对象,并获取它

<canvas id="c"></canvas>


var canvas = document.querySelector("#c");


2.创建一个 WebGL 渲染上下文(WebGLRenderingContext)

var gl = canvas.getContext("webgl");
if (!gl) {
   // 你不能使用WebGL!
   ...



3.利用 JavaScript 中创建字符串的方式创建 GLSL 字符串


   方式一:用串联的方式(concatenating), 用AJAX下载,用多行模板数据。大多数三维引擎在运行时利用模板,串联等方式创建 GLSL。


   方式二:将它们放在非JavaScript类型的标签中,下面代码展示的就是这种

<script id="vertex-shader-2d" type="notjs">
  // 一个属性变量,将会从缓冲中获取数据
  attribute vec4 a_position;
  // 所有着色器都有一个main方法
  void main() {
    // gl_Position 是一个顶点着色器主要设置的变量
    gl_Position = a_position;
  }
</script>
<script id="fragment-shader-2d" type="notjs">
  // 片断着色器没有默认精度,所以我们需要设置一个精度
  // mediump是一个不错的默认值,代表“medium precision”(中等精度)
  precision mediump float;
  void main() {
    // gl_FragColor是一个片断着色器主要设置的变量
    gl_FragColor = vec4(1, 0, 0.5, 1); // 返回“瑞迪施紫色”
  }
</script>


4.创建两个着色器

// 创建着色器方法,输入参数:渲染上下文,着色器类型,数据源
function createShader(gl, type, source) {
  var shader = gl.createShader(type); // 创建着色器对象
  gl.shaderSource(shader, source); // 提供数据源
  gl.compileShader(shader); // 编译 -> 生成着色器
  var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  if (success) {
    return shader;
  }
  console.log(gl.getShaderInfoLog(shader));
  gl.deleteShader(shader);
}
var vertexShaderSource = document.querySelector("#vertex-shader-2d").text;
var fragmentShaderSource = document.querySelector("#fragment-shader-2d").text;
var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);


5.将这两个着色器 link(链接)到一个 program(着色程序)

function createProgram(gl, vertexShader, fragmentShader) {
  var program = gl.createProgram();
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  gl.linkProgram(program);
  var success = gl.getProgramParameter(program, gl.LINK_STATUS);
  if (success) {
    return program;
  }
  console.log(gl.getProgramInfoLog(program));
  gl.deleteProgram(program);
}


调用:

var program = createProgram(gl, vertexShader, fragmentShader);


6.从刚才创建的 GLSL 着色程序中找到属性值 a_position 所在的位置。

var positionAttributeLocation = gl.getAttribLocation(program, "a_position");


7.创建一个缓冲:属性值从缓冲中获取数据

var positionBuffer = gl.createBuffer();


8.绑定位置信息缓冲

WebGL可以通过绑定点操控全局范围内的许多数据,你可以把绑定点想象成一个WebGL内部的全局变量。


首先绑定一个数据源到绑定点

// 绑定点就是ARRAY_BUFFER
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);


然后通过绑定点向缓冲中存放数据

// 三个二维点坐标
var positions = [
  0, 0,
  0, 0.5,
  0.7, 0,
];
// 最后一个参数gl.STATIC_DRAW是提示WebGL我们将怎么使用这些数据。WebGL会根据提示做出一些优化。
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);


上面的代码完成了几件事:

   有了一个 JavaScript 序列 positions

   new Float32Array(positions) 创建了 32 位浮点型数据序列, 并从 positions 中复制数据到序列中

   gl.bufferData 复制这些数据到 GPU 的 positionBuffer 对象上


渲染


1.调整画布(canvas)的尺寸以匹配它的显示尺寸

webglUtils.resizeCanvasToDisplaySize(gl.canvas);


2.把提供的 gl_Position 裁剪空间坐标对应到画布像素坐标

// WebGL裁剪空间的 -1 -> +1 分别对应到x轴的 0 -> gl.canvas.width 和y轴的 0 -> gl.canvas.height
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);


3.清空画布

// 清空画布
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);


4.运行着色程序

// 告诉它用我们之前写好的着色程序(一个着色器对)
gl.useProgram(program);


5.从缓冲中获取数据给着色器中的属性

// 启用对应属性
gl.enableVertexAttribArray(positionAttributeLocation);
// 将绑定点绑定到缓冲数据(positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// 告诉属性怎么从positionBuffer中读取数据 (ARRAY_BUFFER)
var size = 2;          // 每次迭代运行提取两个单位数据
var type = gl.FLOAT;   // 每个单位的数据类型是32位浮点型
var normalize = false; // 不需要归一化数据
var stride = 0;        // 0 = 移动单位数量 * 每个单位占用内存(sizeof(type))
                       // 每次迭代运行运动多少内存到下一个数据开始点
var offset = 0;        // 从缓冲起始位置开始读取
// gl.vertexAttribPointer是将属性绑定到当前的ARRAY_BUFFER
gl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset)


绘制一个三角形

运行 GLSL 着色程序

var primitiveType = gl.TRIANGLES; // 图元类型为(三角形)
var offset = 0;
var count = 3; // 顶点着色器将运行三次
gl.drawArrays(primitiveType, offset, count);


  • 第一次:将会从位置缓冲中读取前两个值赋给属性值 a_position.xa_position.y
  • 第二次:a_position.xy 将会被赋予后两个值
  • 最后一次运行将被赋予最后两个值。


三角形在裁剪空间中的坐标如下:

0, 0,
 0, 0.5,
 0.7, 0,


如果画布大小是 400×300 会得到类似以下的转换

裁剪空间          屏幕空间
0, 0         ->   200, 150
0, 0.5       ->   200, 225
0.7, 0       ->   340, 150



在线示例:点此在新窗口中浏览


20210420010608468.png




完整代码:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <title>WebGL - Fundamentals</title>
  <link type="text/css" href="https://webglfundamentals.org/webgl/resources/webgl-tutorials.css" rel="stylesheet" />
</head>
<body>
  <canvas id="c"></canvas>
</body>
</html>
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<script id="vertex-shader-2d" type="notjs">
  // an attribute will receive data from a buffer
  attribute vec4 a_position;
  // all shaders have a main function
  void main() {
    // gl_Position is a special variable a vertex shader
    // is responsible for setting
    gl_Position = a_position;
  }
</script>
<script id="fragment-shader-2d" type="notjs">
  // fragment shaders don't have a default precision so we need
  // to pick one. mediump is a good default
  precision mediump float;
  void main() {
    // gl_FragColor is a special variable a fragment shader
    // is responsible for setting
    gl_FragColor = vec4(1, 0, 0.5, 1); // return redish-purple
  }
</script>
<script>
  /* eslint no-console:0 consistent-return:0 */
  "use strict";
  function createShader(gl, type, source) {
    var shader = gl.createShader(type);
    gl.shaderSource(shader, source);
    gl.compileShader(shader);
    var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (success) {
      return shader;
    }
    console.log(gl.getShaderInfoLog(shader));
    gl.deleteShader(shader);
  }
  function createProgram(gl, vertexShader, fragmentShader) {
    var program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);
    var success = gl.getProgramParameter(program, gl.LINK_STATUS);
    if (success) {
      return program;
    }
    console.log(gl.getProgramInfoLog(program));
    gl.deleteProgram(program);
  }
  function main() {
    // Get A WebGL context
    var canvas = document.querySelector("#c");
    var gl = canvas.getContext("webgl");
    if (!gl) {
      return;
    }
    // Get the strings for our GLSL shaders
    var vertexShaderSource = document.querySelector("#vertex-shader-2d").text;
    var fragmentShaderSource = document.querySelector("#fragment-shader-2d")
      .text;
    // create GLSL shaders, upload the GLSL source, compile the shaders
    var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
    var fragmentShader = createShader(
      gl,
      gl.FRAGMENT_SHADER,
      fragmentShaderSource
    );
    // Link the two shaders into a program
    var program = createProgram(gl, vertexShader, fragmentShader);
    // look up where the vertex data needs to go.
    var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
    // Create a buffer and put three 2d clip space points in it
    var positionBuffer = gl.createBuffer();
    // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    var positions = [0, 0, 0, 0.5, 0.7, 0];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
    // code above this line is initialization code.
    // code below this line is rendering code.
    webglUtils.resizeCanvasToDisplaySize(gl.canvas);
    // Tell WebGL how to convert from clip space to pixels
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    // Clear the canvas
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    // Tell it to use our program (pair of shaders)
    gl.useProgram(program);
    // Turn on the attribute
    gl.enableVertexAttribArray(positionAttributeLocation);
    // Bind the position buffer.
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
    var size = 2; // 2 components per iteration
    var type = gl.FLOAT; // the data is 32bit floats
    var normalize = false; // don't normalize the data
    var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
    var offset = 0; // start at the beginning of the buffer
    gl.vertexAttribPointer(
      positionAttributeLocation,
      size,
      type,
      normalize,
      stride,
      offset
    );
    // draw
    var primitiveType = gl.TRIANGLES;
    var offset = 0;
    var count = 3;
    gl.drawArrays(primitiveType, offset, count);
  }
  main();
</script>



本地效果:


20210420015156546.png


改造顶点着色器

改造顶点着色器:提供给它像素坐标而不是裁剪空间坐标

<script id="vertex-shader-2d" type="notjs">
  // ~~attribute vec4 a_position;~~ 
  attribute vec2 a_position; // 改成 vec2 类型是因为我们只需要用x和y值。
  uniform vec2 u_resolution; // 添加了一个uniform(全局变量)叫做u_resolution
  void main() {
    // 从像素坐标转换到 0.0 到 1.0
    vec2 zeroToOne = a_position / u_resolution;
    // 再把 0->1 转换 0->2
    vec2 zeroToTwo = zeroToOne * 2.0;
    // 把 0->2 转换到 -1->+1 (裁剪空间)
    vec2 clipSpace = zeroToTwo - 1.0;
    gl_Position = vec4(clipSpace, 0, 1);
  } 
</script>



为了设置 u_resolution 的值我们需要找到它的位置。

var resolutionUniformLocation = gl.getUniformLocation(program, "u_resolution");



绘制一个矩形

通过绘制两个三角形来绘制一个矩形

var positions = [
  10, 20,
  80, 20,
  10, 30,
  10, 30,
  80, 20,
  80, 30,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);


所有类似 gl.uniformXXX 格式的方法都是设置当前着色程序的全局变量。

gl.useProgram(program);
...
// 设置全局变量 分辨率
gl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height);


WebGL 要运行六次顶点着色器来画两个三角形:

// 绘制
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 6;
gl.drawArrays(primitiveType, offset, count);


在线示例:点此在新窗口中浏览

20210420013129864.png



完整代码:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <title>WebGL - 2D Rectangle</title>
  <link type="text/css" href="https://webglfundamentals.org/webgl/resources/webgl-tutorials.css" rel="stylesheet" />
</head>
<body>
  <canvas id="c"></canvas>
</body>
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<!-- vertex shader -->
<script id="vertex-shader-2d" type="notjs">
    attribute vec4 a_position;
    uniform vec2 u_resolution;
    void main() {
       // convert the position from pixels to 0.0 to 1.0
       vec2 zeroToOne = a_position.xy / u_resolution;
       // convert from 0->1 to 0->2
       vec2 zeroToTwo = zeroToOne * 2.0;
       // convert from 0->2 to -1->+1 (clipspace)
       vec2 clipSpace = zeroToTwo - 1.0;
       gl_Position = vec4(clipSpace, 0, 1);
    }
  </script>
<!-- fragment shader -->
<script id="fragment-shader-2d" type="notjs">
    precision mediump float;
    void main() {
      gl_FragColor = vec4(1, 0, 0.5, 1); // return redish-purple
    }
  </script>
<script>
  "use strict";
  function main() {
    // Get A WebGL context
    var canvas = document.querySelector("#c");
    var gl = canvas.getContext("webgl");
    if (!gl) {
      return;
    }
    // Use our boilerplate utils to compile the shaders and link into a program
    var program = webglUtils.createProgramFromScripts(gl, [
      "vertex-shader-2d",
      "fragment-shader-2d",
    ]);
    // look up where the vertex data needs to go.
    var positionAttributeLocation = gl.getAttribLocation(
      program,
      "a_position"
    );
    // look up uniform locations
    var resolutionUniformLocation = gl.getUniformLocation(
      program,
      "u_resolution"
    );
    // Create a buffer to put three 2d clip space points in
    var positionBuffer = gl.createBuffer();
    // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    var positions = [10, 20, 80, 20, 10, 30, 10, 30, 80, 20, 80, 30];
    gl.bufferData(
      gl.ARRAY_BUFFER,
      new Float32Array(positions),
      gl.STATIC_DRAW
    );
    webglUtils.resizeCanvasToDisplaySize(gl.canvas);
    // Tell WebGL how to convert from clip space to pixels
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    // Clear the canvas
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    // Tell it to use our program (pair of shaders)
    gl.useProgram(program);
    // Turn on the attribute
    gl.enableVertexAttribArray(positionAttributeLocation);
    // Bind the position buffer.
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
    var size = 2; // 2 components per iteration
    var type = gl.FLOAT; // the data is 32bit floats
    var normalize = false; // don't normalize the data
    var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
    var offset = 0; // start at the beginning of the buffer
    gl.vertexAttribPointer(
      positionAttributeLocation,
      size,
      type,
      normalize,
      stride,
      offset
    );
    // set the resolution
    gl.uniform2f(
      resolutionUniformLocation,
      gl.canvas.width,
      gl.canvas.height
    );
    // draw
    var primitiveType = gl.TRIANGLES;
    var offset = 0;
    var count = 6;
    gl.drawArrays(primitiveType, offset, count);
  }
  main();
</script>
</html>



在本地的效果:

20210420014516849.png


WebGL认为左下角是 0,0 。 想要像传统二维API那样起点在左上角,需要翻转y轴。

gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);


效果如下:

20210420191719333.png



如果改成 (-1, 1)

20210420195928938.png


效果如下

20210420200014795.png



随机绘制矩形


比如:随机绘制50个随机位置,随机大小,随机颜色的矩形

定义一个片断着色器:可以通过全局变量接收自定义颜色


<script id="fragment-shader-2d" type="notjs">
  precision mediump float;
  uniform vec4 u_color;
  void main() {
    gl_FragColor = u_color;
  }
</script>


代码如下:

  var colorUniformLocation = gl.getUniformLocation(program, "u_color");
  ...
  // 绘制50个随机颜色矩形
  for (var ii = 0; ii < 50; ++ii) {
    // 创建一个随机矩形
    // 并将写入位置缓冲
    // 因为位置缓冲是我们绑定在
    // `ARRAY_BUFFER`绑定点上的最后一个缓冲
    setRectangle(
        gl, randomInt(300), randomInt(300), randomInt(300), randomInt(300));
    // 设置一个随机颜色
    gl.uniform4f(colorUniformLocation, Math.random(), Math.random(), Math.random(), 1);
    // 绘制矩形
    gl.drawArrays(gl.TRIANGLES, 0, 6);
  }
}
// 返回 0 到 range 范围内的随机整数
function randomInt(range) {
  return Math.floor(Math.random() * range);
}
// 用参数生成矩形顶点并写进缓冲
function setRectangle(gl, x, y, width, height) {
  var x1 = x;
  var x2 = x + width;
  var y1 = y;
  var y2 = y + height;
  // 注意: gl.bufferData(gl.ARRAY_BUFFER, ...) 将会影响到
  // 当前绑定点`ARRAY_BUFFER`的绑定缓冲
  // 目前我们只有一个缓冲,如果我们有多个缓冲
  // 我们需要先将所需缓冲绑定到`ARRAY_BUFFER`
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
     x1, y1,
     x2, y1,
     x1, y2,
     x1, y2,
     x2, y1,
     x2, y2]), gl.STATIC_DRAW);
}


在线示例:点此在新窗口中浏览

完整的代码如下:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
  <title>WebGL - 2D Rectangles</title>
  <link type="text/css" href="https://webglfundamentals.org/webgl/resources/webgl-tutorials.css" rel="stylesheet" />
</head>
<body>
  <canvas id="canvas"></canvas>
</body>
<!-- vertex shader -->
<script id="vertex-shader-2d" type="x-shader/x-vertex">
attribute vec2 a_position;
uniform vec2 u_resolution;
void main() {
   // convert the rectangle from pixels to 0.0 to 1.0
   vec2 zeroToOne = a_position / u_resolution;
   // convert from 0->1 to 0->2
   vec2 zeroToTwo = zeroToOne * 2.0;
   // convert from 0->2 to -1->+1 (clipspace)
   vec2 clipSpace = zeroToTwo - 1.0;
   gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
}
</script>
<!-- fragment shader -->
<script id="fragment-shader-2d" type="x-shader/x-fragment">
  precision mediump float;
uniform vec4 u_color;
void main() {
   gl_FragColor = u_color;
}
</script>
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<script>
  "use strict";
  function main() {
    // Get A WebGL context
    /** @type {HTMLCanvasElement} */
    var canvas = document.querySelector("#canvas");
    var gl = canvas.getContext("webgl");
    if (!gl) {
      return;
    }
    // setup GLSL program
    var program = webglUtils.createProgramFromScripts(gl, ["vertex-shader-2d", "fragment-shader-2d"]);
    // look up where the vertex data needs to go.
    var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
    // look up uniform locations
    var resolutionUniformLocation = gl.getUniformLocation(program, "u_resolution");
    var colorUniformLocation = gl.getUniformLocation(program, "u_color");
    // Create a buffer to put three 2d clip space points in
    var positionBuffer = gl.createBuffer();
    // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    webglUtils.resizeCanvasToDisplaySize(gl.canvas);
    // Tell WebGL how to convert from clip space to pixels
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    // Clear the canvas
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    // Tell it to use our program (pair of shaders)
    gl.useProgram(program);
    // Turn on the attribute
    gl.enableVertexAttribArray(positionAttributeLocation);
    // Bind the position buffer.
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
    var size = 2; // 2 components per iteration
    var type = gl.FLOAT; // the data is 32bit floats
    var normalize = false; // don't normalize the data
    var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
    var offset = 0; // start at the beginning of the buffer
    gl.vertexAttribPointer(
      positionAttributeLocation, size, type, normalize, stride, offset);
    // set the resolution
    gl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height);
    // draw 50 random rectangles in random colors
    for (var ii = 0; ii < 50; ++ii) {
      // Setup a random rectangle
      // This will write to positionBuffer because
      // its the last thing we bound on the ARRAY_BUFFER
      // bind point
      setRectangle(
        gl, randomInt(300), randomInt(300), randomInt(300), randomInt(300));
      // Set a random color.
      gl.uniform4f(colorUniformLocation, Math.random(), Math.random(), Math.random(), 1);
      // Draw the rectangle.
      var primitiveType = gl.TRIANGLES;
      var offset = 0;
      var count = 6;
      gl.drawArrays(primitiveType, offset, count);
    }
  }
  // Returns a random integer from 0 to range - 1.
  function randomInt(range) {
    return Math.floor(Math.random() * range);
  }
  // Fill the buffer with the values that define a rectangle.
  function setRectangle(gl, x, y, width, height) {
    var x1 = x;
    var x2 = x + width;
    var y1 = y;
    var y2 = y + height;
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
      x1, y1,
      x2, y1,
      x1, y2,
      x1, y2,
      x2, y1,
      x2, y2,
    ]), gl.STATIC_DRAW);
  }
  main();
</script>
</html>


本地效果:每次刷新颜色大小都会变


20210420195043742.png



拓展

type=“notjs” 是什么意思?

<script> 标签内默认放置的是 JavaScript 代码。


不定义 type 或者定义 type="javascript" 或者 type="text/javascript" ,浏览器则会将内容翻译成 JavaScript。


如果你对 type 有其它任何定义。浏览器会忽略 script 标签的内容。

对浏览器而言 type="notjs" 或者 type="foobar" 都是没有意义的。这样就可以很方便的编辑着色器。








相关实践学习
基于阿里云DeepGPU实例,用AI画唯美国风少女
本实验基于阿里云DeepGPU实例,使用aiacctorch加速stable-diffusion-webui,用AI画唯美国风少女,可提升性能至高至原性能的2.6倍。
目录
相关文章
|
7月前
|
Web App开发 前端开发 JavaScript
WebGL:开始学习 / 理解 WebGL / WebGL 需要掌握哪些知识 / 应用领域 / 前端值得学WebGL吗
WebGL:开始学习 / 理解 WebGL / WebGL 需要掌握哪些知识 / 应用领域 / 前端值得学WebGL吗
137 0
|
30天前
|
移动开发 前端开发 JavaScript
游戏开发技术- 请谈谈WebGL与Canvas的区别,以及在什么情况下更适合使用WebGL。
WebGL和Canvas都是网页游戏开发的关键技术。Canvas是2D绘图API,适合初学者和简单的2D游戏,而WebGL是基于OpenGL的3D渲染标准,用于复杂3D图形和游戏,提供GPU硬件加速。当涉及3D渲染、高级视觉效果或高性能需求时,WebGL是更好的选择。对于轻量级2D应用,Canvas就足够了。某些游戏引擎支持两者自动切换,以实现最佳性能和兼容性。
|
4月前
|
前端开发 数据可视化 API
webGL的基本原理详细教程
webGL的基本原理详细教程
31 0
|
6月前
|
存储 缓存 前端开发
WebGL简介
WebGL简介
77 0
WebGL简介
|
移动开发 JavaScript 前端开发
webgl入门指南(一)
本文适合对图形感兴趣的小伙伴阅读。
webgl入门指南(一)
|
前端开发 JavaScript 异构计算
WebGL基础笔记
WebGL基础笔记
158 0
|
缓存 异构计算
几个简单的小例子手把手带你入门webgl(一)
各位同学们大家好,又到了周末写文章的时间,之前群里有粉丝提问, 就是shader不是很理解。然后今天他就来了, 废话不多说,读完今天的这篇文章你可以学到以下几点: 为什么需要有shader ? shader的作用是什么???? shader 中的每个参数到底是什么意思??怎么去用??? 你如果会了,这篇文章你可以不用看👀,不用浪费时间,去看别的文章。如果哪里写的有问题欢迎大家指正,我也在不断地学习当中。 why need shader 这里我结合自己的思考🤔,讲讲webgl的整个的一个渲染过程。 渲染管线 「Webgl」的渲染依赖底层「GPU」的渲染能力。所以「WEBGL」 渲染流程
几个简单的小例子手把手带你入门webgl(一)
|
存储 缓存 前端开发
几个简单的小例子手把手带你入门webgl(二)
实战——绘制个三角形 在进行实战之前,我们先给你看一张图,让你能大概了解,用原生webgl生成一个三角形需要那些步骤: draw 我们就跟着这个流程图一步一步去操作: 初始化canvas 新建一个webgl画布 <canvas id="webgl" width="500" height="500"></canvas> 创建webgl 上下文: const gl = document.getElementById('webgl').getContext('webgl') 创建着色器程序 着色器的程序这些代码,其实是重复的,我们还是先看下图,看下我们到底需要哪些步骤: shader 那我们就跟着这
几个简单的小例子手把手带你入门webgl(二)
|
监控 前端开发 索引
如何用webgl(three.js)搭建一个3D库房,3D密集架,3D档案室,-第二课
闲话少叙,我们接着第一课继续讲(http://www.cnblogs.com/yeyunfei/p/7899613.html),很久没有做技术分享了。很多人问第二课有没有,我也是抽空写一下第二课。 第一课程提到了在库房的基础上添加上下架 消防 温湿度等等控制 刚好 最近有接到一个客户的需求 是和库房...
2118 0
|
存储 前端开发
WEBGL学习【十五】利用WEBGL实现三维场景的一般思路总结
版权声明:本文为博主原创文章,未经博主允许不得转载。更多学习资料请访问我爱科技论坛:www.52tech.tech https://blog.csdn.net/m0_37981569/article/details/79232119 实现三维场景载入操作的实现步骤: 主要知识点:着色器,纹理贴图,文件载入 实现思路: 获取canvas,初始化WEBGL上下文信息。
1237 0