Строим 3D проекты – Шаг 6

В программе 2-го шага, мы нарисовали простейшую 2D фигуру. На 3-ем шаге, мы закрашивали ее разными цветами:

Теперь разберемся, как закрасить фигуру текстурой из графического файла.
Пример на основе простейшей 2D фигуры
Это изображение из 128 на 128 пикселей будет минимальным элементом текстуры — текселем (texture element). Координатные оси размещения текселей задаются буквами {s,t,p,q}. Это стандартные буквы, как и {x,y,z,w} для координат, и буквы {r,g,b,a} для цветов.
Для 2D наложения текстуры используются первые координаты (s,t).
Если тексель размером от 0 до 1, то, в данном случае, 0,5 текселей это 64 пикселей.
2. Заводим глобальные переменные для хранения изображения и текстуры:
var texture06pr = null; var textureImage06pr = null;
3. Дополним вершинный и пиксельный шейдеры для работы с текстурой. Все, что косалось работы с цветом, можно пока удалить. В вершинный шейдер будем передавать координаты текстур:
<script id= "shader-vs06pr" type= "x-shader/x-vertex">
attribute vec3 aVertexPosition06pr;
varying highp vec2 vTextureCoord06pr;
void main(void)
{
gl_Position = vec4( aVertexPosition06pr, 1.0);
vTextureCoord06pr = (aVertexPosition06pr.xy + 0.5);
}
</script>
Для данного примера, с целью упрощения программы, для задания координат текстуры (s,t) используем координаты вершин (x,y) из aVertexPosition. Напомню, что координаты вершин у нас от -0.5 до 0.5:
var triangleVertices =
[
//Triangle's points coodinates.
-0.5, 0.5, 0.0,
0.0, 0.0, 0.0,
-0.5, -0.5, 0.0
];
поэтому, чтобы текстура выводилась из одного текселя, а не из отрицательных текселей, мы добавили к каждому значению 0.5:
vTextureCoord06pr = ( aVertexPosition06pr.xy + 0.5);
В пиксельном шейдере принимаем это значение и отправляем в функцию texture2D:
<script id= "shader-fs06pr" type= "x-shader/x-fragment">
varying highp vec2 vTextureCoord06pr;
uniform sampler2D uSampler06pr;
void main(void)
{
//gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
gl_FragColor = texture2D( uSampler06pr, vec2( vTextureCoord06pr.s, vTextureCoord06pr.t ) );
}
</script>
4. В главный алгоритм программы добавляем функцию loadTexture () для загрузки текстуры из файла. Также добавляем обработчик события textureImage.onload, который в случае успешной загрузки изображения запускает функцию настройки текстуры и функции прорисовки:
if(gl06pr)
{
initShaders06pr(); //[1]
setupBuffers06pr(); //[2]
loadTexture06pr();
textureImage06pr.onload = function()
{
setupTexture06pr();
setupWebGL06pr(); //[3]
drawScene06pr(); //[4]
}
}
5. Добавляем описанные выше новые функции:
function loadTexture06pr()
{
textureImage06pr = new Image();
textureImage06pr.src = "textures/face-128x128.png";
}
Мы использовали HTML объект Image ().
Важно: чтобы загрузился файл текстуры, необходимо запускать программу на веб-сервере (JS не разрешено работать с файлами на компьютере пользователя). То есть, в браузере необходимо ввести адрес программы типа:
http://192.168.137.1/as/06-triangle-2d-texture
Если в указанной таким образом папке есть файл index.html с нашей программой, то он запустится автоматически. Как установить веб-сервер, написано в статье про XAMMP.
Теперь, наконец, главные действия в этой программе — устанавливаем текстуру:
function setupTexture06pr()
{
texture06pr = gl06pr.createTexture();
gl06pr.bindTexture( gl06pr.TEXTURE_2D, texture06pr);
if( !gl06pr.isTexture( texture06pr) )
console.error( "Error: Texture is invalid");
else
{
gl06pr.pixelStorei( gl06pr.UNPACK_FLIP_Y_WEBGL, true);
gl06pr.texParameteri( gl06pr.TEXTURE_2D, gl06pr.TEXTURE_MAG_FILTER, gl06pr.NEAREST);
gl06pr.texParameteri( gl06pr.TEXTURE_2D, gl06pr.TEXTURE_MIN_FILTER, gl06pr.NEAREST);
gl06pr.texImage2D( gl06pr.TEXTURE_2D, 0, gl06pr.RGBA, gl06pr.RGBA, gl06pr.UNSIGNED_BYTE, textureImage06pr );
glProgram06pr.samplerUniform = gl06pr.getUniformLocation( glProgram06pr, "uSampler06pr");
gl06pr.uniform1i( glProgram06pr.samplerUniform, 0);
}
}
а – Мы создаем объект текстуры типа WebGLTextureObject в переменной texture.
б – Связываем с программой текстуру texture в режиме gl.TEXTURE_2D.
в – Функция gl.pixelStorei () указывает WebGL как хранить данные. В данном случае, инвертирует порядок вывода текселей по оси T (они изначально идут сверху вниз).
г – Функции gl.texParameteri () указывают WebGL как выполнять сглаживание и заполнение текстурой. Об этом в другой раз.
д – Вызываем gl.texImage2D () для установки textureImage.
Посылаем загруженную текстуру в программу шейдеров.
е – Находим в шейдерах униформу uSampler и связываем ее с glProgram.samplerUniform.
ё – Устанавливаем в glProgram.samplerUniform текущую текстуру. Параметр ноль «0» соответствует текущей текстуре «TEXTURE0».
Результат программы – наложение текстуры на объект:

Что мы видим в результате? Бесконечная поверхность текстуры виртуально формируется в 2D таким образом:

Мы наложили текстуру с добавлением 0,5 к каждой координате (зеленый треугольник), чтобы использовать целый тексель, а не части с отрицательных. С исходными координатами, мы бы увидели то, что внутри синего треугольника.
Что произойдет, если размер фигуры будет больше 1×1, то есть больше текселя? Увеличим фигуру:
var triangleVertices = [ //Triangle's points coodinates. -1.0, 1.0, 0.0, 1.0, 0.0, 0.0, -1.0, -1.0, 0.0, ];
В шейдере сместим координаты в положительную область:
vTextureCoord06pr = aVertexPosition06pr.xy + 1.0;

В результате, фигура заполняется несколькими текселями. Заполнение будет как у розового треугольника на рисунке ранее.
При размере canvas 800×600 заметны искажения — тексели не квадратной формы. На рисунке выше, мы установили размер canvas не 800×600, а 600×600. Искажения пропали, значит, изменение размеров холста деформирует изображение.
Можно эмулировать изменение размера фигуры, умножением размера текселей, то есть, уменьшая размер текселя:
vTextureCoord06pr = (aVertexPosition06pr.xy + 1.0)*4.0;

Конечно, такое большое и нетипичное для текстуры изображение было выбрано для большей наглядности процесса наложения текстуры. Если бы мы выбрали файл вроде такого:

то результат был бы без очевидной структуры расположения текселей текстуры:

На следующем шаге мы аналогичным образом попробуем наложить текстуру на объемную фигуру 3D.