Hi,
I tried webgl. I have a square with a texture on it. All worked fine until I added some user interaction. If I simply translate the modelview matrix and get some unbeautiful result. When I’m in translation I can see the texture in his current position and in his last position. I get a stripe on my screen. Frames per sec about 60, I use requestAnimFrame().
here is my drawScene function. I modified code from www.learningwebgl.com/blog/?p=507 .
function drawScene() {
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, [-centerX, -centerY, -zoom]);
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, cubeVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, neheTexture);
gl.uniform1i(shaderProgram.samplerUniform, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
}
function setMatrixUniforms() {
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
}
user can only modify centerX, centerY, zoom.
so code could be shorter (or?)
function drawScene() {
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, [-centerX, -centerY, -zoom]);
setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
}
but still looks ugly.
Do i miss some magic?
Is there an other way to translate the texture? like glMatrixMode(GL_TEXTURE) , glTranslatef() in normal OpenGL (bindBuffer and copy tex coords dont work as well)
full file:
(move with up/down, left/right, zoom with page up/down, different textures 0-9)
<html>
<head>
<title>flicker</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<script type="text/javascript">
//from src="glMatrix-0.9.5.min.js"
glMatrixArrayType=typeof Float32Array!="undefined"?Float32Array:typeof WebGLFloatArray!="undefined"?WebGLFloatArray:Array;
var mat4={};
mat4.create=function(a){var b=new glMatrixArrayType(16);if(a){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=a[12];b[13]=a[13];b[14]=a[14];b[15]=a[15]}return b};
mat4.frustum=function(a,b,c,d,e,g,f){f||(f=mat4.create());var h=b-a,i=d-c,j=g-e;f[0]=e*2/h;f[1]=0;f[2]=0;f[3]=0;f[4]=0;f[5]=e*2/i;f[6]=0;f[7]=0;f[8]=(b+a)/h;f[9]=(d+c)/i;f[10]=-(g+e)/j;f[11]=-1;f[12]=0;f[13]=0;f[14]=-(g*e*2)/j;f[15]=0;return f};
mat4.perspective=function(a,b,c,d,e){a=c*Math.tan(a*Math.PI/360);b=a*b;return mat4.frustum(-b,b,-a,a,c,d,e)};
mat4.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=a[12];b[13]=a[13];b[14]=a[14];b[15]=a[15];return b};
mat4.identity=function(a){a[0]=1;a[1]=0;a[2]=0;a[3]=0;a[4]=0;a[5]=1;a[6]=0;a[7]=0;a[8]=0;a[9]=0;a[10]=1;a[11]=0;a[12]=0;a[13]=0;a[14]=0;a[15]=1;return a};
mat4.translate=function(a,b,c){var d=b[0],e=b[1];b=b[2];if(!c||a==c){a[12]=a[0]*d+a[4]*e+a[8]*b+a[12];a[13]=a[1]*d+a[5]*e+a[9]*b+a[13];a[14]=a[2]*d+a[6]*e+a[10]*b+a[14];a[15]=a[3]*d+a[7]*e+a[11]*b+a[15];return a}var g=a[0],f=a[1],h=a[2],i=a[3],j=a[4],k=a[5],l=a[6],o=a[7],m=a[8],n=a[9],p=a[10],r=a[11];c[0]=g;c[1]=f;c[2]=h;c[3]=i;c[4]=j;c[5]=k;c[6]=l;c[7]=o;c[8]=m;c[9]=n;c[10]=p;c[11]=r;c[12]=g*d+j*e+m*b+a[12];c[13]=f*d+k*e+n*b+a[13];c[14]=h*d+l*e+p*b+a[14];c[15]=i*d+o*e+r*b+a[15];return c};
</script>
<script type="text/javascript">
//from src src="webgl-utils.js"
/**
* Provides requestAnimationFrame in a cross browser way.
*/
window.requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
window.setTimeout(callback, 1000/60);
};
})();
</script>
<script id="shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
void main(void) {
gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
}
</script>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
varying vec2 vTextureCoord;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
}
</script>
<script type="text/javascript">
var gl;
function initGL(canvas) {
try {
gl = canvas.getContext("experimental-webgl");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
} catch (e) {
}
if (!gl) {
alert("Could not initialise WebGL, sorry :-(");
}
}
function getShader(gl, id) {
var shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}
var str = "";
var k = shaderScript.firstChild;
while (k) {
if (k.nodeType == 3) {
str += k.textContent;
}
k = k.nextSibling;
}
var shader;
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
return null;
}
gl.shaderSource(shader, str);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
var shaderProgram;
function initShaders() {
var fragmentShader = getShader(gl, "shader-fs");
var vertexShader = getShader(gl, "shader-vs");
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Could not initialise shaders");
}
gl.useProgram(shaderProgram);
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);
shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
}
function handleLoadedTexture(texture, data) {
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, data);
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);
}
var neheTexture;
textureSize = 512;
var pixels = new Uint8Array(4*textureSize*textureSize)
var pixelsWhite = new Uint8Array(4*textureSize*textureSize)
var pixelsBlack = new Uint8Array(4*textureSize*textureSize)
function initTexture() {
for (var i=0;i<4*textureSize*textureSize;i+=4){
var bORw = (!(((i/4)%2)+Math.floor((i/4/textureSize)%2)))*255
pixels[i] = bORw
pixels[i+1] = bORw
pixels[i+2] = bORw
pixels[i+3] = 255
}
//nehhh = gl.createTexture();
neheTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, neheTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize,textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels );
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);
for (var i=0;i<4*textureSize*textureSize;i+=4){
pixelsWhite[i] = 255
pixelsWhite[i+1] = 255
pixelsWhite[i+2] = 255
pixelsWhite[i+3] = 255
pixelsBlack[i] = 0
pixelsBlack[i+1] = 0
pixelsBlack[i+2] = 0
pixelsBlack[i+3] = 255
}
}
var mvMatrix = mat4.create();
var mvMatrixStack = [];
var pMatrix = mat4.create();
function mvPushMatrix() {
var copy = mat4.create();
mat4.set(mvMatrix, copy);
mvMatrixStack.push(copy);
}
function mvPopMatrix() {
if (mvMatrixStack.length == 0) {
throw "Invalid popMatrix!";
}
mvMatrix = mvMatrixStack.pop();
}
function setMatrixUniforms() {
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
}
var cubeVertexPositionBuffer;
var cubeVertexTextureCoordBuffer;
var cubeVertexIndexBuffer;
function initBuffers() {
cubeVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
vertices = [
// Front face
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0,
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
cubeVertexPositionBuffer.itemSize = 3;
cubeVertexPositionBuffer.numItems = 24;
cubeVertexTextureCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
var textureCoords = [
// Front face
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
cubeVertexTextureCoordBuffer.itemSize = 2;
cubeVertexTextureCoordBuffer.numItems = 8;
cubeVertexIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
var cubeVertexIndices = [
0, 1, 2, 0, 2, 3, // Front face
];
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
cubeVertexIndexBuffer.itemSize = 1;
cubeVertexIndexBuffer.numItems = 6;
}
var zoom = 0.42;
var dirX = 0;
var dirY = 0;
var centerX =0;
var centerY =0;
var currentlyPressedKeys = {};
function handleKeyDown(event) {
currentlyPressedKeys[event.keyCode] = true;
}
function handleKeyUp(event) {
currentlyPressedKeys[event.keyCode] = false; }
function handleKeys() {
if (currentlyPressedKeys[33]) {
// Page Up
zoom *= 0.99;
}
if (currentlyPressedKeys[34]) {
// Page Down
zoom *= 1.0/0.99;
}
if (currentlyPressedKeys[37]) {
// Left cursor key
dirX=-1.0
}else if (currentlyPressedKeys[39]) {
// Right cursor key
dirX=1.0
}else{
dirX=0.0
}
if (currentlyPressedKeys[38]) {
// Up cursor key
dirY=1.0
}else if (currentlyPressedKeys[40]) {
// Down cursor key
dirY=-1.0
}else{
dirY=0.0
}
if(currentlyPressedKeys[49]) {
//white in black
for (var i=0;i<4*textureSize*textureSize;i+=4){
var bORw = (!(((i/4)%2)+Math.floor((i/4/textureSize)%2)))*255
pixels[i] = bORw
pixels[i+1] = bORw
pixels[i+2] = bORw
pixels[i+3] = 255
}
gl.bindTexture(gl.TEXTURE_2D, neheTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize,textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels );
}
if(currentlyPressedKeys[50]) {
//balck & white chess
for (var i=0;i<4*textureSize*textureSize;i+=4){
var bORw = (((i/4)%2)^((i/4/textureSize)%2))*255
pixels[i] = bORw
pixels[i+1] = bORw
pixels[i+2] = bORw
pixels[i+3] = 255
}
gl.bindTexture(gl.TEXTURE_2D, neheTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize,textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels );
}
if(currentlyPressedKeys[51]) {
//x lines black or white
for (var i=0;i<4*textureSize*textureSize;i+=4){
var bORw = ((i/4)%2)*255
pixels[i] = bORw
pixels[i+1] = bORw
pixels[i+2] = bORw
pixels[i+3] = 255
}
gl.bindTexture(gl.TEXTURE_2D, neheTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize,textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels );
}
if(currentlyPressedKeys[52]) {
//y lines black or white
for (var i=0;i<4*textureSize*textureSize;i+=4){
var bORw = Math.floor((i/4/textureSize)%2) *255
pixels[i] = bORw
pixels[i+1] = bORw
pixels[i+2] = bORw
pixels[i+3] = 255
}
gl.bindTexture(gl.TEXTURE_2D, neheTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize,textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels );
}
if(currentlyPressedKeys[53]) {
//bw diagonal
for (var i=0;i<4*textureSize*textureSize;i+=4){
var bORw = ( (i/8)%4 ^ ((i/8/textureSize)%4) )*255
pixels[i] = bORw
pixels[i+1] = bORw
pixels[i+2] = bORw
pixels[i+3] = 255
}
gl.bindTexture(gl.TEXTURE_2D, neheTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize,textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels );
}
if(currentlyPressedKeys[54]) {
//grey random
for (var i=0;i<4*textureSize*textureSize;i+=4){
var value = Math.random()*255
pixels[i] = value
pixels[i+1] = value
pixels[i+2] = value
pixels[i+3] = 255
}
gl.bindTexture(gl.TEXTURE_2D, neheTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize,textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels );
}
if(currentlyPressedKeys[55]) {
//all random
for (var i=0;i<4*textureSize*textureSize;i+=4){
pixels[i] = Math.random()*255
pixels[i+1] = Math.random()*255
pixels[i+2] = Math.random()*255
pixels[i+3] = 255
}
gl.bindTexture(gl.TEXTURE_2D, neheTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize,textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels );
}
if(currentlyPressedKeys[56]) {
//random typed math 1
var value = new Date().getTime()*1e-3
for (var i=0;i<4*textureSize*textureSize;i+=4){
pixels[i] = 0
pixels[i+1] = (value+ i*i/textureSize/4)*255
pixels[i+2] = 0
pixels[i+3] = 255
}
gl.bindTexture(gl.TEXTURE_2D, neheTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize,textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels );
}
if(currentlyPressedKeys[57]) {
//random typed math 2
for (var i=0;i<4*textureSize*textureSize;i+=4){
pixels[i] = ( Math.sin(i/textureSize*2*3.14)+1)*127* (( Math.sin(i/textureSize*2*3.14)+Math.tan(i))>1)
pixels[i+1] = 0
pixels[i+2] = 0
pixels[i+3] = 255
}
gl.bindTexture(gl.TEXTURE_2D, neheTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize,textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels );
}
if(currentlyPressedKeys[48]) {
//random typed math 3
var value = 4*textureSize*textureSize
for (var i=0;i<4*textureSize*textureSize;i+=4){
pixels[i] = 0
pixels[i+1] = 0
pixels[i+2] = (value%i)
pixels[i+3] = 255
}
gl.bindTexture(gl.TEXTURE_2D, neheTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize,textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels );
}
}
function drawScene() {
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, [-centerX, -centerY, -zoom]);
setMatrixUniforms();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, cubeVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, neheTexture);
gl.uniform1i(shaderProgram.samplerUniform, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
}
var lastTime = 0;
function animate() {
var timeNow = new Date().getTime();
if (lastTime != 0) {
var elapsed = timeNow - lastTime;
var speed = zoom*0.42
centerX += elapsed/1000*dirX*speed
centerY += elapsed/1000*dirY*speed
}
lastTime = timeNow;
}
function tick() {
requestAnimFrame(tick);
handleKeys();
drawScene();
animate();
}
function webGLStart() {
var canvas = document.getElementById("flicker");
initGL(canvas);
initShaders();
initBuffers();
initTexture();
gl.clearColor(0.0, 0.3, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
document.onkeydown = handleKeyDown;
document.onkeyup = handleKeyUp;
tick();
}
</script>
</head>
<body onload="webGLStart();" marginwidth="0" marginheight="0" topmargin="0" leftmargin="0">
<canvas id="flicker" style="border: none;" width="800" height="600"></canvas>
</body>
</html>