问题
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