共计 6580 个字符,预计需要花费 17 分钟才能阅读完成。
- 原文地址: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
属性指向最近绑定的缓冲区。请不要遗记这个步骤,这是一个容易呈现的谬误。
论断
咱们曾经学习了将数据传递到片段着色器的另一种办法。
这是解决顶点色彩、纹理很有用的办法(咱们稍后将应用纹理)。
作业
用彩虹???? 的七种色彩顺次渲染七边形每个角。
今天见????