Webgl руководство по программированию

Время на прочтение
13 мин

Количество просмотров 46K

Всем привет!

Хочу начать вольный перевод отличной книги «WebGL Beginner’s Guide», которая, на мой взгляд, будет интересна не только новичкам, но и более продвинутым разработчикам.

Содержание:

  • Глава 1: Начиная работать с WebGL
  • Глава 2: Рендеринг геометрии
  • Глава 3: Освещение
  • Глава 4: Камера
  • Глава 5: Движение
  • Глава 6: Цвет, глубина и альфа-смешение
  • Глава 7: Текстуры
  • Глава 8: Выбор
  • Глава 9: Собираем все вместе
  • Глава 10: Дополнительные методы

WebGL первоначально была основана на OpenGL ES 2.0 (ES означает Embedded Systems), версии спецификации OpenGL для таких устройств как iPhone от Apple и iPad. Но спецификация развивалась, стала независимой, ее основная цель это обеспечение переносимости между различными операционными системами и устройствами. Идея веб-интерфейса, рендеринг в реальном времени открыли новую вселенную возможностей для веб-3D сред, таких как видеоигры, научная и медицинская визуализация. Кроме того, из-за широкого распространения веб-браузеров, эти и другие виды 3D-приложений могут быть запущены на мобильных устройствах, таких как смартфоны и планшеты. Если вы хотите создать свою первую веб-видеоигру, 3D арт-проект для виртуальной галереи, визуализацию данных ваших экспериментов или любое другое 3D-приложение, вы должны иметь ввиду, что первым шагом должно быть то, что вы должны убедиться, что у вас есть подходящая среда.
В этой главе вы сможете:

  • Понять структуру WebGL-приложения
  • Создавать свои области рисования (canvas)
  • Проверить WebGL-возможности вашего браузера
  • Понять, как устроена машина состояний WebGL
  • Изменять переменные WebGL, которые влияют на вашу сцену
  • Загружать и исследовать полнофункциональные сцены

Системные требования

WebGL является веб-основой API 3D-графики. Как таковая она не требует установки. В то время, когда писалась это книга, вы автоматически могли работать с WebGL, если у вас установлен один из следующих интернет-браузеров:

  • Firefox 4.0 или более поздней версии
  • Google Chrome 11 или более поздней версии
  • Safari (OSX 10.6 или более поздней версии). По умолчанию WebGL отключена, но вы можете включить ее, установив опцию Enable WebGL в меню Developer
  • Opera 12 или более поздней версии

Чтобы получить обновленный список веб-браузеров, которые поддерживают WebGL, перейдите по следующей ссылке . Вы также должны убедиться, что на вашем компьютере имеется видеокарта. Если вы хотите быстро проверить, поддерживает ли ваша конфигурация WebGL, то перейдите по этой ссылке.

Что представляет собой WebGL

WebGL является 3D графической библиотекой, которая позволяет современным интернет-браузерам отрисовывать 3D-сцены стандартным и эффективным способом. Согласно Википедии, рендеринг это визуализация процесса создания изображения из модели с помощью компьютерной программы. Поскольку этот процесс выполняется на компьютере, существуют различные способы получения таких изображений.
Первое различие, которое мы должны сделать, используем ли мы какие-либо специальные графические аппаратные средства или нет. Мы можем говорить о программном рендеринге, в тех случаях, когда все расчеты, необходимые для отрисовки 3D-сцен выполняются с использованием основного процессора компьютера; с другой стороны, мы используем термин аппаратного рендеринга, в тех случаях, когда есть графический процессор (GPU) для вычисления 3D-графики в реальном времени. С технической точки зрения, аппаратный рендеринг является гораздо более эффективным по сравнению с программным, потому что есть специализированные аппаратные составляющие, которые обрабатывают операции. Но, с другой стороны, программный рендеринг обычно более распространен из-за нехватки аппаратных зависимостей.
Второе различие, которое мы можем сделать, это происходит ли процесс рендеринга локально или удаленно. Когда изображение, которое должно быть отображено сложное, то рендеринг, скорее всего, будет происходить удаленно. Это случай 3D-анимационных фильмов, когда выделенные серверы с большим количеством аппаратных ресурсов позволяют рендерить сложные сцены. Мы будем называть это серверным рендерингом. Противоположностью этому является рендеринг, выполняющийся локально. Мы будем называть это клиентским рендерингом.
WebGL имеет клиенто-ориентированный подход; элементы, которые составляют части 3D-сцены, обычно загружаются с сервера. Однако, вся дальнейшая обработка, необходимая для получения изображения выполняется локально, с помощью графического оборудования клиента.
По сравнению с другими технологиями (например, Java 3D, Flash и Unity Web Player Plugin) WebGL имеет ряд преимуществ:

  • JavaScript программирование: JavaScript это «родной» язык для веб-разработчиков и веб-браузеров. Работа с JavaScript позволяет получить доступ ко всем DOM-элементам, а также легко с ними обращаться, в отличие от общения с апплетами. Так как WebGL программируется в JavaScript, то это облегчает интеграцию WebGL-приложений с другими JavaScript – библиотеками, такими как JQuery и другими технологиями HTML5.
  • Автоматическое управление памятью: в отличие от своего собрата OpenGL и других технологий, где есть конкретные операции выделения и освобождения памяти вручную, в WebGL нет такой необходимости. Из этого следует, что при выходе JavaScript переменной из области видимости, память, занимаемая ей, автоматически освобождается. Это чрезвычайно облегчает программирование, уменьшает объем кода, делает его более ясным и понятным.
  • Проницаемость: благодаря современным технологическим достижениям, веб-браузеры с поддержкой JavaScript устанавливаются на смартфоны и планшетные устройства. На момент написания, Mozilla Foundation является программой для тестирования возможностей WebGL в телефонах Motorola и Samsung. Существуют подобные разработки поддержки WebGL для платформы Android.
  • Производительность: производительность приложений WebGL сопоставима с эквивалентными автономными приложениями (с некоторыми исключениями). Это происходит благодаря способности WebGL иметь доступ к локальным аппаратным ускорителям графики. До сих пор, многие веб-технологии для 3D рендеринга используют программный рендеринг.
  • Нулевая компиляция: учитывая, что WebGL написана на JavaScript, то нет необходимости в предварительной компиляции кода перед выполнением в веб-браузере. Это позволяет вносить изменения на лету и смотреть, как эти изменения влияют на 3D веб-приложение. Тем не менее, когда мы затронем тему шейдеров, то мы поймем, что нуждаемся в некоторой компиляции. Однако, это происходит с помощью наших графических аппаратных средств, а не в нашем браузере.

Структура WebGL приложения

Как и в любой библиотеке 3D-графики, в WebGL необходимо присутствие определенных компонентов для создания 3D-сцен. Эти основные элементы будут рассмотрены в первых четырех главах книги. Начиная с 5 главы, мы будем рассматривать элементы, которые не имеют отношения к работе с 3D-сценой, такие как цвета и текстуры, а затем, позже, мы перейдем к более сложным темам.
Элементы, к которым мы обращаемся, следующие:

  • Canvas: это заполнитель, где будет отображаться сцена. Это стандартный HTML5-элемент, который может быть доступен с помощью объектной модели документа (DOM) через JavaScript.
  • Объекты: это 3D сущности, которые составляют часть сцены. Эти сущности состоят из треугольников. Во второй главе мы увидим, как WebGL обрабатывает геометрию. Мы будем использовать WebGL-буферы для хранения многоугольников и увидим, как WebGL использует буферы для визуализации объектов на сцене.
  • Свет: ничего в 3D мире нельзя увидеть, если нет никаких источников света. Этот элемент любого WebGL-приложения будет рассмотрен в 3 главе. Мы узнаем, что WebGL использует шейдеры для моделирования света на сцене. Так же мы посмотрим, как 3D-объекты отражают и поглощают свет в соответствии с законами физики, а также обсудим иные модели света, которые мы можем создать в WebGL для визуализации наших объектов.
  • Камера: выступает в качестве холста для просмотра 3D-мира. Посмотрим, как с ее помощью можно исследовать 3D-сцену. В 4 главе, будут рассмотрены различные матричные операции, которые необходимы для получения перспективного вида. Также мы поймем, как эти операции могут моделировать поведение камеры.

Эта глава посвящена первому элементу нашего списка – canvas. В ближайших разделах мы увидим, как создать canvas (холст) и как настроить WebGL-контекст.

Создание HTML5 canvas

Давайте создадим веб-страницу и добавим HTML5 canvas. Canvas представляет собой прямоугольный элемент на веб-странице, где будет отображаться 3D-сцена.

  1. Используя ваш любимый редактор, создайте веб-страницу со следующим содержимым:
    	<!DOCTYPE html>
    	<html>
    		<head>
    			<title> WebGL Beginner's Guide - Setting up the canvas </title>
    			<style type="text/css">
    				canvas {border: 2px dotted blue;}
    			</style>
    		</head>
    		<body>
    			<canvas id="canvas-element-id" width="800" height="600">
    				Your browser does not support HTML5
    			</canvas>
    		</body>
    	</html>
    	

  2. Сохраните файл как ch1_Canvas.html.
  3. Откройте этот файл через поддерживаемый браузер.
  4. Вы должны увидеть что-то похожее на это:

Что только что произошло?
Мы только что создали простую веб-страницу с содержащимся в ней холстом. Этот холст будет содержать наши 3D-приложения. Давайте разберем некоторые важные элементы, представленные в этом примере.
Определение CSS-стиля для границы
Это кусок кода, который определяет стиль холста:

<style type="text/css">
	canvas {border: 2px dotted blue;}
</style>

Как вы можете себе представить, этот код не является фундаментальным для создания WebGL-приложений. Тем не менее, синяя пунктирная линия является хорошим способом проверки, где находится холст, учитывая, что полотно будет изначально пусто.
Понимание атрибутов холста
В предыдущем примере есть 3 атрибута:

  • Id: это идентификатор холста в объектной модели документа (DOM)
  • Ширина и высота: эти два атрибута определяют размер нашего холста. Когда эти два атрибута отсутствуют Firefox, Chrome, и WebKit по умолчанию будут использовать холст 300×150.

Что делать, если canvas не поддерживается?
Если вы видите, что на экране появилось сообщение: Your browser does not support HTML5 (которое было записано между тегами и ), то вы должны убедиться, что используете браузер, который поддерживает WebGL.
Если вы используете Firefox и все еще видите сообщение HTML5 not supported, то проверьте, что включена поддержка WebGL. Для этого введите в адресной строке about:config, а затем посмотрите на свойство webgl.disabled. Если установлено в true то перейдите и поменяйте. При повторном запуске Firefox и загрузке ch1_Canvas.html вы должны увидеть пунктирные линии холста, то есть все в порядке.
При удаленном подключении, если вы не видите холст, то это может быть связано с тем, что Firefox внес в черный список некоторые драйверы графической карты. В этом случае ничего не остается, как использовать другой компьютер.

Доступ к контексту WebGL

Контекст WebGL это дескриптор (точнее, объект JavaScript), через который мы можем получить доступ ко всем функциям и атрибутам WebGL. Они составляют Application Program Interface WebGL (API).
Мы собираемся создать функцию JavaScript, которая будет проверять можно ли на холсте получить контекст WebGL. В отличие от других библиотек JavaScript, которые должны быть загружены и подключены в рабочий проект, WebGL уже находится в вашем браузере. Другими словами если вы используете один из поддерживаемых браузеров, то вам не нужно устанавливать и включать какую-либо библиотеку.
Мы собираемся изменить предыдущий пример, добавив JavaScript-функцию, которая будет проверять наличие поддержки WebGL в вашем браузере (пытаться получить дескриптор). Эта функция будет вызываться при загрузке страницы. Для этого мы будем использовать стандартное DOM-событие onLoad.

  1. Открываем файл ch1_Canvas.html в вашем любимом текстовом редакторе (в идеале можно выбрать синтаксис для HTML/JavaScript).
  2. Добавьте следующий код прямо под тегом

Мы должны вызвать эту функцию на событие onLoad. Измените содержимое тега body на следующее:

	<body onLoad ="getGLContext()">

Сохраните файл ch1_GL_Context.html.
Откройте файл ch1_GL_Context.html, используя один из браузеров, поддерживающих WebGL.
Если вы не сможете запустить WebGL, то увидите примерно следующее диалоговое окно:

Что же только что произошло?
Используя переменную JavaScript (gl), мы получили ссылку на контекст WebGL. Давайте вернемся и проверим код, который позволяет получить доступ к WebGL:

	var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
	for (var i = 0; i < names.length; ++i) 
	{
		try 
		{
			gl = canvas.getContext(names[i]);
		} 
		catch(e) {}
		if (gl) 
			break;
	}

Метод холста getContext дает нам доступ к WebGL. Все, что нам нужно, это указать имя контекста, которое может быть разным у разных производителей. Поэтому мы сгруппировали все возможные имена контекста в массив names. Крайне важно проверить спецификацию WebGL (вы можете найти ее в интернете) на любые изменения, касающиеся соглашения об именовании.
getContext так же может обеспечить доступ к 2D-графике HTML5 при использовании 2D –библиотеки, в зависимости от имени контекста. В отличие от WebGL это соглашение об именах является стандартным. API 2D-графики HTML5 является полностью независимым от WebGL и выходит за рамки этой книги.

Машина состояний WebGL

Контекст WebGL может пониматься как машина состояний: как только вы измените любой из его атрибутов, то модификация сохранит свое состояние, пока вы снова не поменяете значение атрибута. В любой момент вы можете запросить состояние этих атрибутов, поэтому вы можете определить текущее состояние вашего WebGL-контекста. Давайте проанализируем это поведение на примере.
В этом примере мы собираемся научиться изменять цвет, который мы используем для очистки холста:

  1. Используя ваш любимый текстовый редактор, откройте файл ch1_GL_Attributes.html:
    	<html>
    		<head>
    			<title> 
    				WebGL Beginner's Guide - Setting WebGL context attributes 
    			</title>
    			<style type="text/css">
    				canvas {border: 2px dotted blue;}
    			</style>
    			<script>
    				var gl = null;
    				var c_width = 0;
    				var c_height = 0;
    				window.onkeydown = checkKey;
    				function checkKey(ev)
    				{
    					switch(ev.keyCode)
    					{
    						case 49:
    						{ 
    							// 1
    							gl.clearColor(0.3,0.7,0.2,1.0);
    							clear(gl);
    							break;
    						}
    						case 50:
    						{
    							 // 2
    							gl.clearColor(0.3,0.2,0.7,1.0);
    							clear(gl);
    							break;
    						}
    						case 51:
    						{ 
    							// 3
    							var color = gl.getParameter(gl.COLOR_CLEAR_VALUE);
    							// Don't get confused with the following line. It 
    							// basically rounds up the numbers to one decimal cipher 
    							//just for visualization purposes
    							alert('clearColor = (' + 
    								Math.round(color[0]*10)/10 + 
    								',' + Math.round(color[1]*10)/10+
    								',' + Math.round(color[2]*10)/10+')');
    							window.focus();
    							break;
    						}
    					}
    				}
    				function getGLContext()
    				{
    					var canvas = document.getElementById("canvas-element-id");
    					if (canvas == null)
    					{
    						alert("there is no canvas on this page");
    						return;
    					}
    					var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
    					var ctx = null;
    					for (var i = 0; i < names.length; ++i) 
    					{
    						try 
    						{
    							ctx = canvas.getContext(names[i]);
    						} 
    						catch(e) {}
    						if (ctx) 
    							break;
    					}
    					if (ctx == null)
    					{
    						alert("WebGL is not available");
    					}
    					else
    					{
    						return ctx;
    					}
    				}
    				function clear(ctx)
    				{
    					ctx.clear(ctx.COLOR_BUFFER_BIT); 
    					ctx.viewport(0, 0, c_width, c_height);
    				}
    				function initWebGL()
    				{
    					gl = getGLContext();
    				}
    			</script>
    		</head>
    		<body onLoad="initWebGL()">
    			<canvas id="canvas-element-id" width="800" height="600">
    				Your browser does not support the HTML5 canvas element.
    			</canvas>
    		</body>
    	</html>
    	

  2. Вы видите, что этот файл очень похож на наш предыдущий пример. Тем не менее, появляются новые конструкции, которые мы кратко объясним. Этот файл содержит 4 JavaScript-функции:
    • checkKey: это вспомогательная функция. Она захватывает ввод с клавиатуры и выполняет код в зависимости от введенной команды
    • getGLContext: подобна той, которую мы уже рассмотрели
    • clear: очищает холст текущим цветом, который является одним из атрибутов WebGL-контекста. Как упоминалось ранее, WebGL работает как конечный автомат, поэтому он будет поддерживать выбранный цвет, чтобы очистить холст до того, как этот цвет будет изменен с помощью функции gl.clearColor.
    • initWebGL: эта функция может заменить функцию getGLContext, как функция, вызываемая на событие onLoad документа. Эта функция вызывает улучшенную версию getGLContext, которая возвращает контекст в переменную ctx. Затем этот контекст присваивается глобальной переменно gl.

  3. Откройте файл test_gl_attributes.html, используя поддерживаемый браузер.
  4. Нажмите 1. Вы увидите, как холст меняет свой цвет на зеленый. Если вы захотите узнать используемый цвет, то нажмите 3.
  5. Холст окрашен в зеленый цвет, пока мы не решим изменить цвет очистки, вызвав функцию gl.clearColor. Давайте изменим его, нажав 2. Если посмотреть на код, то можно увидеть, что цвет изменится на синий. Если вы хотите узнать какой это точно цвет, нажмите 3.

Что же только что произошло?
В этом примере мы увидели как мы можем изменять и задавать цвет, который WebGL использует для очистки холста с помощью вызова функции clearColor. Соответственно, мы использовали функцию getParameter (gl.COLOR_CLEAR_VALUE), чтобы получить текущее значение цвета заливки холста.
На протяжении всей книги мы будем видеть аналогичные конструкции, когда конкретные функции устанавливают атрибуты контекста, а WebGL-функция getParameter извлекает текущее значение атрибутов в зависимости от переданного аргумента (в нашем примере это COLOR_CLEAR_VALUE).
Получить доступ к WebGL API, используя контекст
Также важно отметить, что все функции WebGL доступны через контекст WebGL. В наших примерах контекст находится в переменной gl. Таким образом, любой вызов WebGL API будет производится с помощью этой переменной.

Загружаем 3D-сцену

До сих пор мы видели как можно получить и настроить контекст WebGL; следующим шагом является обсуждение объектов, освещения и камеры. Однако, почему мы должны ждать, чтобы посмотреть на возможности WebGL? В этом разделе мы посмотрим как выглядит WebGL-сцена.

Виртуальный автосалон

Благодаря книге, мы будем развивать виртуальное приложение автосалона с помощью WebGL. На данный момент, мы будем загружать одну простую сцену на холст. Эта сцена будет содержать автомобиль, несколько источников света и камеру.
После прочтения книги, вы сможете создавать сцены, подобные той, которую мы будем создавать.

  1. Откройте файл ch1_Car.html в любом из поддерживаемых браузеров
  2. Вы увидите WebGL-сцену с автомобилем, как показано ниже:

  3. С помощью ползунка можно интерактивно обновлять четыре источника света, которые определены для этой сцены. Каждый источник света состоит из трех элементов: окружение, диффузные и зеркальные элементы. Более подробно мы рассмотрим тему освещения в 3 главе.
  4. Нажмите и потащите на холсте, чтобы повернуть автомобиль и визуализировать его с разных сторон. Вы можете увеличить его, нажав клавишу Alt при перетаскивании мышью на холсте. Также вы можете использовать клавиши со стрелками, чтобы поворачивать камеру вокруг автомобиля. Убедитесь в том, что холст находится в фокусе, нажав на него, прежде чем использовать клавиши со стрелками. В 4 главе мы посмотрим, как создать и работать с камерой в WebGL.
  5. Для достижения эффекта, который возникает при нажатии на клавиши со стрелками, мы используем JavaScript-таймеры. Мы поговорим об анимации в 5 главе.
  6. Используйте виджет выбора цвета, как показано на предыдущем скриншоте, чтобы изменить цвет автомобиля. Использование цветов будет рассмотрено в 6 главе. Главы 7-10 будет описано использование текстур, выбор объектов на сцене, как построить виртуальный автосалон и передовые WebGL-методы соответственно.

Что же только что произошло?
Мы загрузили простую сцену в интернет-браузер с помощью WebGL.
Эта сцена состоит из:

  • Холста, с помощью которого мы видим сцену
  • Несколько полигонов (объектов), которые представляют собой части автомобиля: крыша, окна, фары, крылья, двери, колеса, спойлер бамперы и так далее.
  • Источники света, в противном случае все было бы черным
  • Камера, которая определяет наше положение в 3D-мире. Камера, может быть интерактивной, то есть положение наблюдателя может меняться, в зависимости от пользовательского ввода. В этом примере мы использовали левую и правую клавиши со стрелками и мышь, чтобы перемещать камеру вокруг автомобиля.

Существуют и другие элементы, которые не охвачены в данном примере, такие как текстуры, цвета и специальные световые эффекты (зеркальность). Не паникуйте! Позже будут рассмотрены все элементы более подробно.

Резюме

В этой главе мы рассмотрели четыре основных элемента, которые всегда присутствуют в любом WebGL-приложении: холст, объект, свет и камера.
Мы узнали, как добавить на веб-страницу HTML5 canvas, как установить его ID, ширину и высоту. После этого мы написали код, который создавал контекст WebGL. Мы увидели, что WebGL работает как машина состояний, как таковая, мы можем запросить любую из своих переменных, использую функцию getParameter.
В следующей главе мы узнаем, как определить, загрузить и отрисовать 3D-модель в WebGL-сцене.

Ресурсы к этой статье вы можете скачать по ссылке

Всем большое спасибо за внимание!

Предложите, как улучшить StudyLib

(Для жалоб на нарушения авторских прав, используйте

другую форму
)

Ваш е-мэйл

Заполните, если хотите получить ответ

Оцените наш проект

1

2

3

4

5

   Исходный код книги https://download.csdn.net/download/qfire/10371055

В 2009 году Хронос создал рабочую группу WebGL, начал разработку спецификации WebGL на основе OpenGL ES и выпустил первую версию спецификации WebGL в 2011 году. Эта книга в основном основана на первом издании спецификации WebGL. Последующие обновления в настоящее время выпускаются в виде черновиков. Вы также можете обратиться к нему при необходимости. www.khronos.org/registry/webgl/specs/1.0/

1.1 Структура программы WebGL

   

1.2 Что такое холст?

До появления HTML5, если вы хотите отображать изображения на веб-странице, вы могли использовать только тег <img> собственного решения, предоставляемый HTML. Хотя с этим тегом просто отображать изображения, он может отображать только статические изображения и не может быть нарисован и визуализирован в реальном времени. Поэтому позже появились некоторые сторонние решения, например Flash Player. С появлением HTML5 все изменилось: появился тег <canvas>, позволяющий JavaScript динамически рисовать графику.

Пример

Опорожненный

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="utf-8"/>
	<title>Clear canvas</title>
</head>
<body onload="main()">
	<canvas id="webgl" width="400" height="400">
		Please use the browser supporting "canvas"
	</canvas>

	<script src="../lib/webgl-utils.js"></script>
	<script src="../lib/webgl-debug.js"></script>
	<script src="../lib/cuon-utils.js"></script>
	<script src="HelloCanvas.js"></script>
</body>
</html>
function main() {
	var canvas = document.getElementById('webgl');
	var gl = getWebGLContext(canvas);
	if (!gl) {
		console.log("Failed to get the rendering context for WebGL");
		return;
	}
	//RGBA
	gl.clearColor(0.0, 0.0, 0.0, 1.0);
	 // Пусто
	gl.clear(gl.COLOR_BUFFER_BIT);
	 // Рисуем точки
	//gl.drawColor(1.0, 0.0, 0.0, 1.0);
	 gl.drawPoint (0, 0, 0, 10); // Положение и размер точки
}

Нарисуйте красную точку размером 10 пикселей. WebGL обрабатывает трехмерную графику, поэтому нам нужно указать трехмерные координаты для этой точки.

WebGL полагается на новый механизм рисования, называемый шейдером. Шейдеры обеспечивают проникновенный и мощный способ рисования 2D или 3D графики, и все программы WebGL должны его использовать. Шейдер не только мощный, но и более сложный, и им нельзя управлять с помощью простой команды рисования.

// Программа вершинного шейдера
var VSHADER_SOURCE = 
   'void main() {n' + 
       'gl_Position = vec4 (0.0, 0.0, 0.0, 1.0);  n' + // Устанавливаем координаты
       'gl_PointSize = 10.0;  n' + // Установить размер
   '}n';
 // Программа фрагментного шейдера
var FSHADER_SOURCE = 
   'void main() {n' +
       'gl_FragColor = vec4 (1.0, 0.0, 0.0, 1.0);  n' + // Устанавливаем цвет
   '}n';
function main() {
	var canvas = document.getElementById('webgl');
	var gl = getWebGLContext(canvas);
	if (!gl) {
		console.log("Failed to get the rendering context for WebGL");
		return;
	}
	 // Инициализируем шейдер
	if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
		console.log('Failed to initialize shaders.');
		return ;
	}
	//RGBA
	gl.clearColor(0.0, 0.0, 0.0, 1.0);
	 // Пусто
	gl.clear(gl.COLOR_BUFFER_BIT);
	 // Рисуем точки
	 gl.drawArrays (gl.POINTS, 0, 1); // Положение и размер точки
}

WebGL требует двух шейдеров.

  • Вершинный шейдер(Вершинный шейдер): вершинный шейдер — это программа, используемая для описания характеристик вершин (таких как положение, цвет и т. Д.).
  • Фрагментный шейдер(Фрагментный шейдер): программа, выполняющая фрагментную обработку, например освещение. Фрагмент — это термин WebGL, вы можете понимать его как пиксели.

vec4(x, y, z, w); Однородные координаты (x, y, z, w) эквивалентны трехмерным координатам (x / w, y / w, z / w) .Существование однородных координат позволяет описывать преобразование вершин путем умножения матриц. В процессе расчета графической системы однородные координаты обычно используются для представления трехмерных координат вершин.

gl.drawArrays()Может использоваться для рисования различной графики

1.3 Система координат WebGL

    

« gl_Position = vec4 (0.5, 0.5, 0.0, 1.0); n ‘+ // Устанавливаем координаты

« gl_FragColor = vec4 (0.0, 1.0, 0.0, 1.0); n ‘+ // Устанавливаем цвет

Примечание: WebGL не требует обмена цветными буферами.

1.4 Нарисуйте пример

Передайте информацию о положении из программы JavaScript в вершинный шейдер. Это можно сделать двумя способами: переменные атрибутов и универсальные переменные. Переменная атрибута передает данные, относящиеся к вершинам, в то время как переменная uniform передает данные, одинаковые для всех вершин (или не имеющие ничего общего с вершинами).

// Программа вершинного шейдера
var VSHADER_SOURCE = 
   'attribute vec4 a_Position;n' +
   'void main() {n' + 
       'gl_Position = a_Position;  n' + // Устанавливаем координаты
       'gl_PointSize = 10.0;  n' + // Установить размер
   '}n';
 // Программа фрагментного шейдера
var FSHADER_SOURCE = 
   'void main() {n' +
       'gl_FragColor = vec4 (0.0, 1.0, 0.0, 1.0);  n' + // Устанавливаем цвет
   '}n';
   

function main() {
	var canvas = document.getElementById('webgl');
	var gl = getWebGLContext(canvas);
	if (!gl) {
		console.log("Failed to get the rendering context for WebGL");
		return;
	}
	 // Инициализируем шейдер
	if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
		console.log('Failed to initialize shaders.');
		return ;
	}
	 // Получить место хранения переменной атрибута
	var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
	if (a_Position < 0) {
		console.log("failed to get the storage location of a_Position");
		return;
	}
	gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);
	//RGBA
	gl.clearColor(0.0, 0.0, 0.0, 1.0);
	 // Пусто
	gl.clear(gl.COLOR_BUFFER_BIT);
	 // Рисуем точки
	 gl.drawArrays (gl.POINTS, 0, 1); // Положение и размер точки
}

// Программа вершинного шейдера
var VSHADER_SOURCE = 
   'attribute vec4 a_Position;n' +
   'void main() {n' + 
       'gl_Position = a_Position;  n' + // Устанавливаем координаты
       'gl_PointSize = 10.0;  n' + // Установить размер
   '}n';
 // Программа фрагментного шейдера
var FSHADER_SOURCE = 
   'precision mediump float;n' +
       'uniform vec4 u_FragColor;  n' + // равномерная переменная
   'void main() {n' +
       'gl_FragColor = u_FragColor;  n' + // Устанавливаем цвет
   '}n';
  
function main() {
	var canvas = document.getElementById('webgl');
	var gl = getWebGLContext(canvas);
	if (!gl) {
		console.log("Failed to get the rendering context for WebGL");
		return;
	}
	 // Инициализируем шейдер
	if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
		console.log('Failed to initialize shaders.');
		return ;
	}
	 // Получить место хранения переменной атрибута
	var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
	if (a_Position < 0) {
		console.log("failed to get the storage location of a_Position");
		return;
	}
	var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
	if (u_FragColor < 0) {
		console.log("failed to get the storage location of u_FragColor");
		return;
	}
	 // Регистрируем мышь
	canvas.onmousedown = function(ev) { click(ev, gl, canvas, a_Position, u_FragColor)};
	gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);
	//RGBA
	gl.clearColor(0.0, 0.0, 0.0, 1.0);
	 // Пусто
	gl.clear(gl.COLOR_BUFFER_BIT);
	 // Рисуем точки
	 //gl.drawArrays(gl.POINTS, 0, 1); // положение и размер точки
}

var g_points = [];   //
var g_colors = [];
function click(ev, gl, canvas, a_Position, u_FragColor) {
	var x = ev.clientX;
	var y = ev.clientY;
	var rect = ev.target.getBoundingClientRect();
	x = ((x-rect.left) - canvas.width/2)/(canvas.width/2);
	y = (canvas.height/2 - (y-rect.top))/(canvas.height/2);
	 // Сохраняем координаты в массиве g_points
	g_points.push([x, y]);
	 // Сохраняем цвет точки в массиве g_colors
	 if (x> = 0.0 && y> = 0.0) {// Первый квадрант
		 g_colors.push ([1.0, 0.0, 0.0, 1.0]); // Красный
	 } else if (x <0.0 && y <0.0) {// третий квадрант
		 g_colors.push ([0.0, 1.0, 0.0, 1.0]); // зеленый
	} else {
		 g_colors.push ([1.0, 1.0, 1.0, 1.0]); // белый
	}
	 // Пусто
	gl.clear(gl.COLOR_BUFFER_BIT);
	var len = g_points.length;
	for (var i=0; i<len; i++) {
		var xy = g_points[i];
		var rgba = g_colors[i];
		 // Переносим позицию точки в переменную a_Position
		gl.vertexAttrib3f(a_Position, xy[0], xy[1], 0.0);
		 // Переносим цвет точки в переменную u_FragColor
		gl.uniform4f(u_FragColor, rgba[0], rgba[1], rgba[2], rgba[3]);
		 // Рисуем точки
		gl.drawArrays(gl.POINTS, 0, 1);
	}
}

WebGL Programming Guide. Matsuda & Lea

This web site acts as the primary location for the example code in the book as well as a place for us to provide updates and new materials as we get feedback.

This book covers the WebGL 1.0 API along with all related JavaScript functions. You will learn how HTML, JavaScript, and WebGL are related, how to set up and run WebGL applications, and how to incorporate sophisticated 3D program “shaders” under the control of JavaScript. The book details how to write vertex and fragment shaders, how to implement advanced rendering techniques such as per-pixel lighting and shadowing, and basic interaction techniques such as selecting 3D objects. Each chapter develops a number of working, fully functional WebGL applications and explains key WebGL features through these examples. After finishing the book, you will be ready to write WebGL applications that fully harness the programmable power of web browsers and the underlying graphics hardware.

Book examples by chapter

Full text example chapter: Chapter 3

    • Chapter 1: Overview of WebGL

    • Chapter 2: Your First Step with WebGL

    • Chapter 3: Drawing and Transforming Triangles

    • Chapter 4: More transformations and Basic Animation

    • Chapter 5: Using Colors and Texture Images

    • Chapter 6: The OpenGL ES Shading Language (GLSL ES)

    • Chapter 7: Toward the 3D World

    • Chapter 8: Lighting Objects

    • Chapter 9: Hierarchical Objects

    • Chapter 10: Advanced Techniques

    • Appendices: A, B, C, D, E, F, G, H

    • Extras: extra examples

    • Download all examples

Some useful links

Errata an updated list of mistakes

WebGL

WebGL learning notes.

Useful Links

  • WebGL Home
  • WebGL Specification 1.0
  • WebGL Specification(Latest Revisions)
  • WebGL API Reference Card
  • MDN WebGL API
  • GLSL_ES_Specification_1.00.pdf
  • OpenGL ES 2.0 Specification
  • OpenGL® ES 2.0 Reference Pages

01_canvas_api

Canvas drawing api

  • fillRect

02_simple_webgl

First webgl demo, call gl apis clearColor and clear

var canvas = document.getElementById('webgl')
var gl = getWebGLContext(canvas)
gl.clearColor(1.0, 0.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)

03_draw_dot_fixed

Frist demo using shaders.

var vs = 
'void main() {n' +
'  gl_Position = vec4(0.5, 0.0, 0.0, 1.0);n' +
'  gl_PointSize = 10.0; n' +
'}n'

var fs = 
'void main() {n' +
'  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);n' +
'}n'

gl.drawArrays(gl.POINTS, 0, 1)

04_draw_dot_attribute

Draw dots by mouse clicks using attribute and uniform in GLSL.

Mouse listener.

key points:

  • gl.getAttribLocation

  • gl.getUniformLocation

  • gl.vertexAttrib[1234][ifv]

  • gl.uniform[1234][ifv]

  • canvas.onmousedown

05_multipoints

Draw multiple points using gl buffer.

    // 1. create buffer
    var buffer = gl.createBuffer();

    var vertices = new Float32Array([
        0.0, 0.5, -0.5, -0.5, 0.5, -0.5
    ]);

    var n = 3;

    // 2. bind buffer
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

    // 3. buffer data
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

    // 4. vertex attribute
    var a_pos = gl.getAttribLocation(gl.program, 'a_pos');
    gl.vertexAttribPointer(a_pos, 2, gl.FLOAT, false, 0, 0);

    // 5. enable vertex
    gl.enableVertexAttribArray(a_pos);

    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);

    // draw from buffer[0] to buffer[n-1], count == n.
    gl.drawArrays(gl.POINTS, 0, n);

key points:

  • gl.createBuffer

  • gl.bindBuffer

  • gl.bufferData

  • gl.vertexAttribPointer

  • gl.enableVertexAttribArray

  • gl.drawArrays(mode, from, count)

06_use_matrix

Rotate, Translate, Scale triangle use matrix.

rotate

 // rotate angle 
    var angle = 66.0;
    var radian = Math.PI * angle / 180.0;
    var sinb = Math.sin(radian);
    var cosb = Math.cos(radian);

    var uRotMat = new Float32Array([
            cosb, sinb, 0.0, 0.0,
            -sinb, cosb, 0.0, 0.0,
            0.0, 0.0, 1.0, 0.0,
            0.0, 0.0, 0.0, 1.0, 
    ]);

    var u_rot_mat = gl.getUniformLocation(gl.program, "u_rot_mat");
    gl.uniformMatrix4fv(u_rot_mat, false, uRotMat);

translate

 var tx = 0.5;
    var ty = 0.5;
    var tz = 0.0;

    var uTransMat = new Float32Array([
            1.0, 0.0, 0.0, 0.0,
            0.0, 1.0, 0.0, 0.0,
            0.0, 0.0, 1.0, 0.0,
            tx, ty, tz, 1.0, 
    ]);

    var u_trans_mat = gl.getUniformLocation(gl.program, "u_trans_mat");
    gl.uniformMatrix4fv(u_trans_mat, false, uTransMat);

scale

 var sx = 1.5;
    var sy = 0.5;
    var sz = 1.0;

    var uScaleMat = new Float32Array([
            sx, 0.0, 0.0, 0.0,
            0.0, sy, 0.0, 0.0,
            0.0, 0.0, sz, 0.0,
            0.0, 0.0, 0.0, 1.0,
    ]);

    var u_scale_mat = gl.getUniformLocation(gl.program, "u_scale_mat");
    gl.uniformMatrix4fv(u_scale_mat, false, uScaleMat);

07_rotating_triangle

Use matrix lib functions, and requestAnimationFrame for animation.

var currentAngle = 0.0;

    var uModelMat = new Matrix4();

    var u_model_mat = gl.getUniformLocation(gl.program, "u_model_mat");

    gl.clearColor(0.0, 0.0, 0.0, 1.0);

    var tick = function() {
        currentAngle = animate(currentAngle);
        draw(gl, n, currentAngle, uModelMat, u_model_mat);
        requestAnimationFrame(tick);
    };

    tick();

08_texture_mapping

Use multiple gl buffers or single buffer with interleaved data.

key points:

  1. stride and offset of vertexAttribPointer
var elemSize = vertices.BYTES_PER_ELEMENT;
// 4. vertex attribute
var a_pos = gl.getAttribLocation(gl.program, 'a_pos');
gl.vertexAttribPointer(a_pos, 2, gl.FLOAT, false, 3 * elemSize, 0);

var a_point_size = gl.getAttribLocation(gl.program, "a_point_size");
gl.vertexAttribPointer(a_point_size, 1, gl.FLOAT, false, 3 * elemSize, 2 * elemSize);
  1. varying variable: pass values from vertex shader to fragment shader
var vssrc = 
    'attribute vec4 a_pos;n' +
    'attribute vec4 a_color;n' +
    'varying vec4 v_color;n' +
    'void main() {n' +
    '  gl_Position = a_pos;n' +
    '  gl_PointSize = 10.0; n' +
    '  v_color = a_color; n' +
    '}n';

var fssrc = 
    '#ifdef GL_ESn' +
    'precision mediump float;n' +
    '#endifn'+
    'varying vec4 v_color;n' +
    'void main() {n' +
    '  gl_FragColor = v_color;n' +
    '}n';
  1. Texture coordinate (u,v) (or (s,t)), texture2D(u_sampler, v_tex_coord).
var vssrc = 
    'attribute vec4 a_pos;n' +
    'attribute vec2 a_tex_coord;n' +
    'varying vec2 v_tex_coord;n' +
    'void main() {n' +
    '  gl_Position = a_pos;n' +
    '  v_tex_coord = a_tex_coord; n' +
    '}n';

var fssrc = 
    '#ifdef GL_ESn' +
    'precision mediump float;n' +
    '#endifn' +
    'uniform sampler2D u_sampler;n' +
    'varying vec2 v_tex_coord;n' +
    'void main() {n' +
    '  gl_FragColor = texture2D(u_sampler, v_tex_coord);n' +
    '}n';

function loadTexture(gl, n, texture, u_sampler, image) {
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);

    gl.activeTexture(gl.TEXTURE0);

    gl.bindTexture(gl.TEXTURE_2D, texture);

    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);

    gl.uniform1i(u_sampler, 0);

    gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
}
  1. multiple textures.
var fssrc = 
    '#ifdef GL_ESn' +
    'precision mediump float;n' +
    '#endifn' +
    'uniform sampler2D u_sampler0;n' +
    'uniform sampler2D u_sampler1;n' +
    'varying vec2 v_tex_coord;n' +
    'void main() {n' +
    '  vec4 color0 = texture2D(u_sampler0, v_tex_coord);n' +
    '  vec4 color1 = texture2D(u_sampler1, v_tex_coord);n' +
    '  gl_FragColor = color0 * color1;n' +
    '}n';

09_3d_projection

key points:

  • eye point (eyeX, eyeY, eyeZ), look-at point (atX, atY, atZ), up direction (upX, upY, upZ).

    change eye point using document.onkeydown

  • orthographic projection matrix or perspective projection matrix

    canonical view volume.

    setOrtho(left, right, bottom, top, near, far)

    setPerspective(fov, aspect, near, far)

  • model, view, projection matrix (mvp)

    move the model or move the eye point? the same effect.

  • DEPTH_TEST & POLYGON_OFFSET_FILL (solve z fighting)

  • gl.drawElements, gl.ELEMENT_ARRAY_BUFFER demo, drawing a cube.

10_light

demo codes cover: directional light, point light, ambient light(no spot light, no diminishing light considered).

key points:

  • directional light:

    light direction, and light color normal vector

var vssrc = 
    'attribute vec4 a_pos;n' +
    'attribute vec4 a_color;n' +
    'uniform mat4 u_mvpMat;n' +
    'attribute vec3 a_normal;n' +
    'uniform vec3 u_lightColor;n' +
    'uniform vec3 u_lightDirection;n' +
    'varying vec4 v_color;n' +
    'void main() {n' +
    '  gl_Position = u_mvpMat * a_pos;n' +
    '  vec3 normal = normalize(a_normal);n' +
    '  float nDotL = max(dot(normal, u_lightDirection), 0.0);n' +
    '  vec3 diffuse = u_lightColor * vec3(a_color) * nDotL;n' +
    '  v_color = vec4(diffuse, a_color.a);n' +
    '}n';
var vssrc = 
    'attribute vec4 a_pos;n' +
    'attribute vec4 a_color;n' +
    'uniform mat4 u_mvpMat;n' +
    'attribute vec3 a_normal;n' +
    'uniform vec3 u_lightColor;n' +
    'uniform vec3 u_ambient;n' +
    'uniform vec3 u_lightDirection;n' +
    'varying vec4 v_color;n' +
    'void main() {n' +
    '  gl_Position = u_mvpMat * a_pos;n' +
    '  vec3 normal = normalize(a_normal);n' +
    '  float nDotL = max(dot(normal, u_lightDirection), 0.0);n' +
    '  vec3 diffuse = u_lightColor * vec3(a_color) * nDotL;n' +
    '  vec3 ambientLight = u_ambient * a_color.rgb;n' +
    '  v_color = vec4(diffuse + ambientLight, a_color.a);n' +
    '}n';
**transformed, compute the real-time normal vector by multiply inverse of and compose of model matrix. **
    // normal mat, (inverse of & transpose of modelMat)
    var normalMat = new Matrix4();
    normalMat.setInverseOf(modelMat);
    normalMat.transpose();
    var u_normalMat = gl.getUniformLocation(gl.program, "u_normalMat");
    gl.uniformMatrix4fv(u_normalMat, false, normalMat.elements);

    // ...

    // in vertex shader.
    vec3 normal = normalize(vec3(u_normalMat * a_normal));
    // a_normal is the original normal before the model's transform changed.
  • point light

    the same computation process as directional light except that point light come from all directions, so use (light position — vertex position) to decide the light direction of each vertex.

var vssrc = 
    'attribute vec4 a_pos;n' +
    'attribute vec4 a_color;n' +
    'attribute vec4 a_normal;n' +
    'uniform mat4 u_mvpMat;n' +
    'uniform mat4 u_modelMat;n' +
    'uniform mat4 u_normalMat;n' +
    'uniform vec3 u_lightPos;n' +
    'uniform vec3 u_lightColor;n' +
    'uniform vec3 u_ambient;n' +
    'varying vec4 v_color;n' +
    'void main() {n' +
    '  gl_Position = u_mvpMat * a_pos;n' +
    '  vec3 normal = normalize(vec3(u_normalMat * a_normal));n' +
    '  vec4 vertexPos = u_modelMat * a_pos;n' +
    '  vec3 lightDirection = normalize(u_lightPos - vec3(vertexPos));n' +
    '  float nDotL = max(dot(normal, lightDirection), 0.0);n' +
    '  vec3 diffuse = u_lightColor * vec3(a_color) * nDotL;n' +
    '  vec3 ambientLight = u_ambient * a_color.rgb;n' +
    '  v_color = vec4(diffuse + ambientLight, a_color.a);n' +
    '}n';
compute light in vertex shader (phong in vertex) -- Gouraud 
compute light in fragment shader(per-fragment lighting)  -- phong
var fssrc = 
    '#ifdef GL_ESn' +
    'precision mediump float;n' +
    '#endifn' +
    'uniform vec3 u_lightPos;n' +
    'uniform vec3 u_lightColor;n' +
    'uniform vec3 u_ambient;n' +
    'varying vec4 v_color;n' +
    'varying vec3 v_normal;n' +
    'varying vec3 v_vertexPos;n' +
    'void main() {n' +
    // v_normal may be not normalized after interpolate.
    '  vec3 normal = normalize(v_normal);n' +
    '  vec3 lightDirection = normalize(u_lightPos - v_vertexPos);n' +
    '  float nDotL = max(dot(normal, lightDirection), 0.0);n' +
    '  vec3 diffuse = u_lightColor * vec3(v_color) * nDotL;n' +
    '  vec3 ambientLight = u_ambient * v_color.rgb;n' +
    '  gl_FragColor = vec4(diffuse + ambientLight, v_color.a);n' +
    '}n';
var fssrc = 
    '#ifdef GL_ESn' +
    'precision mediump float;n' +
    '#endifn' +
    'uniform vec3 u_lightPos;n' +
    'uniform float u_specularStrength;n' +
    'uniform vec3 u_lightColor;n' +
    'uniform vec3 u_ambient;n' +
    'uniform vec3 u_eyePos;n' +
    'varying vec4 v_color;n' +
    'varying vec3 v_normal;n' +
    'varying vec3 v_vertexPos;n' +
    'void main() {n' +
    '  vec3 normal = normalize(v_normal);n' +
    '  vec3 lightDirection = normalize(u_lightPos - v_vertexPos);n' +
    '  float nDotL = max(dot(normal, lightDirection), 0.0);n' +
    '  vec3 diffuse = u_lightColor * vec3(v_color) * nDotL;n' +
    '  vec3 ambientLight = u_ambient * v_color.rgb;n' +
    '  vec3 viewDirection = normalize(u_eyePos - v_vertexPos);n' +


    // here
    '  vec3 reflectDirection = reflect(-lightDirection, v_normal);n' +
    '  float shininess = 64.0;n' +
    '  float spe = pow(max(dot(viewDirection, reflectDirection), 0.0), shininess);n' +
    '  vec3 specular = (u_specularStrength * spe * u_lightColor) * v_color.rgb;n' +


    '  gl_FragColor = vec4(diffuse + ambientLight + specular, v_color.a);n' +
    '}n';

11_experiment

rotate the cube with mouse and animation.

12_demos_from_book

demos from book: WebGL Programming Guide

Понравилась статья? Поделить с друзьями:
  • Руководство по ремонту киа соренто прайм скачать
  • Как выгрузить кредитную историю через госуслуги пошаговая инструкция
  • Дезсредства для рук инструкция по применению
  • Акиба протравитель семян инструкция по применению
  • Выкройка чехла на компьютерное кресло своими руками пошаговая инструкция