Работа с координатами текстуры

Последнее обновление: 1.11.2015

Используя координаты текстуры мы моем более точно проецировать ее на поверхность объекта. Итак, создадим следующую страницу:

<!DOCTYPE html>
<html>
<head>
<title>3D in WebGL!</title>
<meta charset="utf-8" />
</head>
<body>
<canvas id="canvas3D" width="400" height="300">Ваш браузер не поддерживает элемент canvas</canvas>

<script id="shader-fs" type="x-shader/x-fragment">
precision highp float;
uniform sampler2D uSampler;
varying vec2 vTextureCoords;

  void main(void) {
    gl_FragColor = texture2D(uSampler, vTextureCoords);
  }
</script>

<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec2 aVertexTextureCoords;
varying vec2 vTextureCoords;

  void main(void) {
    gl_Position = vec4(aVertexPosition, 1.0);
	vTextureCoords = aVertexTextureCoords;
  }
</script>

<script type="text/javascript">
var gl;
var shaderProgram;
var vertexBuffer;
var indexBuffer;
var textureCoordsBuffer; // буфер координат текстуры

var texture; // переменная для хранения текстуры

function initShaders() {
	var fragmentShader = getShader(gl.FRAGMENT_SHADER, 'shader-fs');
	var vertexShader = getShader(gl.VERTEX_SHADER, 'shader-vs');

	shaderProgram = gl.createProgram();

	gl.attachShader(shaderProgram, vertexShader);
	gl.attachShader(shaderProgram, fragmentShader);

	gl.linkProgram(shaderProgram);
	 
	if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
		alert("Не удалось установить шейдеры");
	}
	 
	gl.useProgram(shaderProgram);

	shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
	gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); 
	shaderProgram.vertexTextureAttribute = gl.getAttribLocation(shaderProgram, "aVertexTextureCoords");
    gl.enableVertexAttribArray(shaderProgram.vertexTextureAttribute);
}

function getShader(type,id) {
	var source = document.getElementById(id).innerHTML;

	var shader = gl.createShader(type);
	
	gl.shaderSource(shader, source);

	gl.compileShader(shader);
  
	if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
		alert("Ошибка компиляции шейдера: " + gl.getShaderInfoLog(shader));
		gl.deleteShader(shader);   
		return null;
	}
	return shader;  
}

function initBuffers() {

	var	vertices =[
				-0.5, -0.5, 0.5, // v1
				-0.5, 0.5, 0.5,  // v2
				 0.5, 0.5, 0.5,  // v3
				 0.5, -0.5, 0.5  // v4
				 ];

  vertexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
  vertexBuffer.itemSize = 3;
 
	var indices = [0, 1, 2, 2, 3, 0];
	indexBuffer = gl.createBuffer();
	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
	gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
	indexBuffer.numberOfItems = indices.length;	
  
	// Координаты текстуры
	var textureCoords = [
				0.0, 0.0,
				0.0, 1.0,
				1.0, 1.0,
				1.0, 0.0
				 ];
	// Создание буфера координат текстуры
	textureCoordsBuffer = gl.createBuffer();
	gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordsBuffer);
	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
	textureCoordsBuffer.itemSize=2; // каждая вершина имеет две координаты
}
 
function draw() {    

	gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
	gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 
                         vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);

	gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordsBuffer);
	gl.vertexAttribPointer(shaderProgram.vertexTextureAttribute,
                         textureCoordsBuffer.itemSize,  gl.FLOAT, false, 0, 0);
	gl.activeTexture(gl.TEXTURE0);
	gl.bindTexture(gl.TEXTURE_2D, texture);
	
	gl.drawElements(gl.TRIANGLES, indexBuffer.numberOfItems, gl.UNSIGNED_SHORT,0);
	
}
function setupWebGL()
{
	gl.clearColor(0.0, 0.0, 0.0, 1.0); 	
	gl.clear(gl.COLOR_BUFFER_BIT || gl.DEPTH_BUFFER_BIT); 	
				
	gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
}
 
function setTextures(){
	texture = gl.createTexture();
	gl.bindTexture(gl.TEXTURE_2D, texture);
	var image = new Image();
	 
	image.onload = function() {
	
		handleTextureLoaded(image, texture);	
  }
  
   image.src = "brickwall.png";

	shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
	gl.uniform1i(shaderProgram.samplerUniform, 0);
}
 function handleTextureLoaded(image, texture) {
 
	gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
	gl.bindTexture(gl.TEXTURE_2D, null);
}
window.onload=function(){

	var canvas = document.getElementById("canvas3D");
	try {
		gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
	}
	catch(e) {}
  
	  if (!gl) {
		alert("Ваш браузер не поддерживает WebGL");
	  }
	if(gl){
		gl.viewportWidth = canvas.width;
		gl.viewportHeight = canvas.height;

		initShaders();
		initBuffers();		 
		setTextures();
		(function animloop(){
			
			setupWebGL();
			draw(); 
			requestAnimFrame(animloop, canvas);
		})();
	}
}
window.requestAnimFrame = (function(){
      return  window.requestAnimationFrame       || 
              window.webkitRequestAnimationFrame || 
              window.mozRequestAnimationFrame    || 
              window.oRequestAnimationFrame      || 
              window.msRequestAnimationFrame     ||
         function(callback, element) {
           return window.setTimeout(callback, 1000/60);
         };
    })();
</script>
</body>
</html>

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

// Координаты текстуры
var textureCoords = [
			0.0, 0.0,
			0.0, 1.0,
			1.0, 1.0,
			1.0, 0.0
		];
// Создание буфера координат текстуры
textureCoordsBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
textureCoordsBuffer.itemSize=2; // каждая вершина имеет две координаты

Каждая объявленная в массиве textureCoords точка на текстуре соответствует вершине двухмерного объекта:

А создание буфера координат текстуры аналогично созданию буфера вершин.

Функция setTextures(), которая выполняет загрузку и настройку текстуры осталась та же, что и в прошлом параграфе за тем исключением, что теперь из нее вынесены методы setupWebGL() и draw() в функцию анимации.

После создания буфера координат текстуры нам надо его содержание передать в шейдер. И чтобы сделать это, нам, во-первых, надо создать атрибут (это делается в функции настройки шейдеров initShaders): shaderProgram.vertexTextureAttribute = gl.getAttribLocation(shaderProgram, "aVertexTextureCoords");

Далее в функции отрисовки draw подобно тому, как мы создаем указатель на атрибут для вершинного буфера, то же самое проделываем с буфером координат текстуры:

gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordsBuffer);
  gl.vertexAttribPointer(shaderProgram.vertexTextureAttribute,
                         textureCoordsBuffer.itemSize, gl.FLOAT, false, 0, 0);
  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE_2D, texture);

То есть также создаем указатель на атрибут и подключаем его и далее делаем активной текстуру (gl.TEXTURE0) и связываем ее. WebGL поддерживает работу с несколькими текстурами одновременно, а использование gl.TEXTURE0 отсылает нас к первой текстуре.

И в завершении нам надо получить в шейдере атрибут aVertexTextureCoords и как-то его использовать. Для этого в вершинном шейдере заводим переменную для передачи значений текстуры во фрагментный шейдер, а данные она берет как раз из атрибута aVertexTextureCoords:

attribute vec3 aVertexPosition;
attribute vec2 aVertexTextureCoords;
varying vec2 vTextureCoords;

  void main(void) {
    gl_Position = vec4(aVertexPosition, 1.0);
	vTextureCoords = aVertexTextureCoords;
  }

И во фрагментном шейдере так же проходим семплером: gl_FragColor = texture2D(uSampler, vTextureCoords);

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850