В прошлой главе мы рассмотрели настройку и использование буферов и отрисовку примитивов, оставив на будущее работу шейдеров. Теперь рассмотрим использование шейдеров в программе и для примера возьмем небольшое веб-приложение, которое рисует набор линий:
<!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"> void main(void) { gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); } </script> <!-- вершинный шейдер --> <script id="shader-vs" type="x-shader/x-vertex"> attribute vec3 aVertexPosition; void main(void) { gl_Position = vec4(aVertexPosition, 1.0); } </script> <script type="text/javascript"> var gl; var shaderProgram; // программа шейдеров var vertexBuffer; // буфер вершин var indexBuffer; //буфер индексов // установка шейдеров 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); //связываем контекст WebGL с программой шейдеров gl.linkProgram(shaderProgram); if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert("Не удалось установить шейдеры"); } // начинаем использовать программу шейдеров gl.useProgram(shaderProgram); // установка атрибута позиции вершин shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); } // Функция создания шейдера - запускается для обоих шейдеров 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() { vertices =[ -0.5, -0.5, 0.0, -0.5, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.5, -0.5, 0.0]; indices = [0, 1, 1, 2, 2, 3, 3, 4]; vertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); vertexBuffer.itemSize = 3; indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); indexBuffer.numberOfItems = indices.length; } 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.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexBuffer.itemSize, gl.FLOAT, false, 0, 0); gl.drawElements(gl.LINES, indexBuffer.numberOfItems, gl.UNSIGNED_SHORT,0); } 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>
Здесь нас интересуют две функции: initShaders()
и getShader()
. Поскольку создание обоих шейдеров предусматривает
однотипные операции, все эти операции были вынесены в отдельную функцию getShader()
. В эту функцию передаются два параметра: type (тип
шейдера) и id элемента, который содержит код шейдера. Поскольку метод gl.createShader()
может создавать шейдеры обоих типов, то
в этот метод в качестве параметра и передаем тип создаваемого шейдера. В качестве типа используем встроенные константы gl.FRAGMENT_SHADER
и gl.VERTEX_SHADER
В функции initShaders()
мы создаем оба шейдера и инициализируем программу шейдеров shaderProgram
и затем устанавливаем
атрибут.
Потом уже в функции отрисовке через программу шейдеров мы используем ранее созданные шейдеры. Таков вкратце общий механизм использования шейдеров.