<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://wiki2.linuxformat.ru/skins/common/feed.css?97"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>LXF111:Игрострой - История изменений</title>
		<link>http://wiki2.linuxformat.ru/index.php?title=LXF111:%D0%98%D0%B3%D1%80%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B9&amp;action=history</link>
		<description>История изменений этой страницы в вики</description>
		<language>ru</language>
		<generator>MediaWiki 1.11.1</generator>
		<lastBuildDate>Wed, 13 May 2026 22:39:02 GMT</lastBuildDate>
		<item>
			<title>Crazy Rebel в 11:05, 15 января 2010</title>
			<link>http://wiki2.linuxformat.ru/index.php?title=LXF111:%D0%98%D0%B3%D1%80%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B9&amp;diff=9509&amp;oldid=prev</link>
			<description>&lt;p&gt;&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;← Предыдущая&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Версия 11:05, 15 января 2010&lt;/td&gt;
			&lt;/tr&gt;
		&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Строка 1:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Строка 1:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;: '''GLSL''' Проникаем в тайны видеоускорителя с программами на шейдерах. [[Категория:Учебники]]&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;: '''GLSL''' Проникаем в тайны видеоускорителя с программами на шейдерах. [[Категория:Учебники]]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;background: #cfc; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;background: #cfc; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;{{Цикл/Ни строчки кода}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;==Текстуры на заказ==&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;==Текстуры на заказ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</description>
			<pubDate>Fri, 15 Jan 2010 11:05:47 GMT</pubDate>			<dc:creator>Crazy Rebel</dc:creator>			<comments>http://wiki2.linuxformat.ru/index.php/%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:LXF111:%D0%98%D0%B3%D1%80%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B9</comments>		</item>
		<item>
			<title>Crazy Rebel: викификация, оформление, иллюстрация</title>
			<link>http://wiki2.linuxformat.ru/index.php?title=LXF111:%D0%98%D0%B3%D1%80%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B9&amp;diff=8857&amp;oldid=prev</link>
			<description>&lt;p&gt;викификация, оформление, иллюстрация&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая статья&lt;/b&gt;&lt;/p&gt;&lt;div&gt;: '''GLSL''' Проникаем в тайны видеоускорителя с программами на шейдерах. [[Категория:Учебники]]&lt;br /&gt;
&lt;br /&gt;
==Текстуры на заказ==&lt;br /&gt;
&lt;br /&gt;
: '''ЧАСТЬ 2''' Оказывается, текстуры можно создавать не только в графическом редакторе! Андрей Прахов умеет делать это на лету, при помощи… правильно, программ-шейдеров.&lt;br /&gt;
&lt;br /&gt;
Когда-то, много лет назад, я увлекался программированием на ассемблере для микрокомпьютера ZX-Spectrum.&lt;br /&gt;
В наше время его технические характеристики могут вызвать разве что улыбку: ОЗУ 48-1024&lt;br /&gt;
КБ, процессор 3,4 МГц. Однако, несмотря на&lt;br /&gt;
крохотную память, малые тактовые частоты и полное отсутствие какого-либо ускорения графики, на нем делались потрясающие&lt;br /&gt;
игры и демо. Существовало повальное увлечение демо-сценой, когда программист старался вместить музыку, графику, спецэффекты&lt;br /&gt;
в ничтожное количество байт. Современные&lt;br /&gt;
игры пожирают гигабайты места на винчестере и многие мегабайты простой и видеопамяти. Откуда такие аппетиты? Наиболее ресурсоемкие части игры – это графика и музыка.&lt;br /&gt;
Особенно трудно приходится программистам&lt;br /&gt;
при расчете объема текстурной памяти, необходимого для определенного момента текущей сцены. Уменьшить объем используемой&lt;br /&gt;
видеопамяти можно, если заменить обычные&lt;br /&gt;
текстуры процедурными, то есть вычисляемыми в нужный момент времени. В этом случае ОЗУ расходуется только на генерирующую программу. Понятное дело, это требует немалых вычислительных мощностей, если не поручить ее выполнение GPU, а точнее – его шейдерным конвейерам.&lt;br /&gt;
&lt;br /&gt;
Специально для этого урока я подготовил модель дома с окружением, но вот основные текстуры для него мы заменим на процедурные&lt;br /&gt;
программы. Итак, нам предстоит разработать шейдеры для основания&lt;br /&gt;
дома, стен, окон и двери, т.е. создать имитации мрамора, дерева и&lt;br /&gt;
кирпича. Давайте прикинем, сколько же мы на этом сэкономим? Если&lt;br /&gt;
брать за стандарт текстуру формата Targa с разрешением 1024х1024,&lt;br /&gt;
то в сумме получится... ого, примерно шесть мегабайт! За них стоит&lt;br /&gt;
побороться.&lt;br /&gt;
&lt;br /&gt;
===Джек, который построил дом...===&lt;br /&gt;
&lt;br /&gt;
Вначале определимся с вершинным шейдером, который будет одинаковым для всех примеров. Возьмем за основу код вычисления диффузного освещения из предыдущего урока и немного переделаем его:&lt;br /&gt;
&lt;br /&gt;
 //GLSL vertex shader&lt;br /&gt;
 void main ()&lt;br /&gt;
 const vec4 inColor = vec4 (1.0, 0.0, 0.0, 1.0);&lt;br /&gt;
 uniform vec3 lightPos;&lt;br /&gt;
   varying vec4 outColor;&lt;br /&gt;
   {&lt;br /&gt;
             vec3 position = vec3 (gl_ModelViewMatrix * gl_Vertex);&lt;br /&gt;
             vec3 lightvec = normalize (vec3 (lightPos) - position);&lt;br /&gt;
             vec3 norm = normalize (gl_NormalMatrix * gl_Normal);&lt;br /&gt;
             outColor = inColor * (max (dot (norm, lightvec), 0.0));&lt;br /&gt;
             gl_Position = gl_ModelViewProectionMatrix * gl_Vertex;&lt;br /&gt;
             gl_Position = ftransform();&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
В приведенном выше примере мы передавали координаты источника света через переменную '''LightPos'''. На этом уроке будет удобнее брать их непосредственно из самого шейдера. Раз у нас всего один источник&lt;br /&gt;
света, то строка будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
 vec3 ll=gl_LightSource[0].position;&lt;br /&gt;
&lt;br /&gt;
Так как основные манипуляции будут происходить во фрагментном&lt;br /&gt;
шейдере, то логичнее было бы вычислять не полное освещение и цвет&lt;br /&gt;
текущей вершины, а всего лишь интенсивность светового потока. В&lt;br /&gt;
приведенном коде за это отвечает строка&lt;br /&gt;
&lt;br /&gt;
 outColor = inColor * (max (dot (norm, lightvec), 0.0));&lt;br /&gt;
&lt;br /&gt;
Здесь происходит умножение постоянного цвета примитива (переменная '''InColor''') на интенсивность освещения. Соответственно, удалив первый операнд, мы получим необходимый код. Заодно поменяем&lt;br /&gt;
название выходной varying-переменной на более подходящее имя&lt;br /&gt;
'''outLight'''. В дальнейшем останется только умножить результат работы&lt;br /&gt;
фрагментного шейдера на '''outLight''' для получения конечного результата. Кроме того, для работы нам понадобятся координаты рассчитываемой вершины. Введем для этого новую переменную '''MCposition'''.&lt;br /&gt;
Реализация последней задачи будет немного варьироваться для каждого конкретного шейдера, и мы обговорим ее отдельно.&lt;br /&gt;
&lt;br /&gt;
Со всеми приведенными изменениями у нас получился следующий код вершинного шейдера:&lt;br /&gt;
&lt;br /&gt;
 //GLSL vertex shader (optimization for break)&lt;br /&gt;
 void main ()&lt;br /&gt;
 varying float outLight;&lt;br /&gt;
 varying vec2 MCposition;&lt;br /&gt;
 {&lt;br /&gt;
             vec3 ll=gl_LightSource[0].position;&lt;br /&gt;
             vec3 position = vec3 (gl_ModelViewMatrix * gl_Vertex);&lt;br /&gt;
             vec3 lightvec = normalize (vec3 (ll) – position);&lt;br /&gt;
             vec3 norm = normalize (gl_NormalMatrix * gl_Normal);&lt;br /&gt;
             outLight = max (dot (norm, lightvec), 0.0);&lt;br /&gt;
             gl_Position = gl_ModelViewProectionMatrix * gl_Vertex;&lt;br /&gt;
             MCposition = gl_Vertex.xy;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Существуют два варианта создания процедурной текстуры: использование особого алгоритма для повторяющихся элементов рисунка&lt;br /&gt;
и работа с шумовыми функциями. Сначала мы разработаем шейдер&lt;br /&gt;
для имитации кирпичной стены. В этом случае наиболее естественным&lt;br /&gt;
выглядит первый вариант.&lt;br /&gt;
&lt;br /&gt;
Создаваемая нами программа должна учитывать следующие&lt;br /&gt;
особенности:&lt;br /&gt;
* Кирпичная кладка состоит из блоков одинакового размера, где каждый кирпич отделен друг от друга пространством другого цвета;&lt;br /&gt;
* Каждый следующий ряд сдвинут по отношению к предыдущему на половину длины кирпича.&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF111_97_1.jpg|300px|Рис. 1]]  Рис. 1. Выглядит не очень, но это же просто чертеж!|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
Давайте определимся с необходимыми переменными. Это uniform-переменные для хранения цветов самих кирпичей и промежутков между ними. Кроме того, нам еще понадобятся размеры брусков с учетом&lt;br /&gt;
имеющихся промежутков и процентное соотношение общих размеров&lt;br /&gt;
с реальными размерами кирпича (рис. 1). Итого получается четыре&lt;br /&gt;
переменные:&lt;br /&gt;
&lt;br /&gt;
   uniform vec3 BColor1, Bcolor2; // (1.0,0.4,0.0), (1.0,1.0,1.0)&lt;br /&gt;
   uniform vec2 Bsize, BP; // (0.30,0.15), (0.90,0.85)&lt;br /&gt;
&lt;br /&gt;
Чтобы определить, к какой точке объекта принадлежит текущий&lt;br /&gt;
фрагмент, нам необходимо передать в varying-переменной координаты&lt;br /&gt;
рассчитываемой вершины. Для этого в vertex-шейдере служит строка&lt;br /&gt;
&lt;br /&gt;
 MCposition = gl_Vertex.xy;&lt;br /&gt;
&lt;br /&gt;
Таким образом мы также удалим нежелательный эффект скольжения рисунка. Не забудьте разместить эту переменную в обоих&lt;br /&gt;
шейдерах!&lt;br /&gt;
&lt;br /&gt;
Теперь нам нужно определить, как рисовать текущий ряд кирпичиков: ведь каждый второй у нас идет со смещением. Однако вначале&lt;br /&gt;
необходимо вычислить номера ряда и блока в нем. Простым делением&lt;br /&gt;
координат вершины на размер кирпича мы получим ряд в переменной&lt;br /&gt;
'''Lpos.y''' и номер блока в '''Lpos.x''':&lt;br /&gt;
&lt;br /&gt;
 vec2 Lpos = MCposition / Bsize;&lt;br /&gt;
&lt;br /&gt;
А вот теперь самое интересное! Для определения смещения мы&lt;br /&gt;
воспользуемся оператором ветвления '''IF...ELSE'''. Замечу, что правила написания его абсолютно аналогичны таковым в ''C/C++''. Так как у нас кирпич должен смещаться на 0.5 от своей длины, то для расчета&lt;br /&gt;
мы возьмем номер ряда из переменной '''Lpos.y''', умножим его на 0.5 и&lt;br /&gt;
отбросим функцией '''fract''' целую часть результата. Полученный результат сравнивается с 0.5. Таким образом, через раз условие будет истинным, а ряд – смещаться на 0.5:&lt;br /&gt;
&lt;br /&gt;
 if (fract (Lpos.y * 0.5)&amp;gt;0.5) Lpos.x +=0.5;&lt;br /&gt;
&lt;br /&gt;
Для выбора цвета на текущем участке необходима функция, возвращающая 1 на кирпиче и 0 на промежутке. Их у нас будет две, ведь существуют и горизонтальные, и вертикальные компоненты. Результаты&lt;br /&gt;
обеих функций перемножаются для получения окончательного цвета.&lt;br /&gt;
&lt;br /&gt;
И снова на помощь нам приходит мощь языка ''GLSL'' в виде уже&lt;br /&gt;
готовой функции '''step (edge, x)'''. Вообще-то она служит для получения&lt;br /&gt;
импульсов прямоугольной формы. В нашем случае, функция будет&lt;br /&gt;
возвращать 0 при условии '''BP.x &amp;lt;= Lpos.x''' и единицу в случае несоответствия. Однако вначале следует вычислить координаты фрагмента по отношению к активному блоку:&lt;br /&gt;
&lt;br /&gt;
 Lpos = fract (Lpos);&lt;br /&gt;
 vec2 uses = step (Lpos, BP);&lt;br /&gt;
&lt;br /&gt;
Дело осталось за малым – надо вычислить окончательное значение&lt;br /&gt;
цвета и умножить его на переменную '''outLight''' для получения правильного освещения фрагмента. Воспользуемся встроенной функцией '''mix (vec4 x, vec4 y, float a)'''. Ее воплощение выражается формулой '''x*(1.0-a)+y*a'''. Так как результат операции '''uses.x * uses.y''' может принять либо единицу, либо ноль, то и функция будет возвращать одно-единственное&lt;br /&gt;
значение (при 1 – цвет кирпича, при 0 – цвет промежутка).&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF111_98_1.jpg|300px|Рис. 2]]Рис. 2. Почти как настоящий кирпич.|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
Конечный код фрагментного шейдера кирпичной стены (рис. 2) таков:&lt;br /&gt;
&lt;br /&gt;
 //GLSL fragment shader&lt;br /&gt;
 uniform vec3 BColor1, BColor2;&lt;br /&gt;
 uniform vec2 BSize, BP;&lt;br /&gt;
 varying vec2 MCposition;&lt;br /&gt;
 varying float outLight;&lt;br /&gt;
 void main() {&lt;br /&gt;
        vec2 Lpos = MCposition / BSize;&lt;br /&gt;
        if (fract (Lpos.y * 0.5)&amp;gt;0.5)&lt;br /&gt;
               Lpos.x +=0.5;&lt;br /&gt;
        Lpos = fract (Lpos);&lt;br /&gt;
        vec2 uses = step (Lpos, BP);&lt;br /&gt;
        vec3 color = mix (BColor2, BColor1, uses.x * uses.y) * outLight;&lt;br /&gt;
                gl_FragColor = vec4 (color, 1.0);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Noise-фактор===&lt;br /&gt;
&lt;br /&gt;
Зачастую при упоминании термина «шум» у непосвященных возникает ассоциация либо со звуком, либо с отсутствием сигнала на телеэкране (хаотичный «снег»). Однако в трехмерной графике использование «чистого» шума не имеет смысла. Ведь, как правило, текстура при наложении на объект размножается, и в случае «чистого» шума&lt;br /&gt;
добиться необходимого узора невозможно. Шумовые функции выдают непрерывную псевдослучайную последовательность, которая&lt;br /&gt;
зависит от начального значения. Благодаря этим особенностям появляется возможность имитации цветов природных и искусственных&lt;br /&gt;
материалов, неровностей поверхностей, «хаотичной» анимации.&lt;br /&gt;
&lt;br /&gt;
В случае с шейдерами предлагается три способа работы с шумом:&lt;br /&gt;
* Использование встроенных функций ''GLSL'';&lt;br /&gt;
* Текстурные карты;&lt;br /&gt;
* Функции, определяемые пользователями.&lt;br /&gt;
&lt;br /&gt;
Язык программирования шейдеров имеет встроенную процедуру для генерации шумов – '''noise()'''. Использование этого подхода имеет&lt;br /&gt;
ряд преимуществ по сравнению с остальными. Во-первых, не расходуется текстурная память на хранение карты шумов. Кроме того,&lt;br /&gt;
шейдеры, использующие '''noise()''', не зависят от конкретного приложения. Однако в наше время эта функция, как правило, не применяется из-за неполной аппаратной реализации. Гораздо проще и быстрее&lt;br /&gt;
один раз вычислить необходимую последовательность и сохранить ее&lt;br /&gt;
в текстурной карте. В каждой текстуре можно хранить до 4 различных&lt;br /&gt;
значений и получать их за одно обращение. Данные разделяются и&lt;br /&gt;
хранятся в каналах RGBA. Замечу, что могут использоваться не только&lt;br /&gt;
двумерные, но и трех-, четырехмерные карты шумов. В нашем случае,&lt;br /&gt;
мы будем работать с двумерной, уже заранее просчитанной картой.&lt;br /&gt;
Что же касается процедуры генерации, то ее легко написать самому&lt;br /&gt;
или воспользоваться всезнающим Интернетом. Здесь при создании&lt;br /&gt;
карты шумов использовалась стандартная функция Перлина.&lt;br /&gt;
&lt;br /&gt;
Попробуем для начала смоделировать узор, напоминающий мраморный камень. Исправьте вершинный шейдер, заменив в нем строку:&lt;br /&gt;
&lt;br /&gt;
 MCposition = gl_Vertex.xy;&lt;br /&gt;
&lt;br /&gt;
на&lt;br /&gt;
&lt;br /&gt;
 MCposition = vec3 (gl_Vertex);&lt;br /&gt;
&lt;br /&gt;
То есть, в отличие от первого шейдера, мы будем использовать&lt;br /&gt;
полноценные трехмерные координаты вершины. Не забудьте при этом&lt;br /&gt;
изменить описание переменной в начале программы. На этом модификация вершинного шейдера завершена.&lt;br /&gt;
&lt;br /&gt;
Для фрагментного шейдера нам понадобятся следующие переменные:&lt;br /&gt;
&lt;br /&gt;
 uniform sampler2D NoiseMap;&lt;br /&gt;
 uniform vec3 MColor1; //0.80,0.86,0.74&lt;br /&gt;
 uniform vec3 MColor2; //0.35,0.15,0.1&lt;br /&gt;
&lt;br /&gt;
где '''MColor1''' хранит основной цвет камня, а '''MColor2''' – цвет прожилок.&lt;br /&gt;
Кроме того, шейдеру необходимо указать на текстурную карту, которая будет использоваться для генерации шума. За это отвечает строка &lt;br /&gt;
&lt;br /&gt;
  uniform sampler2D NoiseMap.&lt;br /&gt;
&lt;br /&gt;
Решение задачи донельзя простое. Загружаем в вектор значения шума с шагом 1.5 для активного пикселя:&lt;br /&gt;
&lt;br /&gt;
 vec3 noisevec = texture2D (NoiseMap, MCposition *1.5);&lt;br /&gt;
&lt;br /&gt;
Вычисляем интенсивность узора путем сложения обоих векторов:&lt;br /&gt;
&lt;br /&gt;
 float intensity = (noisevec[0] - 0.5) +(noisevec[1] - 0.25);&lt;br /&gt;
&lt;br /&gt;
Для создания прожилок воспользуемся функцией '''sin (float x)''':&lt;br /&gt;
&lt;br /&gt;
 float ssin = sin(MCposition.y * 100.0 + intensity * 100.0)+0.4;&lt;br /&gt;
&lt;br /&gt;
Осталось вычислить конечный цвет способом, который мы уже&lt;br /&gt;
использовали для шейдера кирпичной стены. Но слишком уж это скучно! Давайте усложним задачу, разбив мрамор на некое подобие плиток. Чтобы не изобретать велосипед, воспользуемся готовым шейдером&lt;br /&gt;
кирпича с небольшими модификациями.&lt;br /&gt;
&lt;br /&gt;
Конечный результат работы программы должен отвечать следующим требованиям:&lt;br /&gt;
* Поверхность модели разбита на квадраты, напоминающие шахматную доску;&lt;br /&gt;
* Узор мрамора непрерывен и неразрывен для всей площади объекта.&lt;br /&gt;
&lt;br /&gt;
Исходя из поставленных условий, логичнее всего произвести разбивку на квадраты, а потом залить все узором мрамора. За рисунок&lt;br /&gt;
кирпичей у нас отвечали строки&lt;br /&gt;
&lt;br /&gt;
 if (fract (Lpos.y * 0.5)&amp;gt;0.5)&lt;br /&gt;
              Lpos.x +=0.5;&lt;br /&gt;
 Lpos = fract (Lpos);&lt;br /&gt;
 vec2 uses = step (Lpos, BP);&lt;br /&gt;
&lt;br /&gt;
Опытным путем несложно добиться, чтобы небольшая модификация кода привела к желаемому результату:&lt;br /&gt;
&lt;br /&gt;
 if (fract (Lpos.y )&amp;gt;0.5)&lt;br /&gt;
              Lpos.x +=0.5;&lt;br /&gt;
 Lpos = fract (Lpos);&lt;br /&gt;
 vec2 uses = step (Lpos, BP);&lt;br /&gt;
&lt;br /&gt;
Однако результат выбора цвета необходимо занести в отдельную переменную и обобщить только после вычисления кода «мрамора».&lt;br /&gt;
Конечный код фрагментного шейдера мраморных плиток выглядит следующим образом:&lt;br /&gt;
&lt;br /&gt;
 //GLSL fragment shader&lt;br /&gt;
 uniform sampler2D NoiseMap;&lt;br /&gt;
 uniform vec3 MColor1;&lt;br /&gt;
 uniform vec3 MColor2;&lt;br /&gt;
 uniform vec3 BColor1, Bcolor2; //0.0,0.0,0.0 | 0.5,0.5,0.5&lt;br /&gt;
 uniform vec2 BSize, BP; //1.30,1.15 | 0.50,1.0&lt;br /&gt;
 varying float outLight;&lt;br /&gt;
 varying vec3 MCposition;&lt;br /&gt;
 void main ()&lt;br /&gt;
 {&lt;br /&gt;
              // вычисляем квадраты&lt;br /&gt;
 vec2 Lpos = MCposition / BSize;&lt;br /&gt;
 if (fract (Lpos.y )&amp;gt;0.5)&lt;br /&gt;
              Lpos.x +=0.5;&lt;br /&gt;
 Lpos = fract (Lpos);&lt;br /&gt;
 vec2 uses = step (Lpos, BP);&lt;br /&gt;
 vec3 color1 = mix (BColor2, BColor1, uses.x * uses.y);&lt;br /&gt;
 // генерируем узор&lt;br /&gt;
 vec3 noisevec = texture2D (NoiseMap, MCposition *1.5);&lt;br /&gt;
 float intensity = (noisevec[0] - 0.5) +(noisevec[1] - 0.25);&lt;br /&gt;
 float ssin = sin(MCposition.y * 100.0 + intensity * 100.0)+0.4;&lt;br /&gt;
 // перемешиваем и загружаем в gl_FragColor&lt;br /&gt;
 vec3 color = mix(MColor2+color1, MColor1+color1, ssin) * outLight;&lt;br /&gt;
 gl_FragColor = vec4 (color, 1.0);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF111_99_1.jpg|300px|Рис. 3]]Рис. 3. Результат работы шейдера мрамора (контрастность усилена).|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
У полученного шейдера немало недостатков, однако со своей ознакомительной функцией он вполне справляется (рис. 3). Если еще&lt;br /&gt;
немного поиграть с параметрами, можно добиться гораздо более естественного результата, но нас ждет последний в этом уроке шейдер&lt;br /&gt;
деревянной поверхности.&lt;br /&gt;
&lt;br /&gt;
===Под фанеру===&lt;br /&gt;
&lt;br /&gt;
Принцип его создания примерно такой же, как и в случае с мрамором.&lt;br /&gt;
Уточним условия задачи:&lt;br /&gt;
* Рисунок поверхности состоит из чередования светлых и темных участков в форме концентрических цилиндров;&lt;br /&gt;
* Для реалистичности необходимо искажение цилиндров при помощи шума.&lt;br /&gt;
&lt;br /&gt;
Для шейдера деревянной поверхности нам понадобятся переменные, содержащие основной и дополнительный цвета, масштабирование и фактор искажения для создания шума:&lt;br /&gt;
&lt;br /&gt;
 uniform vec3 LWood; //0.9,0.8,0.6&lt;br /&gt;
 uniform vec3 DWood; //0.6,0.3,0.04&lt;br /&gt;
 uniform vec3 NScale; //0.1,0.1,0.1&lt;br /&gt;
 uniform float NNess; //3.0&lt;br /&gt;
&lt;br /&gt;
Как обычно, вначале загружаем в вектор карту шумов и настраиваем необходимый тип генерации:&lt;br /&gt;
&lt;br /&gt;
 vec3 noisevec = vec3 (texture2D (NoiseMap, MCposition * NScale)*Nness);&lt;br /&gt;
&lt;br /&gt;
Последние два параметра отвечают за толщину и узор волокон. Поиграйте с ними и посмотрите, что получится. Мы высчитываем&lt;br /&gt;
конечные координаты в соответствии с полученным вектором и координатами вершины:&lt;br /&gt;
&lt;br /&gt;
 vec3 location = MCposition + noisevec;&lt;br /&gt;
&lt;br /&gt;
Теперь займемся рисованием кругов. Для этого нужно знать расстояние от оси бревна до текущего годичного кольца. Возведем&lt;br /&gt;
координаты '''x''' и '''z''' в квадрат и извлечем из них квадратный корень. Количество кругов можно контролировать, если перемножить коэффициент масштабирования на полученный результат:&lt;br /&gt;
&lt;br /&gt;
 float dist = sqrt (location.x * location.x * location.z * location.z);&lt;br /&gt;
 dist *=4;&lt;br /&gt;
&lt;br /&gt;
Однако то, что у нас сейчас получилось, очень мало напоминает&lt;br /&gt;
дерево. Обычно структура древесины не состоит из строгих концентрических цилиндров с резким разделением на темные и светлые полосы, а имеет плавные переходы между ними. Добиться этого можно, если&lt;br /&gt;
написать функцию с постепенным изменением значения от 0 до 1.0 и&lt;br /&gt;
наоборот. Необходимо сложить полученное расстояние с тремя октавами шума и выделить из результата дробную часть. Небольшой кусок&lt;br /&gt;
кода будет отвечать за своевременное изменение конечного значения:&lt;br /&gt;
&lt;br /&gt;
 float r = fract (dist + noisevec[0] + noisevec [1] + noisevec[2]);&lt;br /&gt;
 if (r &amp;gt; 1.0) r = 2.0 -r;&lt;br /&gt;
&lt;br /&gt;
Вот таким образом мы получили необходимые данные для функции&lt;br /&gt;
'''mix''' на выходе. Остальной код вам хорошо знаком. В целом, фрагментный шейдер дерева выглядит следующим образом (рис. 4):&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF111_99_2.jpg|300px|Рис. 4]]Рис. 4. Дом, который построил Дж... Нет уж, мы!|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
 //GLSL fragment shader (wood)&lt;br /&gt;
 uniform sampler2D NoiseMap;&lt;br /&gt;
 uniform vec3 LWood;&lt;br /&gt;
 uniform vec3 DWood;&lt;br /&gt;
 uniform vec3 NScale;&lt;br /&gt;
 uniform float NNess;&lt;br /&gt;
 varying float outLight;&lt;br /&gt;
 varying vec3 MCposition;&lt;br /&gt;
 void main ()&lt;br /&gt;
 {&lt;br /&gt;
       vec3 noisevec = vec3 (texture2D (NoiseMap, MCposition * NScale)* NNess);&lt;br /&gt;
        vec3 location = MCposition + noisevec;&lt;br /&gt;
       float dist = sqrt (location.x * location.x * location.z * location.z);&lt;br /&gt;
        dist *=4;&lt;br /&gt;
       float r = fract (dist + noisevec[0] + noisevec [1] + noisevec[2]);&lt;br /&gt;
       if (r &amp;gt; 1.0)&lt;br /&gt;
                    r = 2.0 -r;&lt;br /&gt;
       vec3 color = mix (LWood, DWood, r);&lt;br /&gt;
       color *= outLight;&lt;br /&gt;
       gl_FragColor = vec4 (color, 1.0);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Процедурные текстуры занимают немаловажное место в реалистичной визуализации сцены. Они компактны, шустры и очень гибки в плане настраиваемости под конкретную задачу. Но даже если откинуть нарочитость простоты реализации наших шейдеров, видно, что до&lt;br /&gt;
уровня естественности они явно не дотягивают. Большое значение при&lt;br /&gt;
этом имеет правильная работа с освещением. Именно этому будет&lt;br /&gt;
посвящен следующий урок. '''LXF'''&lt;/div&gt;</description>
			<pubDate>Tue, 06 Oct 2009 12:15:46 GMT</pubDate>			<dc:creator>Crazy Rebel</dc:creator>			<comments>http://wiki2.linuxformat.ru/index.php/%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:LXF111:%D0%98%D0%B3%D1%80%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B9</comments>		</item>
	</channel>
</rss>