Извините, ничего не найдено.

Не расстраивайся! Лучше выпей чайку!
Регистрация
Справка
Календарь

Вернуться   forum.boolean.name > Веб-программирование > JavaScript / HTML

JavaScript / HTML Создание динамической разметки страниц

Ответ
 
Опции темы
Старый 15.11.2011, 21:39   #1
ABTOMAT
Ференька
 
Аватар для ABTOMAT
 
Регистрация: 26.01.2007
Адрес: улица Пушкина дом Колотушкина
Сообщений: 10,741
Написано 5,461 полезных сообщений
(для 15,675 пользователей)
Learning WebGL по-русски. Урок 1, часть 1.

Ссылка на оригинал.

От толмача:
Итак, я нашёл в сети превосходнейший цикл статей о WebGL. Сие называется Learning WebGL и является блогом, в котором автор, сам, по мере изучения WebGL, выкладывал свои адаптации к реалиям WebGL туториалов об OpenGL небезызвестного тов. NeHe.

И вот я решил таки вольно перевести на Вел.Мог.Рус.Яз. Усовершенствовать свой английский да и вообще. Может, кому-то будет полезно

По некоторым причинам я решил переводить сразу первый урок, а есть ещё нулевой, в котором описано, как найти браузер с поддержкой WebGL. Это было 2 года назад, и сегодня уже такой проблемы нет: ставим последнюю версию FireFox или Google Chrome. Я использовал Google Chrome 15.0.874.106 m — актуальную на сегодня версию. Ничего включать не надо, само всё заработало. Только вот Опера всё никак не родит (хотя и обещает), ну, O% market share не просто так. Думаю, двух вариантов достаточно.

Итак, представляю вам мой вольный перевод...


Итак, представляю вам мой первый WebGL-туториал! Этот урок основан на OpenGL-туториале №2 NeHe, которые являются популярным способом изучения 3D-графики для разработки игр. Он покажет вам, как нарисовать треугольник и квадрат на Web-странице. Это, может, и не особо-то впечатляет, но это отличное введение в основы WebGL. Если вы поймёте, как оно работает, дальше будет несложно...

Вот как выглядит результат урока, запущенный в браузере с поддержкой WebGL:


Здесь можно посмотреть "живую" демку на WebGL, если ваш браузер его поддерживает.

Ниже описано, как же оно работает.

Небольшое предупреждение: эти уроки нацелены на людей с заметным опытом и знаниями в программировании, но не шибко знакомых с 3D-графикой. Цель - "поднять вас на ноги" с хорошим пониманием того, что происходит в коде, чтобы вы смогли делать ваши собственные 3D Web-страницы как можно скорее. Я пишу это одновременно с тем, как я и сам учусь WebGL'ю, так что здесь могут найтись (и, поди, найдутся) неточности. Используйте сие на свой страх и риск! Впрочем, я устраняю баги и исправляю ошибки как только про них узнаю, так что если видите, что что-то не так, то дайте мне знать в комментариях.

Есть два способа посмотреть код этого примера. Просто посмотрите исходный код страницы с "живой" демкой, либо, если вы знакомы с GitHub'ом, вы можете клонировать его (и будущие уроки) из этого репозитория. Так или иначе, как только у вас будет код, откройте его в вашем любимом IDE и гляньте на него. Сложновато на первый взгляд, даже если вы немного знакомы со, скажем, OpenGL. Прямо вначале мы описываем парочку шейдеров, которые вообще считаются относительно "продвинутой" темой... Но не падайте духом! Это намного проще, чем кажется!

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

Вы увидите вот такую HTML-разметку:

<body onload="webGLStart();">
  <
a href="http://learningwebgl.com/blog/?p=28">Back to Lesson 1</a><br />

  <
canvas id="lesson01-canvas" style="border: none;" width="500" height="500"></canvas>

  <
br/>
  <
a href="http://learningwebgl.com/blog/?p=28">Back to Lesson 1</a><br />
</
body
Вообще, писать обработку событий прямо в атрибуты html-элемента, равно как и не выносить javascript-код/css в отдельные файлы - в наши дни дурной тон, как я считаю. Но, думаю, автор не стал городить огород чтобы не усложнять пример почём зря, а я оставил как есть.
- прим. толм.


Это и есть всё тело страницы — всё остальное находится в JavaScript'е (если вы взяли код прямо со страницы, используя "View source", вы, возможно, увидите некоторые лишние скрипты, необходимые для аналитики на моём сайте. Просто игнорируйте их.). Очевидно, что мы можем разместить и больше обычной HTML-разметки в <body> и вставить нашу WebGL-картинку в обычную web-страницу, но в этом простом примере у нас только ссылки обратно на этот пост и тег <canvas>, в котором, собственно, 3D-графики и живёт. Канвасы - новинка HTML5 — это то, как он поддерживает новые типы рисуемых Джаваскриптом элементов на web-страницах, как 2D, так и (через WebGL) 3D. Мы ничего не указываем кроме простых параметров канваса в этом теге, вместо этого мы разместим весь код настройки WebGL в JavaScript-функцию webGLStart, которая, как вы видите, вызывается, как только страница загрузится. Давайте пролистаем код наверх и глянем на неё:

function webGLStart() {
    var 
canvas document.getElementById("lesson01-canvas");
    
initGL(canvas);
    
initShaders();
    
initBuffers();

    
gl.clearColor(0.00.00.01.0);
    
gl.enable(gl.DEPTH_TEST);

    
drawScene();
  } 
Она вызывает функции инициализации WebGL, и шейдеры, которые я упомянул ранее, передаются в канвас, на котором мы хотим рисовать наше 3D, а затем она инициализирует парочку буферов при помощи initBuffers. Буферы — это такие штуки, которые хранят информацию о треугольнике и о квадрате, которые мы будем отрисовывать — о них мы скоро поговорим. Далее, она делает кое-какие начальные WebGL-настройки, говоря, что мы очищаем канвас, он должен стать чёрным, а также что нам нужен тест глубины (это когда то, что нарисовано за чем-то, будет скрыто тем, что находится перед ним). Эти шаги выполнены в виде вызова методов объекта gl — позже мы увидим, как его инициализируют. Наконец, она вызывает функцию drawScene. Она (как вы поняли из названия) рисует треугольники и квадрат, используя буферы.

Позже мы вернёмся к initGL и initShaders, ибо они важны для понимания того, как работает эта веб-страница, но сначала давайте посмотрим на initBuffers и drawScene.

Вначале initBuffers, шаг за шагом:

var triangleVertexPositionBuffer;
  var 
squareVertexPositionBuffer
Мы описываем две глобальные переменные чтобы хранить в них буфер. (В любой реальной WebGL-странице вы, конечно, не будете делать глобальную переменную для каждого объекта в сцене, но мы так сделали чтобы было попроще, ведь мы только начинаем)

Далее:

function initBuffers() {
    
triangleVertexPositionBuffer gl.createBuffer(); 
Мы создаём буфер для позиций вершин. Вершины — это точки в трёхмерном пространстве, которые описывает фигуры, которые мы рисуем. Для нашего треугольника у нас их будет три (которые мы через минуту сделаем). Этот буфер — в-общем-то некоторая часть видеопамяти. Мы азмещаем там положения вершин в самом начале при инициализации, а потом, когда мы уже хотим отрисовать сцену, по существу просто говорим WebGl'ю: "А ну-ка нарисуй те штуки, про которые я тебе чуть раньше рассказывал". Таким образом мы делаем наш код действительно эффективным, особенно, когда мы начнём анимировать сцену и захотим рисовать наши объекты десятки раз в секунду, чтобы они двигались. Конечно, когда у нас всего три позиции вершин, как в данном случае, не так уж и дорого сунуть их в видеопамять, но, когда вы имеете дело с большими моделями с десятками тысяч вершин, это станет значительным преимуществом. Далее:

gl.bindBuffer(gl.ARRAY_BUFFERtriangleVertexPositionBuffer); 
Эта строчка говорит WebGL, что любые последующие операции, которые работают с буферами, должны будут делать это с тем буфером, который мы указали. Всегда существует понятие "текущего буфера", и функции работают именно с ним, только если вы не укажете другой. Вообще, я думаю, за этим стоят какие-то важные причины, связанные с производительностью...

var vertices = [
         
0.0,  1.0,  0.0,
        -
1.0, -1.0,  0.0,
         
1.0, -1.0,  0.0
    
]; 
Далее, мы описываем наши позиции вершин как массив JavaScript. Как вы можете заметить, они являются точками равностороннего треугольника с центром в начале координат.

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); 
Теперь мы создаём объект Float32Array, основанные на нашем массиве и говорим WebGL использовать его, чтобы заполнить текущий буфер, который, конечно, - наш triangleVertexPositionBuffer. Мы поговорим побольше о Float32Array'ях в будущих уроках, а пока что всё, что вам нужно знать - это то, что они есть способ превратить Жабаскриптовый массив в кое-что, что мы можем передать в WebGL для заполнения его буферов.

triangleVertexPositionBuffer.itemSize 3;
    
triangleVertexPositionBuffer.numItems 3
Последнее, что мы делаем с буфером, это выставление ему двух новых свойств. Это не часть WebGL, но это будет нам в будущем полезно. Одна классная вещь (а кто-то скажет, наоборот, отстойная), касающаяся JavaScript'а, состоит в том, что объект не обязан иметь список свойств, которые вы потом можете использовать. Так что, хоть объект нашего буфера и не имел свойств itemSize и numItems, то теперь — имеет. Просто назначили - и они появились. Мы используем их, чтобы сказать, что этот буфер из девяти элементов представляет три отдельных позиции вершин (numItems), каждая из которых сделана из трёх чисел (itemSize).

Теперь мы разобрались с буфером треугольника, так что сделаем то же самое с квадратом, по аналогии:

squareVertexPositionBuffer gl.createBuffer();
    
gl.bindBuffer(gl.ARRAY_BUFFERsquareVertexPositionBuffer);
    
vertices = [
         
1.0,  1.0,  0.0,
        -
1.0,  1.0,  0.0,
         
1.0, -1.0,  0.0,
        -
1.0, -1.0,  0.0
    
];
    
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    
squareVertexPositionBuffer.itemSize 3;
    
squareVertexPositionBuffer.numItems 4;
  } 
Тут всё должно быть понятно: квадрат имеет 4 позиции вершины, а не 3, как треугольник, так что массив вышел больше и numItems у него иной.

Ладно, это было то, что необходимо, чтобы разместить позиции вершин двух объектов в видеопамять. Теперь давайте поглядим на drawScene, в которой мы используем эти буферы чтобы непосредственно отрисовать изображение. Шаг за шагом:

function drawScene() {
    
gl.viewport(00gl.viewportWidthgl.viewportHeight); 
Первый шаг - сказать WebGL'у пару слов о размере канваса, используя функцию viewport. Позже я поясню, почему это важно (в более поздних уроках), а пока то, что вам необходимо знать - то, что эту функцию необходимо вызывать, указывая размер канваса до того, как вы начнёте рисовать. Далее мы очищаем канвас, готовясь там рисовать:
gl.clear(gl.COLOR_BUFFER_BIT gl.DEPTH_BUFFER_BIT); 
...а потом:
mat4.perspective(45gl.viewportWidth gl.viewportHeight0.1100.0pMatrix); 
Здесь мы устанавливаем перспективу, с которой мы хотим видеть сцены. По умолчанию WebGL будет рисовать то, что ближе такого же размера, как и то, что дальше (вид 3D-проекции, называемый ортогональной проекцией). Чтобы заставить дальние объекты быть меньше, чем ближние, нам нужно сообщить информацию о перспективе, которую мы используем. Для этой сцены мы говорим, что наш (вертикальный) угол обзора равен 45°, сообщаем отношение ширины к высоте для нашего канваса и указываем, что не хотим видеть то, что находится ближе 0.1 юнита и дальше 100.

Как видите, эта perspective использует функцию из модуля mat4 и включает в себя переменную с интересным именем pMatrix. Подробно об этом позже. Сейчас, надеюсь, ясно, как использовать её, не вникая в подробности.

Теперь, когда перспектива указана, мы можем продолжить:

mat4.identity(mvMatrix); 
Это первый шаг, чтобы "переместить" центр 3D-сцены. В OpenGL, когда вы рисуете сцену, вы говорите ей рисовать каждый объект в "текущей" позиции и с "текущим" поворотом — так что, например, вы говорите "двинься на 20 юнитов вперёд, повернись на 32 градуса и нарисуй робота", и это является составным набором инструкций типа "двинься туда, повернись маленько, отрисуй то". Это удобно, так как можно завернуть код "нарисуй робота" в одну функцию, а потом двигать и вертеть этого робота просто меняя его позиции и повороты перед вызовом этой функции.

Текущая позиция, как и текущий поворот, хранится в матрице. Как вы, возможно, узнали в школе, матрицы могут представлять параллельные переносы (двигают с места на место), повороты и другие геометрические преобразования. Я сейчас не буду это всё пояснять по понятным причинам. Вы можете использовать матрицу 4х4 (не 3х3), чтобы представить любое количество преобразований в трёхмерном пространстве. Начинаем мы с единичной матрицы - это матрица, представляющая преобразование, которое ничего не делает. Потом помножаем её на матрицу, которая представляет ваше первое преобразование, потом на ту, которая представляет второе преобразование и так далее. Комбинированная матрица представляет все ваши преобразования в одном. Матрица, которую мы используем, чтобы представить наше текущее состояние положения/поворота, называется модельно-видовой матрицей. Сейчас вы, возможно, уже догадались, что переменная mvMatrix содержит в себе нашу модельно-видовую матрицу, и функция mat4.identity делает её единичной матрицей, так что мы теперь готовы начать умножать её на матрицы параллельных переносов и поворотов. Иными словами, мы находимся в "начальной точке", от которой мы можем начинать рисовать наш трёхмерный мир.

Искушённые читатели заметили, что в начале рассказа о матрицах я сказал "В OpenGL", а не "В WebGL". Это потому что в WebGL это не встроено изначально. Вместо этого мы используем сторонюю библиотеку для работы с матрицами — glMatrix Брэндона Джонса — плюс парочку изящных WebGL-трюков, чтобы добиться того же эффекта. Подробней об этом "изяществе" - позже.

Хорошо, давайте продвинемся вперёд к коду, который рисует треугольник на левой стороне нашего канваса.

mat4.translate(mvMatrix, [-1.50.0, -7.0]); 
Переместившись в центр нашей 3D-сцены, сделав mvMatrix единичной матрицей, мы начинаем заниматься треугольником, двинувшись на 1.5 юнита влево (как видите, 1.5 указано с минусом, так что движение произошло именно влево по оси X), и на 7 юнитов от сцены (опять с минусом и по оси Z).

Как вы уже поняли, mat4.translate означает "Помножь данную матрицу на матрицу параллельного переноса, используя стедующие параметры")

Следующий шаг - непосредственно отрисовка!

gl.bindBuffer(gl.ARRAY_BUFFERtriangleVertexPositionBuffer);
    
gl.vertexAttribPointer(shaderProgram.vertexPositionAttributetriangleVertexPositionBuffer.itemSizegl.FLOATfalse00); 
Как вы помните, чтобы использовать один из наших буферов, мы вызываем gl.bindBuffer, чтобы указать текущий буфер, а затем вызываем код, который с тем буфером выполняет действия. Здесь мы выбираем наш triangleVertexPositionBuffer, затем говорим WebGl, что значения из него должны быть использованы как позиции вершин. Я поясню чуть больше о том, как оно работает, позднее. Сейчас, как видите, мы используем свойство itemSize нашего буфера, чтобы пояснить WebGL, что каждая запись в буфере у нас в 3 числа длиной.

Далее вот что:

setMatrixUniforms(); 
Эта строка сообщает WebGL, что нужно учесть нашу модельно-видовую матрицу (а также матрицу проекции, о которой намного позже). Это необходимо, потому что вся эта матричная фигня не встроена в WebGL. Вот как это выглядит: вы можете выполнять перемещения и повороты, меняя mvMatrix, но всё это — только внутри переменных JavaScript. setMatrixUniforms, функция, которую мы рассмотрели выше, "применяет" все эти преобразования, перенося их в видеокарту.

Когда это сделано, у WebGL есть массив чисел, и он знает, что его надо рассматривать, как позиции вершин, а ещё он знает о наших матрицах. Следующий шаг — сказать, что с ними делать:

gl.drawArrays(gl.TRIANGLES0triangleVertexPositionBuffer.numItems); 
Или, иными словами, "нарисуй массив вершин, который я тебе раньше дал, как треугольники, начиная с нулевой записи и заканчивая записью с номером numItems".

Как только это сделано, WebGL нарисует нам наш треугольник (думал, не доживу — прим. толм.)

Следующий шаг - нарисовать квадрат:

mat4.translate(mvMatrix, [3.00.00.0]); 
Мы начинаем, двигая нашу модельно-видовую матрицу на три юнита вправо. Помните, мы сейчас в 1.5 юнитах слева и в 7 юнитах позади центра? После выполнения этой строчки мы "окажемся" в 1.5 юнитах правее центра и всё так же в 7 юнитах позади него.

Далее:

gl.bindBuffer(gl.ARRAY_BUFFERsquareVertexPositionBuffer);
    
gl.vertexAttribPointer(shaderProgram.vertexPositionAttributesquareVertexPositionBuffer.itemSizegl.FLOATfalse00); 
Говорим WebGL'у использовать буферы квадрата, чтобы оттуда брать положения вершин.

setMatrixUniforms(); 
"Суём" все наши матричные преобразования в видеокарту.

И, наконец:

gl.drawArrays(gl.TRIANGLE_STRIP0squareVertexPositionBuffer.numItems); 
Рисуем вершины! Что такое, спросите вы, triangle strip ? Ну, это полоса треугольников
Ориг. юмор:
What, you may ask, is a triangle strip? Well, it’s a strip of triangles
— прим. толм.

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

Так или иначе, на этом мы кончили с функцией drawScene.


Коль скоро вы дочитали досюда, вы готовы экспериментировать! Скопируйте код в локальный файл, или возьмите его через GitHub прямо с "живой" версии. В последнем случае вам нужны index.html и glMatrix-0.9.5.min.js. Запустите это на локальной машине, чтобы убедиться, что оно работает, потом попробуйте поменять парочку позиций вершин. В частности, сейчас сцена плоская. Попробуйте менять значения координат по оси Z для квадрата на 2 или на -3, и посмотрите, как он становится больше или меньше, когда вы его перемещаете назад и вперёд. Или попробуйте менять не все вершины, а только часть из них и полюбуйтесь на искажения перспективы. Веселитесь на полную катушку и не обращайте на меня внимания. Я подожду.

От толмача:
На этом 1-й урок ещё не кончился. Далее автор более подробно рассматривает базовую работу с шейдерами. Эта часть урока почти такая же длинная, как и первая, и перевод её займёт время. Автор сообщает, что она опциональна и предназначена для совсем уж дотошных. Так что я решил для начала запостить первую часть, следующую ждите позднее.
__________________
Мои проекты:
Анальное Рабство
Зелёный Слоник
Дмитрий Маслов*
Различие**
Клюква**

* — в стадии разработки
** — в стадии проектирования
Для проектов в стадии проектирования приведены кодовые имена

(Offline)
 
Ответить с цитированием
Эти 18 пользователя(ей) сказали Спасибо ABTOMAT за это полезное сообщение:
baton4ik (15.11.2011), den (15.11.2011), Dream (02.01.2012), falcon (16.11.2011), Hurrit (04.12.2011), johnk (27.11.2011), LLI.T.A.L.K.E.R. (19.12.2011), mauNgerS (16.11.2011), moka (15.11.2011), Nex (16.11.2011), pax (16.11.2011), Randomize (15.11.2011), RBK (16.11.2011), Reizel (15.11.2011), Reks888 (15.11.2011), SBJoker (16.11.2011), St_AnGer (20.11.2011), VotapilD (16.11.2011)
Ответ


Опции темы

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.


Часовой пояс GMT +4, время: 17:06.


vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot
Style crйe par Allan - vBulletin-Ressources.com