- 原文地址:Day 4. Shader varyings
- 原文做者:Andrei Lesnitsky
这是 WebGL 系列的第 4 天教程,天天都有新文章发布。git
源代码在这里web
第 3 天咱们学习了如何绘制直线和三角形,先从布置的做业开始:canvas
若是 webgl 只能渲染三角形,那咱们如何绘制矩形呢?咱们能够将一个矩形分红两个三角形。ide
很简单,对吧?函数
让咱们定义三角形顶点的坐标学习
📄 src/webgl-hello-world.jswebgl
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);
太棒了,咱们如今就能够渲染矩形!url
如今让咱们画一个六角形,手绘起来有些困难,因此让咱们建立一个辅助函数spa
📄 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
属性指向最近绑定的缓冲区。请不要忘记这个步骤,这是一个容易出现的错误。
结论
咱们已经学习了将数据传递到片断着色器的另外一种方法。
这是处理顶点颜色、纹理颇有用的方法(咱们稍后将使用纹理)。
做业
用彩虹🌈的七种颜色依次渲染七边形每一个角。
明天见👋