「译」WebGL 系列 04 着色器 varyings 基础

时间:2020-9-7 作者:admin

这是 WebGL 系列的第 4 天教程,每天都有新文章发布。

订阅或者加入邮件列表以便及时获取更新内容。

源代码在这里

第 3 天我们学习了如何绘制直线和三角形,先从布置的作业开始:

如果 webgl 只能渲染三角形,那我们如何绘制矩形呢?我们可以将一个矩形分成两个三角形。

「译」WebGL 系列 04 着色器 varyings 基础

很简单,对吧?

让我们定义三角形顶点的坐标

📄 src/webgl-hello-world.js

gl.uniform4fv(colorUniformLocation, [255, 0, 0, 255]);

  const triangles = [
-     0, 0, // v1 (x, y)
-     canvas.width / 2, canvas.height, // v2 (x, y)
-     canvas.width, 0, // v3 (x, y)
+     // first triangle
+     0, 150, // top left
+     150, 150, // top right
+     0, 0, // bottom left
+     
+     // second triangle
+     0, 0, // bottom left
+     150, 150, // top right
+     150, 0, // bottom right
  ];

  const positionData = new Float32Array(triangles);

「译」WebGL 系列 04 着色器 varyings 基础

太棒了,我们现在就可以渲染矩形!

现在让我们画一个六角形,手绘起来有些困难,所以让我们创建一个辅助函数

📄 src/webgl-hello-world.js

150, 0, // bottom right
  ];

+ function createHexagon(center, radius, segmentsCount) {
+     
+ }
+ 
  const positionData = new Float32Array(triangles);

  const positionBuffer = gl.createBuffer(gl.ARRAY_BUFFER);

我们需要把(360-分段角度)以一个标志性的分段角度逐步遍历。

📄 src/webgl-hello-world.js

gl.uniform2fv(resolutionUniformLocation, [canvas.width, canvas.height]);
  gl.uniform4fv(colorUniformLocation, [255, 0, 0, 255]);

- const triangles = [
-     // first triangle
-     0, 150, // top left
-     150, 150, // top right
-     0, 0, // bottom left
-     
-     // second triangle
-     0, 0, // bottom left
-     150, 150, // top right
-     150, 0, // bottom right
- ];
- 
- function createHexagon(center, radius, segmentsCount) {
-     
+ const triangles = [createHexagon()];
+ 
+ function createHexagon(centerX, centerY, radius, segmentsCount) {
+     const vertices = [];
+ 
+     for (let i = 0; i < Math.PI * 2; i += Math.PI * 2 / (segmentsCount - 1)) {
+         
+     }
+ 
+     return vertices;
  }

  const positionData = new Float32Array(triangles);

并应用一些简单的数学计算

「译」WebGL 系列 04 着色器 varyings 基础

📄 src/webgl-hello-world.js

gl.uniform2fv(resolutionUniformLocation, [canvas.width, canvas.height]);
  gl.uniform4fv(colorUniformLocation, [255, 0, 0, 255]);

- const triangles = [createHexagon()];
+ const triangles = createHexagon(canvas.width / 2, canvas.height / 2, canvas.height / 2, 6);

  function createHexagon(centerX, centerY, radius, segmentsCount) {
      const vertices = [];
+     const segmentAngle =  Math.PI * 2 / (segmentsCount - 1);

-     for (let i = 0; i < Math.PI * 2; i += Math.PI * 2 / (segmentsCount - 1)) {
-         
+     for (let i = 0; i < Math.PI * 2; i += segmentAngle) {
+         const from = i;
+         const to = i + segmentAngle;
+ 
+         vertices.push(centerX, centerY);
+         vertices.push(centerX + Math.cos(from) * radius, centerY + Math.sin(from) * radius);
+         vertices.push(centerX + Math.cos(to) * radius, centerY + Math.sin(to) * radius);
      }

      return vertices;

现在我们该如何渲染一个圆?
实际上,可以使用相同的功能构建一个圆,我们只需要增加“段”的数量。

📄 src/webgl-hello-world.js

gl.uniform2fv(resolutionUniformLocation, [canvas.width, canvas.height]);
  gl.uniform4fv(colorUniformLocation, [255, 0, 0, 255]);

- const triangles = createHexagon(canvas.width / 2, canvas.height / 2, canvas.height / 2, 6);
+ const triangles = createHexagon(canvas.width / 2, canvas.height / 2, canvas.height / 2, 360);

  function createHexagon(centerX, centerY, radius, segmentsCount) {
      const vertices = [];

「译」WebGL 系列 04 着色器 varyings 基础

变体

接下来干嘛呢?让我们添加一些颜色🎨。
众所周知,我们可以通过以下方式将颜色传递给片段着色器: uniform
但这不是唯一的方法。
顶点着色器可以传递 varying给每个顶点的片段着色器,并且将对该值进行插值。

听起来有点复杂,让我们看看它是如何工作的。

我们需要在顶点着色器和片段着色器中都定义一个 varying,确保类型匹配。假如把顶点着色器的 vec3和片段着色器的 vec4进行更改, gl.linkProgram(program)则会加载失败。您可以检查程序是否已成功链接 gl.getProgramParameter(program, gl.LINK_STATUS)以及 gl.getProgramInfoLog(program)程序出现错误,查看发生了什么。

📄 src/webgl-hello-world.js

attribute vec2 position;
  uniform vec2 resolution;

+ varying vec4 vColor;
+ 
  #define M_PI 3.1415926535897932384626433832795

  void main() {
      vec2 transformedPosition = position / resolution * 2.0 - 1.0;
      gl_PointSize = 2.0;
      gl_Position = vec4(transformedPosition, 0, 1);
+ 
+     vColor = vec4(255, 0, 0, 255);
  }
  `;

  const fShaderSource = `
      precision mediump float;
-     uniform vec4 color;
+ 
+     varying vec4 vColor;

      void main() {
-         gl_FragColor = color / 255.0;
+         gl_FragColor = vColor / 255.0;
      }
  `;


  const positionPointer = gl.getAttribLocation(program, 'position');
  const resolutionUniformLocation = gl.getUniformLocation(program, 'resolution');
- const colorUniformLocation = gl.getUniformLocation(program, 'color');

  gl.uniform2fv(resolutionUniformLocation, [canvas.width, canvas.height]);
- gl.uniform4fv(colorUniformLocation, [255, 0, 0, 255]);

  const triangles = createHexagon(canvas.width / 2, canvas.height / 2, canvas.height / 2, 360);

现在让我们尝试使用 gl_Position对圆进行增色。

📄 src/webgl-hello-world.js

gl_PointSize = 2.0;
      gl_Position = vec4(transformedPosition, 0, 1);

-     vColor = vec4(255, 0, 0, 255);
+     vColor = vec4((gl_Position.xy + 1.0 / 2.0) * 255.0, 0, 255);
  }
  `;

「译」WebGL 系列 04 着色器 varyings 基础

看起来很酷吧?

但是我们如何从 js 传递一些特定的颜色?

我们需要创建另一个属性

📄 src/webgl-hello-world.js

const vShaderSource = `
  attribute vec2 position;
+ attribute vec4 color;
  uniform vec2 resolution;

  varying vec4 vColor;
      gl_PointSize = 2.0;
      gl_Position = vec4(transformedPosition, 0, 1);

-     vColor = vec4((gl_Position.xy + 1.0 / 2.0) * 255.0, 0, 255);
+     vColor = color;
  }
  `;


  gl.useProgram(program);

- const positionPointer = gl.getAttribLocation(program, 'position');
+ const positionLocation = gl.getAttribLocation(program, 'position');
+ const colorLocation = gl.getAttribLocation(program, 'color');
+ 
  const resolutionUniformLocation = gl.getUniformLocation(program, 'resolution');

  gl.uniform2fv(resolutionUniformLocation, [canvas.width, canvas.height]);
  const stride = 0;
  const offset = 0;

- gl.enableVertexAttribArray(positionPointer);
- gl.vertexAttribPointer(positionPointer, attributeSize, type, nomralized, stride, offset);
+ gl.enableVertexAttribArray(positionLocation);
+ gl.vertexAttribPointer(positionLocation, attributeSize, type, nomralized, stride, offset);

  gl.drawArrays(gl.TRIANGLES, 0, positionData.length / 2);

为这个属性的设置缓冲区:

📄 src/webgl-hello-world.js

}

  const positionData = new Float32Array(triangles);
+ const colorData = new Float32Array(colors);

  const positionBuffer = gl.createBuffer(gl.ARRAY_BUFFER);
+ const colorBuffer = gl.createBuffer(gl.ARRAY_BUFFER);
+ 
+ gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, colorData, gl.STATIC_DRAW);

  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, positionData, gl.STATIC_DRAW);

用数据填充缓冲区:

📄 src/webgl-hello-world.js

gl.uniform2fv(resolutionUniformLocation, [canvas.width, canvas.height]);

  const triangles = createHexagon(canvas.width / 2, canvas.height / 2, canvas.height / 2, 360);
+ const colors = fillWithColors(360);

  function createHexagon(centerX, centerY, radius, segmentsCount) {
      const vertices = [];
      return vertices;
  }

+ function fillWithColors(segmentsCount) {
+     const colors = [];
+ 
+     for (let i = 0; i < segmentsCount; i++) {
+         for (let j = 0; j < 3; j++) {
+             if (j == 0) { // vertex in center of circle
+                 colors.push(0, 0, 0, 255);
+             } else {
+                 colors.push(i / 360 * 255, 0, 0, 255);
+             }
+         }
+     }
+ 
+     return colors;
+ }
+ 
  const positionData = new Float32Array(triangles);
  const colorData = new Float32Array(colors);

并设置属性指针(属性从缓冲区读取数据的方式)。

📄 src/webgl-hello-world.js

gl.enableVertexAttribArray(positionLocation);
  gl.vertexAttribPointer(positionLocation, attributeSize, type, nomralized, stride, offset);

+ gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
+ 
+ gl.enableVertexAttribArray(colorLocation);
+ gl.vertexAttribPointer(colorLocation, 4, type, nomralized, stride, offset);
+ 
  gl.drawArrays(gl.TRIANGLES, 0, positionData.length / 2);

注意在调用 gl.bindBuffer属性之前,把 gl.vertexAttribPointer属性指向最近绑定的缓冲区。请不要忘记这个步骤,这是一个容易出现的错误。

「译」WebGL 系列 04 着色器 varyings 基础

结论

我们已经学习了将数据传递到片段着色器的另一种方法。
这是处理顶点颜色、纹理很有用的方法(我们稍后将使用纹理)。

作业

用彩虹🌈的七种颜色依次渲染七边形每个角。

明天见👋

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。