I can't get this webgl rippling effect to happen on touch screens: http://onestopdesktop.com/D2/ apparently the problem has nothing to do with the touch functions after all. something in the webgl coding itself is preventing it from operating on touch devices. I uncovered that when I changed the SOURCEFLOW default to 4 which causes a single ripple to flow on PC's on screen load. however, nothing at all happens on touch devices on load. it does work perfectly in the Chrome touch device emulator. my test touch devices (iPad 2 and 3, iPhone5, Android Asus, are all up to date OS and browser wise and are fully webgl capable.
I really love this beautiful water rippling code that I found in a tutorial at Webgl Academy, and I'm trying to help a student get this effect to work with her website.
below is the code I'm using. I added the touch event stuff to it.
var main=function() {
var CANVAS=document.getElementById("your_canvas");
CANVAS.width = window.innerWidth;
CANVAS.height = window.innerHeight;
var GL = CANVAS.getContext("webgl", {antialias: false, alpha: false});
var POINTER_X=0.5, POINTER_Y=0.5;
CANVAS.addEventListener("mousemove", waterMove);
CANVAS.addEventListener("touchstart", waterTouch);
CANVAS.addEventListener("touchmove", waterMove2);
CANVAS.addEventListener("touchend", waterStop);
function waterMove(event) {
event.preventDefault();
POINTER_X=(event.clientX-CANVAS.offsetLeft)/CANVAS.width;
POINTER_Y=1-event.clientY/CANVAS.height;
};
function waterMove2(event) {
event.preventDefault();
POINTER_X=(event.changedTouches[0].clientX-CANVAS.offsetLeft)/CANVAS.width;
POINTER_Y=1-event.changedTouches[0].clientY/CANVAS.height;
};
function waterTouch(event) {
event.preventDefault();
SOURCEFLOW = 4;
POINTER_X=(event.touches[0].clientX-CANVAS.offsetLeft)/CANVAS.width;
POINTER_Y=1-event.touches[0].clientY/CANVAS.height;
};
function waterStop(event) {
event.preventDefault();
SOURCEFLOW = 0;
POINTER_X=(event.changedTouches[0].clientX-CANVAS.offsetLeft)/CANVAS.width;
POINTER_Y=1-event.changedTouches[0].clientY/CANVAS.height;
};
SOURCEFLOW=4;
CANVAS.addEventListener("mouseup", function(event) { event.preventDefault();SOURCEFLOW = 0; } , false);
CANVAS.addEventListener("mousedown", function(event) { event.preventDefault();SOURCEFLOW = 4; } , false);
//enable texture float
var EXT_FLOAT = GL.getExtension('OES_texture_float') ||
GL.getExtension('MOZ_OES_texture_float') ||
GL.getExtension('WEBKIT_OES_texture_float');
/*========================= PARAMETERS ========================= */
var SIMUSIZEPX=512; //GPGPU simulation texture size in pixel
var SIMUWIDTH=1; //Simulation size in meters
var GPGPU_NPASS=6; //number of GPGPU pass per rendering
var WATER_DEEP=0.01; //mean height of water in meters
var RENDERING_FLOOR_SIZE=1; //size of the water floor texture in meters
/*========================= RENDERING SHADERS ========================= */
var vertSrc_render="\n\
attribute vec2 position;\n\
\n\
varying vec2 vUV;\n\
\n\
void main(void) {\n\
gl_Position = vec4(position, 0., 1.);\n\
vUV=0.5*(position+vec2(1.,1.));\n\
}";
var fragSrc_render="\n\
precision mediump float;\n\
\n\
uniform float H; //water deep (meters)\n\
uniform float L; //simulation size (meters)\n\
uniform float l; //ground texture tile size (meters)\n\
uniform sampler2D sampler;\n\
uniform sampler2D sampler_normals;\n\
\n\
varying vec2 vUV;\n\
\n\
const vec3 light=vec3(1.,1.,1.);\n\
const vec4 color_sky=vec4(60./255., 90./255., 150./255., 1.);\n\
const float n=1./1.33;\n\
\n\
void main(void) {\n\
vec4 water=texture2D(sampler_normals, vUV);\n\
vec3 water_normal=water.rgb+vec3(0.,0.,1.);\n\
float water_height=water.a;\n\
\n\
vec3 I=vec3(0.,0.,1.); //incident vector\n\
vec3 R = refract(I, water_normal, n); //refracted vector\n\
\n\
float k=(water_height+H)/R.z; //k so that M=kR belongs to the water floor\n\
vec3 M=k*R; //belongs to the water floor\n\
vec2 uv=(vUV*L-M.xy)/l; //texture coordinates of the water floor\n\
vec4 color_floor=texture2D(sampler, uv);\n\
\n\
float F=water_normal.z; //Fresnel reflexion coefficient = (I.N)\n\
\n\
vec4 color=mix(color_sky, color_floor, 0.6+F*0.3);\n\
\n\
vec3 lightDir=normalize(light-vec3(L*(vUV-vec2(0.5,0.5)), water_height));\n\
\n\
float lightPow=clamp(pow(dot(lightDir, water_normal),4.),1., 1.3);\n\
\n\
gl_FragColor=lightPow*color;\n\
}";
/*================= SHALLOW WATER EQUATION SHADERS ================== */
var fragSrc_water="\n\
precision mediump float;\n\
\n\
uniform float dt, H, b, g, epsilon;\n\
uniform float scale;\n\
uniform vec2 mouse;\n\
\n\
uniform float sourceRadius, sourceFlow;\n\
uniform sampler2D sampler_water, sampler_normals;\n\
\n\
varying vec2 vUV;\n\
\n\
void main(void) {\n\
\n\
vec4 water_t = texture2D(sampler_water, vUV);\n\
float h = water_t.r;\n\
vec2 uvSpeed = water_t.gb;\n\
\n\
vec2 dx=vec2(epsilon, 0.);\n\
vec2 dy=vec2(0., epsilon);\n\
float du_dx=(texture2D(sampler_water, vUV+dx).g-texture2D(sampler_water, vUV- dx).g)/(2.*scale);\n\
float dv_dy=(texture2D(sampler_water, vUV+dy).b-texture2D(sampler_water, vUV- dy).b)/(2.*scale);\n\
\n\
vec3 normals=texture2D(sampler_normals,vUV).xyz;\n\
\n\
//we add 1 to Nz because RGB = (0,0,0) -> Normal = (0,0,1)\n\
vec2 d_uvSpeed = -dt * (g * normals.xy/(normals.z+1.) + b*uvSpeed);\n\
\n\
float d_h = -dt * H * (du_dx + dv_dy);\n\
\n\
float dSource = length(vUV-mouse);\n\
\n\
d_h += dt * sourceFlow * (1. - smoothstep(0., sourceRadius, dSource));\n\
gl_FragColor = vec4(h+d_h, uvSpeed+d_uvSpeed, 1.);\n\
}";
/*================= TEXTURE COPY SHADERS ================== */
var fragSrc_copy="\n\
precision mediump float;\n\
\n\
uniform float scale;\n\
uniform sampler2D sampler;\n\
\n\
varying vec2 vUV;\n\
\n\
void main(void) {\n\
float dxy=1./scale;\n\
vec4 waterData = texture2D(sampler, vUV);\n\
vec4 waterDataAvg=(texture2D(sampler, vUV+vec2(dxy,0.))\n\
+.5*texture2D(sampler, vUV+vec2(dxy,dxy))\n\
+texture2D(sampler, vUV+vec2(0.,dxy))\n\
+.5*texture2D(sampler, vUV+vec2(-dxy,dxy))\n\
+texture2D(sampler, vUV+vec2(-dxy,0.))\n\
+.5*texture2D(sampler, vUV+vec2(-dxy,-dxy))\n\
+texture2D(sampler, vUV+vec2(0.,-dxy))\n\
+.5*texture2D(sampler, vUV+vec2(dxy,-dxy)))/6.;\n\
\n\
gl_FragColor = mix(waterData, waterDataAvg, 0.3);\n\
}";
/*================= NORMALS SHADERS ================== */
var fragSrc_normals="\n\
precision mediump float;\n\
\n\
uniform sampler2D sampler;\n\
uniform float epsilon, scale; //horizontal scale in meters\n\
varying vec2 vUV;\n\
\n\
vec3 getPoint(float x, float y, vec2 uv){\n\
float h = texture2D(sampler, uv+vec2(x,y)).r; //water height\n\
return vec3(x*scale,y*scale,h);\n\
}\n\
\n\
void main(void) {\n\
vec3 points[4];\n\
points[0]=getPoint(-epsilon,0., vUV);\n\
points[1]=getPoint(0.,-epsilon, vUV);\n\
points[2]=getPoint(epsilon ,0., vUV);\n\
points[3]=getPoint(0. ,epsilon, vUV);\n\
\n\
vec3 normal=normalize(cross(points[1]-points[3], points[2]-points[0]));\n\
\n\
//We substract 1 to Nz because Normal = (0,0,1) -> RGB = (0,0,0)\n\
normal.z-=1.;\n\
\n\
float height=texture2D(sampler, vUV).r;\n\
gl_FragColor=vec4(normal, height);\n\
}";
//compile a shader :
var get_shader=function(source, type, typeString) {
var shader = GL.createShader(type);
GL.shaderSource(shader, source);
GL.compileShader(shader);
if (!GL.getShaderParameter(shader, GL.COMPILE_STATUS)) {
alert("ERROR IN "+typeString+ " SHADER : " + GL.getShaderInfoLog(shader));
return false;
}
return shader;
};
//build a shader program :
var get_shaderProgram=function(vertex_source, fragment_source, typeStr){
var shader_vertex=get_shader(vertex_source, GL.VERTEX_SHADER, typeStr+" VERTEX");
var shader_fragment=get_shader(fragment_source, GL.FRAGMENT_SHADER, typeStr+" FRAGMENT");
var shader_program=GL.createProgram();
GL.attachShader(shader_program, shader_vertex);
GL.attachShader(shader_program, shader_fragment);
GL.linkProgram(shader_program);
return shader_program;
};
//final rendering shader program
var SHP_VARS={};
var SHP_RENDERING=get_shaderProgram(vertSrc_render, fragSrc_render, "RENDER");
SHP_VARS.rendering={
H: GL.getUniformLocation(SHP_RENDERING, "H"),
L: GL.getUniformLocation(SHP_RENDERING, "L"),
l: GL.getUniformLocation(SHP_RENDERING, "l"),
sampler: GL.getUniformLocation(SHP_RENDERING, "sampler"),
sampler_normals: GL.getUniformLocation(SHP_RENDERING, "sampler_normals"),
position: GL.getAttribLocation(SHP_RENDERING, "position")
};
var SHP_WATER=get_shaderProgram(vertSrc_render, fragSrc_water, "WATER");
SHP_VARS.water={
dt: GL.getUniformLocation(SHP_WATER, "dt"),
H: GL.getUniformLocation(SHP_WATER, "H"),
b: GL.getUniformLocation(SHP_WATER, "b"),
g: GL.getUniformLocation(SHP_WATER, "g"),
mouse: GL.getUniformLocation(SHP_WATER, "mouse"),
sourceFlow: GL.getUniformLocation(SHP_WATER, "sourceFlow"),
sourceRadius: GL.getUniformLocation(SHP_WATER, "sourceRadius"),
epsilon: GL.getUniformLocation(SHP_WATER, "epsilon"),
scale: GL.getUniformLocation(SHP_WATER, "scale"),
sampler_water: GL.getUniformLocation(SHP_WATER, "sampler_water"),
sampler_normals : GL.getUniformLocation(SHP_WATER, "sampler_normals"),
position: GL.getAttribLocation(SHP_WATER, "position")
};
var SHP_COPY=get_shaderProgram(vertSrc_render, fragSrc_copy, "COPY");
SHP_VARS.copy={
scale : GL.getUniformLocation(SHP_COPY, "scale"),
sampler: GL.getUniformLocation(SHP_COPY, "sampler"),
position: GL.getAttribLocation(SHP_COPY, "position")
};
var SHP_NORMALS=get_shaderProgram(vertSrc_render, fragSrc_normals, "NORMALS");
SHP_VARS.normals={
sampler: GL.getUniformLocation(SHP_NORMALS, "sampler"),
scale: GL.getUniformLocation(SHP_NORMALS, "scale"),
epsilon: GL.getUniformLocation(SHP_NORMALS, "epsilon"),
position: GL.getAttribLocation(SHP_NORMALS, "position")
};
/*========================= THE QUAD ========================= */
//POINTS :
var quad_vertex=[
-1,-1, //first summit -> bottom left of the viewport
1,-1, //bottom right
1,1, //top right
-1,1 //top left
];
var QUAD_VERTEX= GL.createBuffer ();
GL.bindBuffer(GL.ARRAY_BUFFER, QUAD_VERTEX);
GL.bufferData(GL.ARRAY_BUFFER,new Float32Array(quad_vertex),GL.STATIC_DRAW);
//FACES :
var quad_faces = [0,1,2, 0,2,3];
var QUAD_FACES= GL.createBuffer ();
GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, QUAD_FACES);
GL.bufferData(GL.ELEMENT_ARRAY_BUFFER, new Uint16Array([0,1,2, 0,2,3]),GL.STATIC_DRAW);
/*========================= THE TEXTURE ========================= */
var renderingImage=new Image();
renderingImage.src='ressources/waterFloor.jpg';
var renderingTexture=GL.createTexture();
GL.pixelStorei(GL.UNPACK_FLIP_Y_WEBGL, true);
GL.bindTexture(GL.TEXTURE_2D, renderingTexture);
GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, 1, 1, 0, GL.RGBA, GL.UNSIGNED_BYTE,
new Uint8Array([255, 0, 0, 255]));
renderingImage.onload=function() {
GL.bindTexture(GL.TEXTURE_2D, renderingTexture);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.LINEAR);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.LINEAR);
GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, renderingImage);
};
/*====================== RENDER TO TEXTURE ====================== */
//GPGPU WATER RTT :
var fb_water=GL.createFramebuffer();
GL.bindFramebuffer(GL.FRAMEBUFFER, fb_water);
var texture_water=GL.createTexture();
GL.bindTexture(GL.TEXTURE_2D, texture_water);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST);
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.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, SIMUSIZEPX, SIMUSIZEPX, 0, GL.RGBA, GL.FLOAT, null);
GL.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, GL.TEXTURE_2D, texture_water, 0);
//COPY RTT :
var fb_copy=GL.createFramebuffer();
GL.bindFramebuffer(GL.FRAMEBUFFER, fb_copy);
var texture_water_copy=GL.createTexture();
GL.bindTexture(GL.TEXTURE_2D, texture_water_copy);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST);
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.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, SIMUSIZEPX, SIMUSIZEPX, 0, GL.RGBA, GL.FLOAT, null);
GL.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, GL.TEXTURE_2D, texture_water_copy, 0);
//NORMALS RTT :
var fb_normals=GL.createFramebuffer();
GL.bindFramebuffer(GL.FRAMEBUFFER, fb_normals);
var texture_normals=GL.createTexture();
GL.bindTexture(GL.TEXTURE_2D, texture_normals);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST);
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.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, SIMUSIZEPX, SIMUSIZEPX, 0, GL.RGBA, GL.FLOAT, null);
GL.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, GL.TEXTURE_2D, texture_normals, 0);
/*========================= INIT ========================= */
//WEBGL GENERAL INIT
GL.disable(GL.DEPTH_TEST);
GL.disable(GL.SCISSOR_TEST);
GL.clearColor(0.0, 0.0, 0.0, 0.0);
//SHADER PROGRAM RENDERING INIT
GL.useProgram(SHP_RENDERING);
GL.enableVertexAttribArray(SHP_VARS.rendering.position);
GL.uniform1f(SHP_VARS.rendering.H, WATER_DEEP);
GL.uniform1f(SHP_VARS.rendering.L, SIMUWIDTH);
GL.uniform1f(SHP_VARS.rendering.l, RENDERING_FLOOR_SIZE);
GL.uniform1i(SHP_VARS.rendering.sampler, 0);
GL.uniform1i(SHP_VARS.rendering.sampler_normals, 1);
GL.vertexAttribPointer(SHP_VARS.rendering.position, 2, GL.FLOAT, false,8,0) ;
GL.bindBuffer(GL.ARRAY_BUFFER, QUAD_VERTEX);
GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, QUAD_FACES);
GL.disableVertexAttribArray(SHP_VARS.rendering.position);
//SHADER PROGRAM GPGPU WATER INIT
GL.useProgram(SHP_WATER);
GL.uniform1i(SHP_VARS.water.sampler_water, 0);
GL.uniform1i(SHP_VARS.water.sampler_normals, 1);
//WE SIMULATE A SQUARE WATER SURFACE SIDE MEASURING 2 METERS
GL.uniform1f(SHP_VARS.water.g, -9.8); //gravity acceleration
GL.uniform1f(SHP_VARS.water.H, WATER_DEEP); //mean height of water in meters
GL.uniform1f(SHP_VARS.water.b, 0.001); //viscous drag coefficient
GL.uniform1f(SHP_VARS.water.epsilon, 1/SIMUSIZEPX); //used to compute space derivatives
GL.uniform1f(SHP_VARS.water.scale, SIMUWIDTH/SIMUSIZEPX);
GL.uniform1f(SHP_VARS.water.sourceRadius, 0.04); //percentage of the surface which is flowed by the source
GL.enableVertexAttribArray(SHP_VARS.water.position);
GL.vertexAttribPointer(SHP_VARS.water.position, 2, GL.FLOAT, false,8,0) ;
GL.bindBuffer(GL.ARRAY_BUFFER, QUAD_VERTEX);
GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, QUAD_FACES);
GL.disableVertexAttribArray(SHP_VARS.water.position);
//SHADER PROGRAM TEXTURE COPY INIT
GL.useProgram(SHP_COPY);
GL.uniform1f(SHP_VARS.copy.scale, SIMUSIZEPX);
GL.uniform1i(SHP_VARS.copy.sampler, 0);
GL.enableVertexAttribArray(SHP_VARS.copy.position);
GL.vertexAttribPointer(SHP_VARS.copy.position, 2, GL.FLOAT, false,8,0) ;
GL.bindBuffer(GL.ARRAY_BUFFER, QUAD_VERTEX);
GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, QUAD_FACES);
GL.disableVertexAttribArray(SHP_VARS.copy.position);
//SHADER PROGRAM NORMALS INIT
GL.useProgram(SHP_NORMALS);
GL.uniform1i(SHP_VARS.normals.sampler, 0);
GL.uniform1f(SHP_VARS.normals.epsilon, 1/SIMUSIZEPX); //used to compute space derivatives
GL.uniform1f(SHP_VARS.normals.scale, SIMUWIDTH);
GL.enableVertexAttribArray(SHP_VARS.normals.position);
GL.vertexAttribPointer(SHP_VARS.normals.position, 2, GL.FLOAT, false,8,0) ;
GL.bindBuffer(GL.ARRAY_BUFFER, QUAD_VERTEX);
GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, QUAD_FACES);
GL.disableVertexAttribArray(SHP_VARS.normals.position);
/*========================= RENDER LOOP ========================= */
var old_timestamp=new Date().getTime();
var old_timestamp=0;
var animate=function(timestamp) {
var dt=(timestamp-old_timestamp)/1000; //time step in seconds;
dt=Math.min(Math.abs(dt), 0.017);
old_timestamp=timestamp;
GL.clear(GL.COLOR_BUFFER_BIT);
for (var i=0; i<GPGPU_NPASS; i++) {
//COPY
GL.bindFramebuffer(GL.FRAMEBUFFER, fb_copy);
GL.useProgram(SHP_COPY);
GL.viewport(0.0, 0.0, SIMUSIZEPX, SIMUSIZEPX);
GL.enableVertexAttribArray(SHP_VARS.copy.position);
GL.bindTexture(GL.TEXTURE_2D, texture_water);
GL.drawElements(GL.TRIANGLES, 6, GL.UNSIGNED_SHORT, 0);
GL.disableVertexAttribArray(SHP_VARS.copy.position);
//GPGPU PHYSICAL SIMULATION :
GL.bindFramebuffer(GL.FRAMEBUFFER, fb_water);
GL.useProgram(SHP_WATER);
GL.enableVertexAttribArray(SHP_VARS.water.position);
GL.activeTexture(GL.TEXTURE1);
GL.bindTexture(GL.TEXTURE_2D, texture_normals);
GL.activeTexture(GL.TEXTURE0);
GL.bindTexture(GL.TEXTURE_2D, texture_water_copy);
if (!i) {
GL.uniform2f(SHP_VARS.water.mouse, POINTER_X, POINTER_Y);
GL.uniform2f(SHP_VARS.water.touch, POINTER_X, POINTER_Y);
GL.uniform1f(SHP_VARS.water.sourceFlow, SOURCEFLOW);
GL.uniform1f(SHP_VARS.water.dt, dt/GPGPU_NPASS);
};
GL.drawElements(GL.TRIANGLES, 6, GL.UNSIGNED_SHORT, 0);
GL.disableVertexAttribArray(SHP_VARS.water.position);
//NORMALS :
GL.bindFramebuffer(GL.FRAMEBUFFER, fb_normals);
GL.useProgram(SHP_NORMALS);
GL.enableVertexAttribArray(SHP_VARS.normals.position);
GL.bindTexture(GL.TEXTURE_2D, texture_water);
GL.drawElements(GL.TRIANGLES, 6, GL.UNSIGNED_SHORT, 0);
GL.disableVertexAttribArray(SHP_VARS.normals.position);
} //end for GPGPU_NPASS
//RENDERING :
GL.bindFramebuffer(GL.FRAMEBUFFER, null);
GL.useProgram(SHP_RENDERING);
GL.enableVertexAttribArray(SHP_VARS.rendering.position);
GL.viewport(0.0, 0.0, CANVAS.width, CANVAS.height);
GL.activeTexture(GL.TEXTURE1);
GL.bindTexture(GL.TEXTURE_2D, texture_normals);
GL.activeTexture(GL.TEXTURE0);
GL.bindTexture(GL.TEXTURE_2D, renderingTexture);
GL.drawElements(GL.TRIANGLES, 6, GL.UNSIGNED_SHORT, 0);
GL.disableVertexAttribArray(SHP_VARS.rendering.position);
GL.flush();
window.requestAnimationFrame(animate);
};
animate(new Date().getTime());
};
Thanks for any help!!!!
Related
I have a canvas with webgl. I initialize webgl, create a fragment and a vertex shader, two triangles to cover the whole and a texture.
const vertexShaderSource = `
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}
`;
const fragmentShaderSource = `
precision mediump float;
// our texture
uniform sampler2D u_image;
// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(u_image, v_texCoord);
}
`;
I create the texture from a ImageData object whose elements are randomly chosen (each pixel a different color). When I apply the texture, it seems to cover the whole area with the upper-left pixel color stretching to cover the whole canvas.
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, IMAGEDATA);
This is the code I have so far:
const width = 128;
const height = 128;
var createShader = function (gl, shaderSource, shaderType) {
const shader = gl.createShader(shaderType);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!compiled) {
console.log(`Error compiling shader: ${gl.getShaderInfoLog(shader)}`);
return;
}
return shader;
};
var createProgram = function(gl, shaders, opt_attribs, opt_locations) {
const program = gl.createProgram();
for (var ii = 0; ii < shaders.length; ++ii) {
gl.attachShader(program, shaders[ii]);
}
if (opt_attribs) {
for (var ii = 0; ii < opt_attribs.length; ++ii) {
gl.bindAttribLocation(program, opt_locations ? opt_locations[ii] : ii, opt_attribs[ii]);
}
}
gl.linkProgram(program);
// Check the link status
var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!linked) {
console.log(`Error linking shader parameter: ${gl.getProgramInfoLog(program)}`);
gl.deleteProgram(program);
return;
}
return program;
}
// create canvas
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
// imagedata to update the texture
const imageData = {
imageData: new ImageData(width, height),
dataBuf: null,
dataBuf8: null,
data: null
};
imageData.dataBuf = new ArrayBuffer(imageData.imageData.data.length);
imageData.dataBuf8 = new Uint8ClampedArray(imageData.dataBuf);
imageData.data = new Uint32Array(imageData.dataBuf);
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
document.getElementById('container').appendChild(canvas);
// shaders
const vertexShaderSource = `
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}
`;
const fragmentShaderSource = `
precision mediump float;
// our texture
uniform sampler2D u_image;
// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(u_image, v_texCoord);
}
`;
const vertexShader = createShader(gl, vertexShaderSource, gl.VERTEX_SHADER);
const fragmentShader = createShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);
const program = createProgram(gl, [vertexShader, fragmentShader]);
gl.useProgram(program);
// triangle and triangle buffers
var positionLocation = gl.getAttribLocation(program, "a_position");
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
canvas.width, 0,
0, canvas.height,
0, canvas.height,
canvas.width, canvas.height,
canvas.width, 0]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
var texCoordLocation = gl.getUniformLocation(program, "a_texCoord");
var texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
canvas.width, 0,
0, canvas.height,
0, canvas.height,
canvas.width, canvas.height,
canvas.width, 0]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
// texture
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
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.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
for (let i = 0; i < imageData.data.length; i++) {
const r = Math.round(Math.random() * 100);
const g = Math.round(Math.random() * 100);
const b = Math.round(Math.random() * 100);
imageData.data[i] = (255 << 24) | // alpha
(b << 16) | // blue
(g << 8) | // green
r; //
}
imageData.imageData.data.set(imageData.dataBuf8);
// bind texture and draw
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imageData.imageData);
gl.drawArrays(gl.TRIANGLES, 0, 6);
<div id="container"></div>
How can I correctly place the texture?
Just two errors, Wrong location type for a_texCoords and wrong texture coords should be in the range 0 to 1.
See code below for corrections.
const width = 128;
const height = 128;
var createShader = function(gl, shaderSource, shaderType) {
const shader = gl.createShader(shaderType);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!compiled) {
console.log(`Error compiling shader: ${gl.getShaderInfoLog(shader)}`);
return;
}
return shader;
};
var createProgram = function(gl, shaders, opt_attribs, opt_locations) {
const program = gl.createProgram();
for (var ii = 0; ii < shaders.length; ++ii) {
gl.attachShader(program, shaders[ii]);
}
if (opt_attribs) {
for (var ii = 0; ii < opt_attribs.length; ++ii) {
gl.bindAttribLocation(program, opt_locations ? opt_locations[ii] : ii, opt_attribs[ii]);
}
}
gl.linkProgram(program);
// Check the link status
var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!linked) {
console.log(`Error linking shader parameter: ${gl.getProgramInfoLog(program)}`);
gl.deleteProgram(program);
return;
}
return program;
}
// create canvas
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
// imagedata to update the texture
const imageData = {
imageData: new ImageData(width, height),
dataBuf: null,
dataBuf8: null,
data: null
};
imageData.dataBuf = new ArrayBuffer(imageData.imageData.data.length);
imageData.dataBuf8 = new Uint8ClampedArray(imageData.dataBuf);
imageData.data = new Uint32Array(imageData.dataBuf);
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
document.getElementById('container').appendChild(canvas);
// shaders
const vertexShaderSource = `
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}
`;
const fragmentShaderSource = `
precision mediump float;
// our texture
uniform sampler2D u_image;
// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(u_image, v_texCoord);
}
`;
const vertexShader = createShader(gl, vertexShaderSource, gl.VERTEX_SHADER);
const fragmentShader = createShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);
const program = createProgram(gl, [vertexShader, fragmentShader]);
gl.useProgram(program);
// triangle and triangle buffers
var positionLocation = gl.getAttribLocation(program, "a_position");
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
canvas.width, 0,
0, canvas.height,
0, canvas.height,
canvas.width, canvas.height,
canvas.width, 0
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
// You had the wrong location type getUniformLocation a_exCoord is an attribute
var texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
var texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
// You had the wrong texture coords.Coords are in the range 0 to 1
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
1, 0,
0, 1,
0, 1,
1, 1,
1, 0
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
// texture
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
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.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// changed this as I thought you where indexing a 8bit array then saw you
// correctly index a 32 bit array. Id did not change the code back but yours was
// correct as well.
for (let i = 0; i < imageData.dataBuf8.length; i += 4) {
imageData.dataBuf8[i] = Math.random() * 255;; // red
imageData.dataBuf8[i + 1] = Math.random() * 255; // green
imageData.dataBuf8[i + 2] = Math.random() * 255; // blue
imageData.dataBuf8[i + 3] = Math.random() * 255; // alpha //
}
imageData.imageData.data.set(imageData.dataBuf8);
// bind texture and draw
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imageData.imageData);
gl.drawArrays(gl.TRIANGLES, 0, 6);
canvas {
border: 2px solid black;
}
<div id="container"></div>
I’m trying to figure out how to use gl.copyTexImage2D function.
I have the following code (unwieldy though):
//--------- SETUP (not important) ---------------
//I use budo package to easily run browserify
var createContext = require('webgl-context');
var createShader = require('gl-shader');
//↓ here is webgl setup usual routine, using floats and simple one-triangle vertex shader
var gl = createContext({width: 2, height: 2});
gl.getExtension('OES_texture_float');
gl.getExtension('OES_texture_float_linear');
var shader = createShader(gl, `
precision mediump float;
attribute vec2 position;
varying vec2 uv;
void main (void) {
gl_Position = vec4(position, 0, 1);
uv = vec2(position.x * 0.5 + 0.5, position.y * 0.5 + 0.5);
}
`, `
precision mediump float;
uniform sampler2D image;
varying vec2 uv;
void main (void) {
gl_FragColor = texture2D(image, uv);
}
`);
//fullscreen triangle
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, -1, 3, 3, -1]), gl.STATIC_DRAW);
shader.attributes.position.pointer();
//textures
var outTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, outTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
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.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.FLOAT, new Float32Array([1,1,1,1, 0,0,0,1, 0,0,0,1, 0,0,0,1]));
var sourceTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
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.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.FLOAT, new Float32Array([0,0,0,1, 1,1,1,1, 0,0,0,1, 0,0,0,1]));
//--------------- MAIN PART (important) ---------------
//then I setup custom framebuffer ↓
var framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, outTexture, 0);
gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.drawArrays(gl.TRIANGLES, 0, 3);
//here ↓ I am expecting to copy framebuffer’s output, which is `outTexture`, to `sourceTexture`
gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, w, h, 0);
//then I try to render shader again, with it’s own output as input
gl.drawArrays(gl.TRIANGLES, 0, 3);
//when I try to read pixels here ↓ I get the error below
gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, pixels);
The error: [.CommandBufferContext]GL ERROR :GL_INVALID_OPERATION : glCopyTexImage2D:
Cannot figure out what do I do wrong.
The error is from gl.copyTexImage2D not from gl.readPixels. The reason you don't see it until calling gl.readPixels is because WebGL is a command driven language. Commands are not executed until they have to be for various reasons. gl.flush will force the commands to be executed at some point. gl.readPixels also forces the commands to be executed since the results of the commands needed to be used to read the pixels.
As for the error you need to provide more code. The code as is works through gl.copyTexImage2D which means the error you're getting from that has to do with some code your not showing. Either you created your textures wrong or w and h or funky values or something
Trying it out myself below it works but pointed out another error. You can't read floats with gl.readPixels in WebGL. Switching to UNSIGNED_BYTE works
var gl = document.getElementById("c").getContext("webgl");
var w = 300;
var h = 150;
var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
gl.useProgram(programInfo.program);
var arrays = {
position: [-1, -1, 0, 1, -1, 0, -1, 1, 0, -1, 1, 0, 1, -1, 0, 1, 1, 0],
};
var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// make a renderable npot texture
function createRenderableTexture(gl, w, h) {
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
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);
return tex;
}
var outTexture = createRenderableTexture(gl, w, h);
var framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, outTexture, 0);
// render something to it
gl.clearColor(0,1,0,1); // green
gl.clear(gl.COLOR_BUFFER_BIT);
// copy the framebuffer to the texture
var sourceTexture = createRenderableTexture(gl, w, h)
gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, w, h, 0);
// draw to canvas
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// clear to red
gl.clearColor(1,0,0,1);
gl.clear(gl.COLOR_BUFFER_BIT);
// Since we cleared to red and the texture is filled with green
// the result should be green
gl.drawArrays(gl.TRIANGLES, 0, 6);
var pixels = new Uint8Array(w * h * 4);
gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
log("pixel 0: "
+ pixels[0] + ","
+ pixels[1] + ","
+ pixels[2] + ","
+ pixels[3]);
function log(msg) {
var div = document.createElement("pre");
div.appendChild(document.createTextNode(msg));
document.body.appendChild(div);
}
<script src="https://twgljs.org/dist/twgl.min.js"></script>
<script id="vs" type="notjs">
attribute vec4 position;
varying vec2 v_uv;
void main() {
gl_Position = position;
v_uv = position.xy * 0.5 + 0.5;
}
</script>
<script id="fs" type="notjs">
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_texture;
void main() {
gl_FragColor = texture2D(u_texture, v_uv);
}
</script>
<canvas id="c"></canvas>
On the other hand FLOAT textures should not work. They worked for me but as far as I can tell that's a bug on my system not yours.
Specifically FLOAT textures are not allowed by default in WebGL. You can turn them on by enabling the OES_texture_float extension but that extension specifically says
Should CopyTexImage2D, CopyTexSubImage{2D|3D} be supported for textures with half-float and float channels?
RESOLUTION: No.
I filed a bug for Chrome
You can try to work around that issue by implementing copyTexImage2D yourself. Basically attach the texture you want to copy as a source texture to some shader and quad. Attach the texture you want to copy to to a framebuffer object. Since it's a FLOAT texture you need to call checkFramebufferStatus and check it returns FRAMEBUFFER_COMPLETE to make sure your GPU/Driver actually supports floating point textures as attachments. Then draw your quad with a simple shader effectively copying your texture from src to dst.
I've been trying to learn some webgl, and basically have two programs where the rendered pixels get stored in a texture and then used in the other program's shader.
webgl.modTexture(gl, webgl.texture[1], webgl.frameBuffer[0]);
This will call and attempt to readpixels of a screen and put the modified data back into the texture to be used in the next program render routine.
All I get is a black screen becoming more transparent gradually, but there should be the u_image being seen also (since this is being rendered in the first program where the pixels are read with readpixels).
If I comment out the second program's rendering:
gl.useProgram(webgl.program);
gl.clearColor(0, 0.5, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 6);
Then it will just render u_image without any fading.
So I'm not sure what is the problem. Seems like maybe it isn't reading the pixels correctly? But then, why would the fading be working if that's the case? Is there something wrong with trying to use the two textures?
Hopefully someone can look at the code and see what might be the issue.
//webgl stuff
var webgl = new webglData();
function webglData(){
this.then = 0; //used for deltatime in rendering
this.request; //requestanimationframe, used when stopping/starting
this.canvas;
this.context;
this.div;
this.program;
this.cellProgram;
this.texture = [];
this.frameBuffer = [];
this.cellVShader = `
attribute vec2 aVertexPosition;
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
varying vec2 v_NW;
varying vec2 v_N;
varying vec2 v_NE;
varying vec2 v_W;
varying vec2 v_E;
varying vec2 v_SW;
varying vec2 v_S;
varying vec2 v_SE;
vec2 getOffset( int x, int y){
vec2 v = floor(a_texCoord * u_resolution);
v.x += float(x), v.y += float(y);
v /= u_resolution;
return v;
}
void main() {
//v_texCoord = a_texCoord;
//gl_Position = vec4(aVertexPosition, 0.0, 1.0);
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
v_NW = getOffset( -1, 1);
v_N = getOffset( 0, 1);
v_NE = getOffset( 1, 1);
v_W = getOffset( -1, 0);
v_E = getOffset( 1, 0);
v_SW = getOffset( -1, -1);
v_S = getOffset( 0, -1);
v_SE = getOffset( 1, -1);
}
`;
this.cellFShader = `
#ifdef GL_ES
precision highp float;
#endif
uniform sampler2D u_canvas1;
uniform vec4 uColor;
// our texture
uniform sampler2D u_before;
uniform sampler2D u_after;
uniform sampler2D u_image;
// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;
varying vec2 v_NW;
varying vec2 v_N;
varying vec2 v_NE;
varying vec2 v_W;
varying vec2 v_E;
varying vec2 v_SW;
varying vec2 v_S;
varying vec2 v_SE;
void main() {
// Look up a color from the texture.
gl_FragColor = texture2D(u_image, v_W);
//gl_FragColor = uColor;
}
`;
this.vertexShader = `
attribute vec2 aVertexPosition;
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
//v_texCoord = a_texCoord;
//gl_Position = vec4(aVertexPosition, 0.0, 1.0);
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}
`;
this.fragmentShader = `
#ifdef GL_ES
precision highp float;
#endif
uniform sampler2D u_canvas1;
uniform vec4 uColor;
// our texture
uniform sampler2D u_before;
uniform sampler2D u_after;
uniform sampler2D u_image;
// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;
void main() {
// Look up a color from the texture.
gl_FragColor = texture2D(u_before, v_texCoord);
//gl_FragColor = uColor;
}
`;
this.init = function(){
this.div = innerDoc.getElementById('webglDiv');
gui.Window.get().showDevTools();
this.canvas = document.createElement('canvas');
this.context = this.canvas.getContext("experimental-webgl");
this.canvas.width = 512;
this.canvas.height = 512;
this.canvas.style.position = 'absolute';
this.canvas.style.zIndex = -1;
this.canvas.style.pointerEvents = 'none';
this.div.appendChild(this.canvas);
if(!this.context)return;
var gl = this.context;
gl.viewport(0, 0, this.canvas.width, this.canvas.height);
gl.clearColor(0, 0.5, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
//compile the shaders and create the program for webgl
var vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vs, this.vertexShader);
gl.compileShader(vs);
var fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs, this.fragmentShader);
gl.compileShader(fs);
var cvs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(cvs, this.cellVShader);
gl.compileShader(cvs);
var cfs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(cfs, this.cellFShader);
gl.compileShader(cfs);
this.program = gl.createProgram();
gl.attachShader(this.program, vs);
gl.attachShader(this.program, fs);
gl.linkProgram(this.program);
//gl.useProgram(this.program);
this.cellProgram = gl.createProgram();
gl.attachShader(this.cellProgram, cvs);
gl.attachShader(this.cellProgram, cfs);
gl.linkProgram(this.cellProgram);
//gl.useProgram(this.cellProgram);
//output any errors
if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS))console.log(gl.getShaderInfoLog(vs));
if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS))console.log(gl.getShaderInfoLog(fs));
if (!gl.getProgramParameter(this.program, gl.LINK_STATUS))console.log(gl.getProgramInfoLog(this.program));
if (!gl.getShaderParameter(cvs, gl.COMPILE_STATUS))console.log(gl.getShaderInfoLog(cvs));
if (!gl.getShaderParameter(cfs, gl.COMPILE_STATUS))console.log(gl.getShaderInfoLog(cfs));
if (!gl.getProgramParameter(this.cellProgram, gl.LINK_STATUS))console.log(gl.getProgramInfoLog(this.cellProgram));
this.setupStuff(gl, this.program);
this.setupStuff(gl, this.cellProgram);
this.texture.push( this.setupTexture(tool.canvas, 0, "u_image") );
this.texture.push( this.createBlankTexture(gl, tool.canvas.width*tool.canvas.height*4, gl.RGBA, tool.canvas.width, tool.canvas.height, "u_before") );
this.texture.push( this.createBlankTexture(gl, tool.canvas.width*tool.canvas.height*4, gl.RGBA, tool.canvas.width, tool.canvas.height, "u_after") );
this.frameBuffer.push( gl.createFramebuffer() );
this.request = requestAnimationFrame(this.render);
}
this.render = function(now){
if(!webgl.context || config.tab!='scene'){cancelAnimationFrame(webgl.request); return;}
var gl = webgl.context;
// Convert the time to seconds
now *= 0.001;
// Subtract the previous time from the current time
var deltaTime = now - webgl.then;
// Remember the current time for the next frame.
webgl.then = now;
gl.useProgram(webgl.cellProgram);
gl.drawArrays(gl.TRIANGLES, 0, 6);
webgl.modTexture(gl, webgl.texture[1], webgl.frameBuffer[0]);
gl.useProgram(webgl.program);
gl.clearColor(0, 0.5, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 6);
this.request = requestAnimationFrame(webgl.render);
}
this.setupStuff = function(gl, program){
gl.useProgram(program);
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
var texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
// provide texture coordinates for the rectangle.
//this will be what the texture gets displayed on?
var texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
// lookup uniforms
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
// set the resolution
gl.uniform2f(resolutionLocation, this.canvas.width, this.canvas.height);
// Create a buffer for the position of the rectangle corners.
// store the data for the texture coordinates that were defined above, into the a_position?
var buffer = gl.createBuffer();
//this.frameBuffer.push(buffer);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
// Set a rectangle the same size as the image.
//I guess this adjusts the buffer data that was just created?
this.setRectangle(gl, 0, 0, this.canvas.width, this.canvas.height);
//var tex2 = setupTexture(canvas2, 1, program, "u_canvas2");
// Draw the rectangle.
//gl.drawArrays(gl.TRIANGLES, 0, 6);
//gl.drawArrays(gl.TRIANGLES, 0, numItems);
}
this.refreshTexture = function(){
if(!this.context)return;
var gl = this.context;
gl.activeTexture(gl.TEXTURE0 + 0);
gl.bindTexture(gl.TEXTURE_2D, this.texture[0]);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, tool.canvas);
//gl.drawArrays(gl.TRIANGLES, 0, 6);
}
this.modTexture = function(gl, sTexture, framebuffer){
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, sTexture, 0);
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE)
{
var sTextureSize = sTexture.width * sTexture.height * 4; // r, g, b, a
var pixels = new Uint8Array( sTextureSize );
gl.readPixels( 0, 0, sTexture.width, sTexture.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels );
for( var i=0 ; i<sTextureSize ; i+=4 )
{
if( pixels[i+3] > 0 )
{
pixels[i+3] = Math.min( 255, pixels[i+3]*0.995 ); // set half alpha
}
}
// upload changes
gl.activeTexture(gl.TEXTURE0 + 1);
gl.bindTexture(gl.TEXTURE_2D, sTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
sTexture.width, sTexture.height, 0,
gl.RGBA, gl.UNSIGNED_BYTE, pixels);
}
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
this.setupFrameBuffer = function(canvas, textureUnit, program, uniformName) {
if(!this.context)return;
var gl = this.context;
var rttFramebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, rttFramebuffer);
rttFramebuffer.width = this.canvas.width;
rttFramebuffer.height = this.canvas.height;
var rttTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, rttTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
gl.generateMipmap(gl.TEXTURE_2D);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, rttFramebuffer.width, rttFramebuffer.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, rttTexture, 0);
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
return rttFrameBuffer;
}
this.setupTexture = function(canvas, textureUnit, uniformName) {
var gl = this.context;
var tex = gl.createTexture();
this.updateTextureFromCanvas(tex, canvas, textureUnit);
tex.width = canvas.width;
tex.height= canvas.height;
// Set the parameters so we can render any size image.
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.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.useProgram(this.program);
var location = gl.getUniformLocation(this.program, uniformName);
gl.uniform1i(location, textureUnit);
gl.useProgram(this.cellProgram);
location = gl.getUniformLocation(this.cellProgram, uniformName);
gl.uniform1i(location, textureUnit);
return tex;
}
this.updateTextureFromCanvas = function(tex, canvas, textureUnit) {
if(!this.context)return;
var gl = this.context;
gl.activeTexture(gl.TEXTURE0 + textureUnit);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
}
this.createBlankTexture = function(gl, dataArray, type, width, height, uniformName) {
var dataTypedArray = new Uint8Array(dataArray); // Don't need to do this if the data is already in a typed array
for( var i=0 ; i<dataArray ; i+=4 )
{
dataTypedArray[i+3] = 255;
}
var texture = gl.createTexture();
texture.width = width;
texture.height= height;
gl.activeTexture(gl.TEXTURE0 + this.texture.length);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, type, width, height, 0, type, gl.UNSIGNED_BYTE, dataTypedArray);
// Other texture setup here, like filter modes and mipmap generation
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.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.useProgram(this.program);
var location = gl.getUniformLocation(this.program, uniformName);
gl.uniform1i(location, this.texture.length);
gl.useProgram(this.cellProgram);
location = gl.getUniformLocation(this.cellProgram, uniformName);
gl.uniform1i(location, this.texture.length);
return texture;
}
this.setRectangle = function(gl, x, y, width, height) {
var x1 = x;
var x2 = x + width;
var y1 = y;
var y2 = y + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2]), gl.STATIC_DRAW);
}
}
Okay I managed to get things working.
I had to learn what exactly needs to be called at different stages:
Do I have to create separate buffers per webgl program?
And I had to change the part with the framebuffer to use a different texture than the one it is putting the data into.
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, sTexture, 0);
gl.activeTexture(gl.TEXTURE0 + 1);
gl.bindTexture(gl.TEXTURE_2D, sTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
sTexture.width, sTexture.height, 0,
gl.RGBA, gl.UNSIGNED_BYTE, pixels);
so the sTexture needs to change like so:
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, someOtherTexture, 0);
I am using webgl and what I want to do is first render to a texture, then use that texture to render on screen, and I encounter a problem with an attribute in the first step render.
I will try to explain in a few words what I am trying to do. First I want to use a fragment shader to render to a texture something that uses an attribute, then use that texture to render to screen, and repeat the steps for the next frame. But when I try to render with the framebuffer bound that attribute (pos) has invalid values, I mean, no value at all.
I wrote a little demo that shows my problem:
var canvas = document.getElementById("c");
var gl = getWebGLContext(canvas);
var program_init, program_pers, tex, fb;
function init() {
var verts = [
1, 1,
-1, 1,
-1, -1,
1, 1,
-1, -1,
1, -1,
];
var vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(0);
tex = gl.createTexture();
fb = gl.createFramebuffer();
program_init = createProgramFromScripts(gl, ["vshader-init", "fshader-init"], ["pos"]);
program_pers = createProgramFromScripts(gl, ["vshader-pers", "fshader-pers"], ["a_position"]);
}
function renderToTexture(gl, time) {
gl.useProgram(program_init);
var timeLocation = gl.getUniformLocation(program_init, 'time');
gl.uniform1f(timeLocation, time);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
};
function renderToScreen(gl) {
gl.useProgram(program_pers);
gl.drawArrays(gl.TRIANGLES, 0, 6);
};
init();
requestAnimationFrame(function(){
renderToTexture(gl, arguments[0]);
renderToScreen(gl);
requestAnimationFrame( arguments.callee );
});
canvas { border: 1px solid black; }
body { background-color: darkslategrey }
<script src="http://greggman.github.com/webgl-fundamentals/webgl/resources/webgl-utils.js"></script>
<script id="vshader-init" type="vs/shader">
attribute vec4 pos;
varying vec2 uv;
void main() {
gl_Position = pos;
uv = pos.xy * .5 + .5;
}
</script>
<script id="fshader-init" type="fs/shader">
precision mediump float;
varying vec2 uv;
uniform float time;
void main() {
float t = floor(time / 1000.);
vec3 color;
color.x = sin(t*uv.x);
color.y = tan(t*uv.y);
color.z = cos(t);
gl_FragColor = vec4(color, 1.);
}
</script>
<script id="vshader-pers" type="vs/shader">
attribute vec4 a_position;
varying vec2 v_texcoord;
void main() {
gl_Position = a_position;
v_texcoord = a_position.xy * .5 + .5;
}
</script>
<script id="fshader-pers" type="fs/sahder">
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D u_sampler;
void main() {
gl_FragColor = texture2D(u_sampler, v_texcoord);
}
</script>
<body>
<canvas id="c" width="400" height="400"></canvas>
</body>
If I comment the line 38,39 from the javascript code (the binding of the frameBuffer) and line 54 (the rendering to screen code, that runs a different program) we can see that it renders corectly and the name "pos" is given the right values.
var canvas = document.getElementById("c");
var gl = getWebGLContext(canvas);
var program_init, program_pers, tex, fb;
function init() {
var verts = [
1, 1,
-1, 1,
-1, -1,
1, 1,
-1, -1,
1, -1,
];
var vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(0);
tex = gl.createTexture();
fb = gl.createFramebuffer();
program_init = createProgramFromScripts(gl, ["vshader-init", "fshader-init"], ["pos"]);
program_pers = createProgramFromScripts(gl, ["vshader-pers", "fshader-pers"], ["a_position"]);
}
function renderToTexture(gl, time) {
gl.useProgram(program_init);
var timeLocation = gl.getUniformLocation(program_init, 'time');
gl.uniform1f(timeLocation, time);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
//gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
//gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
};
function renderToScreen(gl) {
gl.useProgram(program_pers);
gl.drawArrays(gl.TRIANGLES, 0, 6);
};
init();
requestAnimationFrame(function(){
renderToTexture(gl, arguments[0]);
//renderToScreen(gl);
requestAnimationFrame( arguments.callee );
});
canvas { border: 1px solid black; }
body { background-color: darkslategrey }
<script src="http://greggman.github.com/webgl-fundamentals/webgl/resources/webgl-utils.js"></script>
<script id="vshader-init" type="vs/shader">
attribute vec4 pos;
varying vec2 uv;
void main() {
gl_Position = pos;
uv = pos.xy * .5 + .5;
}
</script>
<script id="fshader-init" type="fs/shader">
precision mediump float;
varying vec2 uv;
uniform float time;
void main() {
float t = floor(time / 1000.);
vec3 color;
color.x = sin(t*uv.x);
color.y = tan(t*uv.y);
color.z = cos(t);
gl_FragColor = vec4(color, 1.);
}
</script>
<script id="vshader-pers" type="vs/shader">
attribute vec4 a_position;
varying vec2 v_texcoord;
void main() {
gl_Position = a_position;
v_texcoord = a_position.xy * .5 + .5;
}
</script>
<script id="fshader-pers" type="fs/shader">
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D u_sampler;
void main() {
gl_FragColor = texture2D(u_sampler, v_texcoord);
}
</script>
<body>
<canvas id="c" width="400" height="400"></canvas>
</body>
I do not know much about how it is supposed to work and I am a bit into the dark. I am sure I'm missing something important, but I can't find anywhere what. Any help will be appreciated.
Your framebuffer texture is 1x1 pixels big. What do you expect to see? You're rendering a single pixel.
Also note that unless your framebuffer is the same size as the canvas you'll want to call gl.viewport and set it to the size of the thing you rendering after each call to gl.bindFramebuffer
You probably also want to not create the texture every frame. Here's your code with those things changed.
var canvas = document.getElementById("c");
var gl = canvas.getContext("webgl");
var program_init, program_pers, tex, fb;
var fbWidth = 400;
var fbHeight = 300;
function init() {
var verts = [
1, 1,
-1, 1,
-1, -1,
1, 1,
-1, -1,
1, -1,
];
var vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(0);
tex = gl.createTexture();
fb = gl.createFramebuffer();
program_init = webglUtils.createProgramFromScripts(gl, ["vshader-init", "fshader-init"], ["pos"]);
program_pers = webglUtils.createProgramFromScripts(gl, ["vshader-pers", "fshader-pers"], ["a_position"]);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, fbWidth, fbHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
// make non power-of-2 texture renderable
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.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
}
function renderToTexture(gl, time) {
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.viewport(0, 0, fbWidth, fbHeight);
gl.useProgram(program_init);
var timeLocation = gl.getUniformLocation(program_init, 'time');
gl.uniform1f(timeLocation, time);
gl.drawArrays(gl.TRIANGLES, 0, 6);
};
function renderToScreen(gl) {
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.useProgram(program_pers);
gl.drawArrays(gl.TRIANGLES, 0, 6);
};
init();
requestAnimationFrame(function(){
renderToTexture(gl, arguments[0]);
renderToScreen(gl);
requestAnimationFrame( arguments.callee );
});
canvas { border: 1px solid black; }
body { background-color: darkslategrey }
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<script id="vshader-init" type="vs/shader">
attribute vec4 pos;
varying vec2 uv;
void main() {
gl_Position = pos;
uv = pos.xy * .5 + .5;
}
</script>
<script id="fshader-init" type="fs/shader">
precision mediump float;
varying vec2 uv;
uniform float time;
void main() {
float t = floor(time / 1000.);
vec3 color;
color.x = sin(t*uv.x);
color.y = tan(t*uv.y);
color.z = cos(t);
gl_FragColor = vec4(color, 1.);
}
</script>
<script id="vshader-pers" type="vs/shader">
attribute vec4 a_position;
varying vec2 v_texcoord;
void main() {
gl_Position = a_position;
v_texcoord = a_position.xy * .5 + .5;
}
</script>
<script id="fshader-pers" type="fs/sahder">
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D u_sampler;
void main() {
gl_FragColor = texture2D(u_sampler, v_texcoord);
}
</script>
<body>
<canvas id="c" width="400" height="400"></canvas>
</body>
I wrote a code that tried to draw only the background image in a WebGL 3D scene. Here are the relevant parts of the code:
<script id="shader-fs" type="x-shader/x-fragment">
varying highp vec2 vTexCoord;
uniform sampler2D uSampler;
void main(void) {
gl_FragColor = texture2D(uSampler, vec2(vTexCoord.s, vTexCoord.t));
}
</script>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
varying highp vec2 vTexCoord;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vTexCoord = aTextureCoord;
}
</script>
<script>
var VBack = [
-24.0, 0.0, 0.0,
24.0, 0.0, 0.0,
24.0, 48.0, 0.0,
-24.0, 48.0, 0.0
];
var vTBack = [
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0
];
var VBuffer;
var vTBuffer;
function initMatrix() {
orthoMatrix = makeOrtho(-24.0, 24.0, 0.0, 48.0, 20.0, -20.0); // From glUtils.js
mvMatrix = Matrix.I(4);
...
}
function handleBkTex(tex) {
GL.bindTexture(GL.TEXTURE_2D, tex);
GL.pixelStorei(GL.UNPACK_FLIP_Y_WEBGL, true);
GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, tex.Img);
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);
}
function initBkgnd() {
backTex = GL.createTexture();
backTex.Img = new Image();
backTex.Img.onload = function() {
handleBkTex(backTex);
}
backTex.Img.src = "Bkgnd.jpg";
}
function drawBkgnd() {
GL.bindBuffer(GL.ARRAY_BUFFER, VBuffer);
GL.vertexAttribPointer(vertexPositionAttribute, 3, GL.FLOAT, false, 0, 0);
GL.bindBuffer(GL.ARRAY_BUFFER, vTBuffer);
GL.vertexAttribPointer(texCoordAttribute, 2, GL.FLOAT, false, 0, 0);
GL.activeTexture(GL.TEXTURE0);
GL.bindTexture(GL.TEXTURE_2D, backTex);
GL.uniform1i(GL.getUniformLocation(shaderProgram, "uSampler"), 0);
GL.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
}
function start() {
var canvas = document.getElementById("glcanvas");
initWebGL(canvas);
if (GL) {
initShaders();
initBuffers();
initMatrix();
initBkgnd();
drawBkgnd();
}
}
The canvas size is 512 x 512, the same as the image size. But I don't get the correct image on the canvas. How to do this correctly?
Do you want the answer to be how to setup a 3D matrix to draw a 2D image or do you want the answer to be how to do this efficiently?
Here's the efficient answer. There's no reason to use 3D to draw 2D. Change your vertices to
var VBack = [
-1, -1,
1, -1,
1, 1,
-1, 1,
];
Change your call to vertexAttribPointer to
GL.vertexAttribPointer(vertexPositionAttribute, 2, GL.FLOAT, false, 0, 0);
Change your vertex shader to
attribute vec4 aVertexPosition;
attribute vec2 aTextureCoord;
varying highp vec2 vTexCoord;
void main(void) {
gl_Position = aVertexPosition;
vTexCoord = aTextureCoord;
}
And it should work.
See this set of articles for more about 2D in WebGL for more.
I found some small errors in my code. Here are they:
WebGL scene must always be redrawn. So the function drawBkgnd() must be called repeatedly, using setInterval() or requestAnimationFrame().
To use drawArrays() with GL_TRIANGLE_STRIP to draw a rectangle correctly, the order of the vertices must be:
*2 *3
*0 *1
or any equivalent order, but not:
*3 *2
*0 *1
So the order of the vertices in VBack must be changed to:
var VBack = [
-24.0, 0.0, 0.0,
24.0, 0.0, 0.0,
-24.0, 48.0, 0.0
24.0, 48.0, 0.0,
];
and the order of vertices in vTBack must also be changed accordingly.