• 原文地址:Day 4. Shader varyings
  • 原文作者:Andrei Lesnitsky

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

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

源代码在这里

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

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

很简略,对吧?

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

???? 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);

太棒了,咱们当初就能够渲染矩形!

当初让咱们画一个六角形,手绘起来有些艰难,所以让咱们创立一个辅助函数

???? 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);

并利用一些简略的数学计算

???? 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 = [];

变体

接下来干嘛呢?让咱们增加一些色彩????。
家喻户晓,咱们能够通过以下形式将色彩传递给片段着色器: 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);  }  `;  

看起来很酷吧?

然而咱们如何从 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 属性指向最近绑定的缓冲区。请不要遗记这个步骤,这是一个容易呈现的谬误。

论断

咱们曾经学习了将数据传递到片段着色器的另一种办法。
这是解决顶点色彩、纹理很有用的办法(咱们稍后将应用纹理)。

作业

用彩虹????的七种色彩顺次渲染七边形每个角。

今天见????