- 原文地址: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
屬性指向最近綁定的緩衝區。請不要忘記這個步驟,這是一個容易出現的錯誤。
結論
咱們已經學習了將數據傳遞到片斷着色器的另外一種方法。
這是處理頂點顏色、紋理頗有用的方法(咱們稍後將使用紋理)。
做業
用彩虹🌈的七種顏色依次渲染七邊形每一個角。
明天見👋