Чтобы установить для каждой вершины цвета, нам надо задать буфер цветов для вершины, а также определить дополнительный атрибут, который будет передавать данные цветов в шейдеры. Посмотрим на практике. Код нашей веб-странички будет следующим:
<!DOCTYPE html> <html> <head> <title>WebGL</title> <meta charset="utf-8" /> </head> <body> <canvas id="canvas3D" width="400" height="300">Ваш браузер не поддерживает элемент canvas</canvas> <script id="shader-fs" type="x-shader/x-fragment"> varying highp vec4 vColor; void main(void) { gl_FragColor = vColor; } </script> <script id="shader-vs" type="x-shader/x-vertex"> attribute vec3 aVertexPosition; attribute vec3 aVertexColor; varying highp vec4 vColor; void main(void) { gl_Position = vec4(aVertexPosition, 1.0); vColor = vec4(aVertexColor, 1.0); } </script> <script type="text/javascript"> var gl; var shaderProgram; var vertexBuffer; // буфер вершин var colorBuffer; //буфер цветов // установка шейдеров function initShaders() { var fragmentShader = getShader(gl.FRAGMENT_SHADER, 'shader-fs'); var vertexShader = getShader(gl.VERTEX_SHADER, 'shader-vs'); shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram); if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert("Не удалось установить шейдеры"); } gl.useProgram(shaderProgram); shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor"); gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute); } // Функция создания шейдера function getShader(type,id) { var source = document.getElementById(id).innerHTML; var shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert("Ошибка компиляции шейдера: " + gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } return shader; } // установка буферов вершин и индексов function initBuffers() { var vertices = [ 0.0, 0.5, 0.0, -0.5, -0.5, 0.0, 0.5, -0.5, 0.0 ]; // установка буфера вершин vertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); vertexBuffer.itemSize = 3; vertexBuffer.numberOfItems = 3; var сolors = [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0 ]; colorBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(сolors), gl.STATIC_DRAW); } // отрисовка function draw() { gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); gl.clear(gl.COLOR_BUFFER_BIT); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexBuffer.itemSize, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, vertexBuffer.itemSize, gl.FLOAT, false, 0, 0); gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.numberOfItems); } window.onload=function(){ var canvas = document.getElementById("canvas3D"); try { gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); } catch(e) {} if (!gl) { alert("Ваш браузер не поддерживает WebGL"); } if(gl){ gl.viewportWidth = canvas.width; gl.viewportHeight = canvas.height; initShaders(); initBuffers(); draw(); } } </script> </body> </html>
После запуска у нас получится следующий красивый треугольничек:
Теперь разберем, что же тут происходит. Вначале рассмотрим основную программу WebGL, а потом шейдеры.
Во-первых, поскольку у нас в шейдеры будут передаваться наборы из двух буферов - цвета и вершин, то мы создаем два атрибута - для каждого типа:
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor"); gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
Затем в функции initBuffers() задаем два буфера. Задание буфера цветов во многом похоже на буфер вершин:
var сolors = [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0 ]; colorBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(сolors), gl.STATIC_DRAW);
Значения 1.0, 0.0, 0.0
передает цвет первой вершины, в данном случае это красный цвет.
Следующая тройка 0.0, 0.0, 1.0,
окрашивает вторую вершину в синий цвет, а третья тройка - в зеленый.
Если при определении буфера вершин каждая тройка обозначает соответственно координаты x, y, z, то при определении буфера цветов три компоненты отвечают за значения rgb - то есть цветовые компоненты.
И далее похожим образом создаем буфер цветов и связываем с контекстом WebGL. Затем уже в функции отрисовки draw() у нас возникает проблема, что надо установить два атрибута - для передачи вершин и цветов. Поэтому мы производим установку последовательно: сначала для одного, потом для другого. А общий принцип одинаков для всех. Теперь перейдем к шейдерам, где эти атрибуты используются.
В вершинном шейдере мы получаем атрибуты, установленные в основной программе WebGL:
attribute vec3 aVertexPosition; attribute vec3 aVertexColor;
Следующая переменная varying highp vec4 vColor;
будет передавать цвет из вершинного шейдера во фрагментный. Поэтому у нее
используется модификатор varying
.
Затем в самой программе устанавливаются значения:
gl_Position = vec4(aVertexPosition, 1.0); vColor = vec4(aVertexColor, 1.0);
И во фрагментном шейдере мы используем для установки финального цвета именно цвет, переданный через переменную vColor.