如何在WebGL中一个接一个地滚动两个不同的纹理(以滚动方式 - 小提琴作为参考)?

时间:2016-05-25 10:29:52

标签: textures webgl

我正在努力实现一些东西,即一个接一个地滚动纹理,就像滚动文本的HTML中的选框一样。

这是我到目前为止所做的事情: Fiddle,如果你加载它,你会看到第一个纹理正确滚动,第二个纹理在一段时间之后就会超过它(我保持10秒钟)。

但理想情况下,它应该像一个接一个地表现,例如:if"这是一个测试"是一个大帐篷,然后他们一个接一个地来。类似地," Image1必须在一些空格后跟随Image2"。 我希望我对自己的问题很清楚。

另外,为了加起来,方法sendImageLineByLine()在服务器中实现,只是为了添加一个测试用例,我添加了虚拟图像。



// WEBGL UTIL START
// jshint ignore: start
var addHeading = function (text) {
	var h1 = document.createElement('h1');
	h1.innerHTML = text;
	document.body.appendChild(h1);
};

var drawCanvas = function (width, height) {
	var canvas = document.createElement('canvas');
	canvas.width = width;
	canvas.height = height;
	document.body.appendChild(canvas);
	return canvas;
};

var getGLContext = function(canvas){
	var ctx = null;
	
	if (canvas == null){
		alert('there is no canvas on this page');
		return null;
	}
	else {
		c_width = canvas.width;
		c_height = canvas.height;
	}
			
	var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];

	for (var i = 0; i < names.length; ++i) {
	try {
		ctx = canvas.getContext(names[i]);
	} 
	catch(e) {}
		if (ctx) {
			break;
		}
	}
	if (ctx == null) {
		alert("Could not initialise WebGL");
		return null;
	}
	else {
		return ctx;
	}
}

var createVertexShader = function (vertexShaderSource) {
	console.log(vertexShaderSource);
	var vertexShader = gl.createShader(gl.VERTEX_SHADER);
	gl.shaderSource(vertexShader, vertexShaderSource);
	gl.compileShader(vertexShader);
	return vertexShader;
}

var createFragmentShader = function (fragmentShaderSource) {
	console.log(fragmentShaderSource);
	var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
	gl.shaderSource(fragmentShader, fragmentShaderSource);
	gl.compileShader(fragmentShader);
	return fragmentShader;
}


var createAndLinkPrograms = function (vertexShader, fragmentShader) {
	var program = gl.createProgram();
	gl.attachShader(program, vertexShader);
	gl.attachShader(program, fragmentShader);
	gl.linkProgram(program);
    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
        alert('Could not initialise shaders');
    }
	gl.useProgram(program);
	return program;
}

var createAndBindBuffer = function (verticesOrIndices, bufferType) {
	var buffer = gl.createBuffer();
	gl.bindBuffer(bufferType, buffer);
	gl.bufferData(bufferType, verticesOrIndices, gl.STATIC_DRAW);
	//clear memory
//	gl.bindBuffer(bufferType, null);
	return buffer;
}

var allowAllImageSizes = function() {
	  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
	  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
	  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
	  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
	  gl.bindTexture(gl.TEXTURE_2D, null);
}
// WEBGL UTIL END

var gl = null, canvas = null;
var $ = window.$;
var imageContainer = [];
var buffer = null;
//update canvas area
var updateCanvasSize = function () {
	canvas = document.getElementById('scrollingCanvas');
	canvas.width = window.innerWidth * 0.99;
	canvas.height = window.innerHeight * 0.79;
	var userAg = navigator.userAgent;
	if (userAg.indexOf('Chrome') !== -1) {
		canvas.height = window.innerHeight * 0.794;
	} else if (userAg.indexOf('Firefox')!== -1) {
		canvas.height = window.innerHeight * 0.782;
	} else if (userAg.indexOf('Opera')!== -1) {
		canvas.height = window.innerHeight * 0.782;
	} else if (userAg.indexOf('Trident')!== -1) {
		canvas.height = window.innerHeight * 0.880;
	} else if (userAg.indexOf('Safari')!== -1) {
		canvas.height = window.innerHeight * 0.784;
	} else {
		window.alert('unknown browser <br><br>');
	}
};

updateCanvasSize();
gl = getGLContext(canvas);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT || gl.DEPTH_BUFFER_BIT);

var vertexShader = createVertexShader([
                                      	'attribute vec4 aVertexPosition;',
                                      	'uniform float u_CosB;',
                                      	'uniform float u_SinB;',
                                      	'attribute vec2 aTextureCoord;',
                                      	'attribute float aOffset;',
                                      	'varying highp vec2 vTextureCoord;',
                                      	'varying highp float offset;',
                                      	'void main(void) {',
                                      		'gl_Position = aVertexPosition;',
                                      		'vTextureCoord = aTextureCoord;',
                                      		'offset = aOffset;',
                                      	'}'
                                      ].join('\n'));
var fragmentShader = createFragmentShader([
											'#ifdef GL_ES',
											'precision highp float;',
											'#endif',
                                          	'varying highp vec2 vTextureCoord;',
                                          	'uniform float offset;',
                                          	'uniform sampler2D uSampler;',
                                          	'void main(void) {',
                                          		'gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s - offset, vTextureCoord.t));',
                                          	'}'
                                          ].join('\n'));
var program = createAndLinkPrograms(vertexShader, fragmentShader);

//get glsl attributes 
var glslAVertexPosition = gl.getAttribLocation(program, 'aVertexPosition');
gl.enableVertexAttribArray(glslAVertexPosition);
var glslATextureCoord = gl.getAttribLocation(program, 'aTextureCoord');
gl.enableVertexAttribArray(glslATextureCoord);

//create vertex and indices coordinates
var vertices = new Float32Array([ -1.0, -1.0,  0.0,  1.0, -1.0,  0.0, 1.0,  1.0, 0.0, -1.0,  1.0,  0.0]);
var textureCoordinates = new Float32Array([ 0.0,  0.0, 1.0,  0.0, 1.0,  1.0,  0.0,  1.0]);
var indices = new Uint16Array([ 0,  1,  2,  0,  2,  3]);

var vertexBuffer = createAndBindBuffer(vertices, gl.ARRAY_BUFFER);
var textureCoordBuffer = createAndBindBuffer(textureCoordinates, gl.ARRAY_BUFFER);
var indicesBuffer = createAndBindBuffer(indices, gl.ELEMENT_ARRAY_BUFFER);

var texture = null;

var offset = 1.0;
var changeVal = 0.030;
var i, j, k, m, willRefresh = false;

var scrollBag = setInterval(function() {
	/*if(willRefresh) {
		offset -= 0.0015;
		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
	    gl.uniform1f(gl.getUniformLocation(program, 'offset'), offset);
	    gl.uniform1f(gl.getUniformLocation(program, 'uSampler'), 0);
	    gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
	    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
	}*/
},16);

var counter=0;
var ANGLE = 10.0;
var fps = document.getElementById('fps');
animate();
function animate() {
	window.requestAnimationFrame( animate );
	if(willRefresh) {
		offset -= 0.0015;
		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
	    gl.uniform1f(gl.getUniformLocation(program, 'offset'), offset);
//	    var uTranslation = gl.getUniformLocation(gl.program, 'u_Translation');
		 var radian = Math.PI * ANGLE / 180.0; // Convert to radians
		 var cosB = Math.cos(radian);
		 var sinB = Math.sin(radian);
		 var uCosB = gl.getUniformLocation(gl.program, 'u_CosB');
		 var uSinB = gl.getUniformLocation(gl.program, 'u_SinB');
		 gl.uniform1f(uCosB, cosB);
		 gl.uniform1f(uSinB, sinB);
//	    gl.uniform4f(uTranslation, offset, 0.0, 0.0, 0.0);
	    gl.uniform1f(gl.getUniformLocation(program, 'uSampler'), 0);
	    gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
	    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
	    counter++;
	}
}
setInterval(function(){ fps.innerHTML = counter + 'fps'; counter=0; },1000);

var lineArray = [];
var renderLineData = function (imageAttr) {
			var data = imageAttr.data;
			var alpha = 4;
			if(imageAttr.newImage) {
				offset = 1.0;
				texture = gl.createTexture();
				willRefresh = true;
				gl.bindTexture(gl.TEXTURE_2D, texture);
				gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
				gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, imageAttr.width, imageAttr.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
				allowAllImageSizes();
				gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
				gl.vertexAttribPointer(glslAVertexPosition, 3, gl.FLOAT, gl.FALSE, 0, 0);
		
				gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
				gl.vertexAttribPointer(glslATextureCoord, 2, gl.FLOAT, gl.FALSE, 0, 0);
				gl.activeTexture(gl.TEXTURE0);
				gl.bindTexture(gl.TEXTURE_2D, texture);
				gl.generateMipmap(gl.TEXTURE_2D);
			}
		    var dataTypedArray = new Uint8Array(imageAttr.height * alpha);
			//render new line
			for (i = 0, k = 0, m = 3; i < 1; i++) {
				for (j = 0 ; j < imageAttr.height; j++) {
					dataTypedArray[m-3] = data[k++];
					dataTypedArray[m-2] = data[k++];
					dataTypedArray[m-1] = data[k++];
					dataTypedArray[m] = data[k++];
					m += 4;
				}
			}
		    gl.texSubImage2D(gl.TEXTURE_2D, 0, imageAttr.index, 0, 1, imageAttr.height, gl.RGBA, gl.UNSIGNED_BYTE, dataTypedArray);
		    if(imageAttr.index === imageAttr.width-1) {
		    	/*clearInterval(scrollBag);
		    	window.alert('scrolling stopped');
		    	willRefresh = false;*/
		    }
		    dataTypedArray = null;
};

var simulateImages = function (width, height, index, data, newImage) {
	//Create a new Object to be delivered to Client.
	var lineData = {};
	lineData.width = width;
	lineData.height = height;
	lineData.index = index;
	lineData.data = data;
	lineData.newImage = newImage;
	renderLineData(lineData);
}

var lineNumber = 1;
var sendImageLineByLine = function () {
	//first image
	var k = 0;
	var newImage = true;
	var imageData = ctx1.getImageData(0, 0 , canvas1.width, canvas1. height);
	var height = imageData.height;
	var width = imageData.width;
	var data = imageData.data;
	var lineDataArr = new ArrayBuffer(height*4);
	for (var i = 0 ; i < width; i++) {
		k = 0;
		for (var j = 0 ; j < height; j++) {
			lineDataArr[k++] = data[(i * 4 + width * 4 * j)]; //red
			lineDataArr[k++] = data[(i * 4 + width * 4 * j) + 1]; // blue
			lineDataArr[k++] = data[(i * 4 + width * 4 * j) + 2]; //green
			lineDataArr[k++] = data[(i * 4 + width * 4 * j) + 3]; //alpha
		}
		simulateImages(width, height, lineNumber++, lineDataArr, newImage);
		lineDataArr = new ArrayBuffer(height*4);
		newImage = false;
	}
	
	//second image
	setTimeout (function () {
		console.log('uiuiuiui');
		lineNumber = 1;
		k = 0;
		imageData = ctx2.getImageData(0, 0 , canvas2.width, canvas2. height);
		height = imageData.height;
		width = imageData.width;
		data = imageData.data;
		lineDataArr = new ArrayBuffer(height*4);
		for (var i = 0 ; i < width; i++) {
			k = 0;
			for (var j = 0 ; j < height; j++) {
				lineDataArr[k++] = data[(i * 4 + width * 4 * j)]; //red
				lineDataArr[k++] = data[(i * 4 + width * 4 * j) + 1]; //green
				lineDataArr[k++] = data[(i * 4 + width * 4 * j) + 2]; //blue
				lineDataArr[k++] = data[(i * 4 + width * 4 * j) + 3]; //alpha
			}
			simulateImages(width, height, lineNumber++, lineDataArr, newImage);
			lineDataArr = new ArrayBuffer(height*4);
			newImage = false;
		}
	}, 10000);
	console.log('Complete');
}

var canvas1 = document.getElementById('canvas1');
var canvas2 = document.getElementById('canvas2');
var ctx1 = canvas1.getContext('2d');
var ctx2 = canvas2.getContext('2d');
ctx1.fillStyle = "red";
ctx2.fillStyle = "green";
for (var i = 0; i < 10; i++) {
  ctx1.fillRect(Math.random()*150,Math.random()*150,Math.random()*100,Math.random()*100);
  ctx2.fillRect(Math.random()*150,Math.random()*150,Math.random()*100,Math.random()*100);
}
		
sendImageLineByLine();
&#13;
<p id="fps"></p>
<canvas id="canvas1" style="display:none" ></canvas><hr/>
<canvas id="canvas2" style="display:none" ></canvas>
<canvas id="scrollingCanvas" width="512" height="512"></canvas>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:0)

可以通过更简单的方法实现期望的效果。你只需要一个四边形(基本上是一个由两个三角形组成的正方形),它将被绘制两次(或根据你的需要多次),并应用不同的纹理和偏移。顶点着色器可能看起来像这样:

attribute vec2 vertexPosition;
attribute vec2 vertexTexCoord;

varying vec2 texCoord;

uniform float offsetX;

void main(void) {
    gl_Position = vec4(vertexPosition + vec2(offsetX, 0), 0, 1);
    texCoord = vertexTexCoord;
}

片段着色器将是微不足道的。每个帧你只需要更新四边形的偏移并重绘它们:

gl.clear(gl.COLOR_BUFFER_BIT);

// You also may want to "wrap" the offsets around so quads
// will come from the left edge of the canvas after disappearing
// to the right.
quad1OffsetY += offsetDelta;
quad2OffsetY += offsetDelta;

// Let's draw the quad first time
gl.uniform1f(offsetYUniformLocation, quad1OffsetY);
gl.bindTexture(gl.TEXTURE2D, quad1Texture);
gl.drawArrays(/* ... */); // or gl.drawElements()

// and the second time
gl.uniform1f(offsetYUniformLocation, quad2OffsetY);
gl.bindTexture(gl.TEXTURE2D, quad2Texture);
gl.drawArrays(/* ... */); // or gl.drawElements()

现在通过操纵增量和初始偏移值,您可以获得两个图像,一个接一个地滑动,或者甚至以不同的速度滑动(例如,用于视差效果)。