How to scroll two different textures(in a scrolling manner- fiddle attached for reference) one after the other in WebGL?

落爺英雄遲暮 提交于 2019-12-13 05:51:55

问题


I am trying to achieve something i.e. a scrolling texture one after the other same as a marquee in HTML that just scrolls the text.

Here's is what I have done so far : Fiddle , if you load it you will see first texture scrolling correctly and the second texture just comes over it after sometime(I kept 10 seconds for this).

But ideally, it should behave like one after the other, for eg: if "This is a test" is a marquee, then they come one after the other. Similarly , "Image1 must be followed by Image2 after some space". I hope I am clear with my question.

Also, to add up, the method sendImageLineByLine() is implemented in server , just for the sake of adding a test case , I have added it with dummy images.

// 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();
<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>

回答1:


Desired effect may be achieved with much simpler approach. You just need a quad (basically, a square comprised of two triangles), which will be drawn twice (or as many times as you want) with different textures and offsets applied. Vertex shader may look somewhat like this:

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;
}

Fragment shader will be trivial. And each frame you just need to update quad's offsets and redraw them:

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()

Now by manipulating deltas and initial offset values, you can get two images sliding one right after the other or even with different speeds (e.g. for parallax effect).



来源:https://stackoverflow.com/questions/37434440/how-to-scroll-two-different-texturesin-a-scrolling-manner-fiddle-attached-for

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!