Oh, and by the way, there's a Google Wave embedded down by the comments for those who would like to use that.
Here's a some of the new features
- Keyframe animation
- Image textures
- Video textures (only Firefox 3.7a has support for them at the moment)
- Can load shaders from .vp, .vert, .fp, and .frag files
- Partial .obj model file loading (only vertices for now)
360 degree interactive video has been around in some form or another for over a decade, but always through a plugin of some kind, from RealPlayer to Flash and many others. Here, it's just the browser, some HTML, and some JavaScript.
Here's the demo, you'll need Firefox 3.7 installed, updated, and have webgl.enable_for_all_sites set to true in the about:config page.
Click and drag to pan the camera
Here's a bigger version and a wireframe version, which shows the 1600 triangles used to form the sphere. You can change the width and height parameters in the url to see it at other sizes.
Here's a video of the same demo
Using Firebug's profiling capabilities I was able to determine that 90% of the running time is dedicated to just four lines of code. Namely, the four that get called every time the video's timer changes.
bindTexture(TEXTURE_2D, this.texture.texture);
texImage2D(TEXTURE_2D, 0, this.texture.video, false);
texParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST);
texParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, NEAREST);
Thanks to WebGLU and GameGLU, this was pretty easy to put together. Now for how I actually did it.
Two simple shaders drive the rendering.
texture.vert
attribute vec3 vertex;attribute vec3 color;
attribute vec2 texCoord;
uniform mat4 ProjectionMatrix;
uniform mat4 ModelViewMatrix;
varying vec2 vTexCoord;
void main(void) {
vTexCoord = texCoord;
gl_Position = ProjectionMatrix * ModelViewMatrix * vec4(vertex, 1.0);
}
texture.frag
uniform sampler2D sampler;varying vec2 vTexCoord;
void main(void) {
gl_FragColor = texture2D(sampler, vTexCoord);
}
Creating the shader program is just three lines.
$W.newProgram('textured');
$W.programs['textured'].attachShader('texVS', 'texture.vert');
$W.programs['textured'].attachShader('texFS', 'texture.frag');
Creating the sphere and applying the video texture to it are not all that much more.
// Create a new renderable object
var sphere = new $W.Object($W.GL.TRIANGLES);
// Have it use the shader program we created above
sphere.setShaderProgram('textured');
// Create a texture from the video tag in the page
new $W.texture.Video('video', 'videoElement');
// Apply the texture to the sphere
sphere.textures.push('video');
// Generate a unit sphere with 40 horizontal rings and 40
// vertical slices
var sphereModel = $W.util.genSphere(40,40);
// fill the data arrays, which correspond to the attributes
// of the same name in the shader.
sphere.fillArray("vertex", sphereModel.vertices);
sphere.fillArray("normal", sphereModel.normals);
sphere.fillArray("texCoord", sphereModel.texCoords);
// Use drawElements when rendering
sphere.setElements(sphereModel.indices);
The new GameGLU companion library makes it simple to respond to key and mouse events without actually dealing with the event listeners directly by providing $G.event.bind('keyname', action) where action is a function to perform when the event occurs. Appending + or - to keyname binds to the keydown or keyup respectively, keydown is assumed if you omit them.
var st = $G.state;
// Initialize the state variables we'll use
st.mouseDown = false;
st.wasMouseDown = false;
st.lastM = $V([0,0]);
st.posM = $V([0,0]);
st.deltaM = $V([0,0]);
st.totalM = $V([Math.PI * 100,0]);
// mouseup event
$G.event.bind('-m1', function(x,y) {
st.mouseDown = false;
});
// mousedown event
$G.event.bind('+m1', function(x,y) {
st.mouseDown = true;
st.lastM.elements = [x, y];
st.posM.elements = [x,y];
});
// mousemove event
$G.event.bind('mousemove', function(x,y) {
st.deltaM.elements = [0,0];
// When the mouse is moved while clicking
if (st.mouseDown) {
// avoid jumping around when we click then release,
// move the mouse, then click again.
if (st.wasMouseDown) {
st.lastM.elements = st.posM.elements;
}
st.posM.elements = [x, y];
st.deltaM = st.posM.subtract(st.lastM);
st.totalM = st.totalM.add(st.deltaM);
}
st.wasMouseDown = st.mouseDown;
});
// Rotate the camera based on the mouse movement
$W.camera.update = function() {
var x = -1 * st.totalM.e(1) / 100;
var y = st.totalM.e(2) / 10;
this.target.elements = [Math.cos(x), y / 10, Math.sin(x)];
}
Hey Benjamin -- this looks really promising. For some reason, though, I just get a static image rather than a video -- the panning works, but the viewpoint isn't moving down the street. I'm using Minefield, ATI Mobility Radeon 4670, Windows 7. Any thoughts?
ReplyDeleteThe video will appear static if it buffers after it begins, just give it a moment.
ReplyDelete