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