Шейдеры являются одним из базовых элементов любой программы на WebGL. Без шейдеров сложно что-то сделать. На вход в графический процессов передается лишь набор вершин. Но благодаря вершинному и фрагментному шейдерам этот набор сначала прекращается в набор примтивов, а затем окрашивается, и мы в итоге видим какие-нибудь трехмерные модели.
Если вы посмотрите на ранее использовавшиеся шейдеры (все они пока однотипны), то вы увидите, что в нем используется особый язык с Си-подобным синтаксисом. Например, код вершинного шейдера:
attribute vec3 aVertexPosition; void main(void) { gl_Position = vec4(aVertexPosition, 1.0); }
Этот язык называется GLSL, а если точнее, то язык шейдеров OpenGL ES Shading Language. В свою очередь GLSL основан на C++. GLSL довольно распространен, лично встречал как минимум три книги по данному языку, поэтому при желании вы можете найти соответствующую литературу и более подробно ознакомиться с данным языком.
Этот язык используется в двух частях программы WebGL - в вершинном шейдере и фрагментном шейдере. Для написания кода на OpenGL ES Shading Language используются выражения языка JavaScript.
Практически в самом начале работы конвейера WebGL буфер вершин передается в вершинный шейдер. Вершинный шейдер проходит по всем переданным вершинам и выполняет определенные преобразования, которые применил в программе вершинного шейдера разработчик. Именно вершинный шейдер отвечает за матричные преобразования координат, их смещения и т.д. На выходе он генерирует финальные координаты вершины и передает ее для дальнейшей обработки дальше. До сих пор я использовал простейшее определение шейдера:
attribute vec3 aVertexPosition; void main(void) { gl_Position = vec4(aVertexPosition, 1.0); }
Атрибут aVertexPosition, имеющий тип vec3
, как раз и передает координаты вершины из буфера вершин. И так как каждая вершина представлена
тремя координатами x, y, z, то для передачи вершины используется трехмерный вектор vec3.
Как и каждая программа на С/С++, шейдер имеет основную процедуру main
, в которой происходит генерация окончательных координат вершины,
только уже в виде четырехмерного вектора. А gl_Position
по сути это и есть преобразованные координаты вершины.
В данном случае у нас никаких преобразований и трансформаций не производится, и финальная вершина будет иметь те же координаты, что и переданная в шейдер.
Фрагментный шейдер уже наполняет набор примитивов цветом, раскрашивает их. Фрагменный шейдер никак не влияет на координаты вершины, он влияет только на цветовую составляющую, преобразуя вершины уже в пиксели или фрагменты.
Для простоты ранее также были использованы простейшие определения фрагментных шейдеров, например:
void main(void) { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); }
Здесь также основная программа main, которая устанавливает финальный цвет для пикселя, представленного вершиной. Так как
представление цвета состоит из четырех элементов - RGBA, то также используется для установки цвета тип четырехмерного вектора
vec4
. В данном случае каждый пиксель примитивов у нас окрашивается в белый цвет. А если бы мы захотели покрасить примитивы в
зеленый цвет, то могли бы задать следующее определение цвета: gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);