代码https://github.com/tparisi/Programming3DApplications
HTML5是HTML标准的大规模修订,包括语法的清理、新的JavaScript语言特性和API、移动端支持以及突破性的多媒体支持。HTML5平台的核心是一系列先进的图形技术。
- WebGL,使得JavaScript支持硬件3D加速渲染。WebGL基于OpenGL,几乎所有的PC端浏览器都支持WebGL,而越来越多的移动端浏览器也开始支持WebGL。
- CSS3 3D变换、平移以及可以支持更高级页面效果的用户自定义滤镜。经过过去几年的发展,CSS现在已经支持硬件3D加速渲染和动画。
- Canvas元素和相应的2D绘图API。浏览器普遍支持这个JavaScript的API,它使得开发者可以在一个DOM元素上绘制任意图形。尽管Canvas是一个2D绘图API,但如果使用一些JavaScript库的话,它也可以用于渲染3D效果---在不支持WebGL和CSS3 3D的平台上,这通常被作为3D渲染的替代解决方案。
这些技术各有其优势和适用场景,它们在构建3D可视化交互的过程中发挥着各自的作用。至于究竟要选择哪一种,你需要综合考虑多方面的因素---你想要构建什么,需要支持哪些平台,性能问题,等等。举例来说,如果你开发一个具有高质量图像的第一人称射击游戏,若不借助WebGL访问图形硬件的能力,这将很难实现。又或者你在为某个视频网站开发一个有趣的调台器界面,包括当前视频缩略图、换频道的旋转效果,以及视频剪辑切换间隙的雪花噪点特效。在这个场景下,CSS3会是创造优秀体验的良好选择。
requestAnimationFrame()函数是替代setInterval()和setTimeout()来驱动动画,使得开发这能够以和浏览器刷新页面元素同步的刷新频率来更新canvas元素的绘图内容,这大大地提升了性能,并防止了绘图残影的产生。
HTML5浏览器同时也支持多线程编程(Web Workers)、全双工TCP/IP通信(WebSockets)、本地数据存储等新特性。
3D坐标系
网格、多边形和顶点
材质、纹理与光源
变换与矩阵
相机、透视、视口与投影
着色器
<html>
<head>
<title>Programming 3D Applications in HTML5 and WebGL — Example 2-3</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<script src="../libs/jquery-1.9.1/jquery-1.9.1.js"></script>
<script src="../libs/gl-matrix/gl-matrix.js"></script>
<script src="../libs/requestAnimationFrame/RequestAnimationFrame.js"></script>
<script type="text/javascript">
function initWebGL(canvas) {
var gl = null;
var msg = "Your browser does not support WebGL, " +
"or it is not enabled by default.";
try
{
gl = canvas.getContext("experimental-webgl");
}
catch (e)
{
msg = "Error creating WebGL Context!: " + e.toString();
}
if (!gl)
{
alert(msg);
throw new Error(msg);
}
return gl;
}
function initViewport(gl, canvas)
{
gl.viewport(0, 0, canvas.width, canvas.height);
}
var projectionMatrix, modelViewMatrix;
var rotationAxis;
function initMatrices(canvas)
{
// Create a model view matrix with object at 0, 0, -8
modelViewMatrix = mat4.create();
mat4.translate(modelViewMatrix, modelViewMatrix, [0, 0, -8]);
// Create a project matrix with 45 degree field of view
projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, Math.PI / 4, canvas.width / canvas.height, 1, 10000);
rotationAxis = vec3.create();
vec3.normalize(rotationAxis, [1, 1, 1]);
}
// Create the vertex, color and index data for a multi-colored cube
function createCube(gl) {
// Vertex Data
var vertexBuffer;
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
var verts = [
// Front face
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
// Back face
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
// Top face
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
// Bottom face
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
// Right face
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
// Left face
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0, -1.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
var texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
var textureCoords = [
// Front face
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
// Back face
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
// Top face
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
// Bottom face
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,
// Right face
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
// Left 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);
// Index data (defines the triangles to be drawn)
var cubeIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeIndexBuffer);
var cubeIndices = [
0, 1, 2, 0, 2, 3, // Front face
4, 5, 6, 4, 6, 7, // Back face
8, 9, 10, 8, 10, 11, // Top face
12, 13, 14, 12, 14, 15, // Bottom face
16, 17, 18, 16, 18, 19, // Right face
20, 21, 22, 20, 22, 23 // Left face
];
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeIndices), gl.STATIC_DRAW);
var cube = {buffer:vertexBuffer, texCoordBuffer:texCoordBuffer, indices:cubeIndexBuffer,
vertSize:3, nVerts:24, texCoordSize:2, nTexCoords: 24, nIndices:36,
primtype:gl.TRIANGLES};
return cube;
}
function createShader(gl, str, type) {
var shader;
if (type == "fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (type == "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 vertexShaderSource =
" attribute vec3 vertexPos;\n" +
" attribute vec2 texCoord;\n" +
" uniform mat4 modelViewMatrix;\n" +
" uniform mat4 projectionMatrix;\n" +
" varying vec2 vTexCoord;\n" +
" void main(void) {\n" +
" // Return the transformed and projected vertex value\n" +
" gl_Position = projectionMatrix * modelViewMatrix * \n" +
" vec4(vertexPos, 1.0);\n" +
" // Output the texture coordinate in vTexCoord\n" +
" vTexCoord = texCoord;\n" +
" }\n";
var fragmentShaderSource =
" precision mediump float;\n" +
" varying vec2 vTexCoord;\n" +
" uniform sampler2D uSampler;\n" +
" void main(void) {\n" +
" // Return the pixel color: always output white\n" +
" gl_FragColor = texture2D(uSampler, vec2(vTexCoord.s, vTexCoord.t));\n" +
"}\n";
var shaderProgram, shaderVertexPositionAttribute, shaderVertexColorAttribute,
shaderProjectionMatrixUniform, shaderModelViewMatrixUniform, shaderSamplerUniform;
function initShader(gl) {
// load and compile the fragment and vertex shader
//var fragmentShader = getShader(gl, "fragmentShader");
//var vertexShader = getShader(gl, "vertexShader");
var fragmentShader = createShader(gl, fragmentShaderSource, "fragment");
var vertexShader = createShader(gl, vertexShaderSource, "vertex");
// link them together into a new program
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
// get pointers to the shader params
shaderVertexPositionAttribute = gl.getAttribLocation(shaderProgram, "vertexPos");
gl.enableVertexAttribArray(shaderVertexPositionAttribute);
shaderTexCoordAttribute = gl.getAttribLocation(shaderProgram, "texCoord");
gl.enableVertexAttribArray(shaderTexCoordAttribute);
shaderProjectionMatrixUniform = gl.getUniformLocation(shaderProgram, "projectionMatrix");
shaderModelViewMatrixUniform = gl.getUniformLocation(shaderProgram, "modelViewMatrix");
shaderSamplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Could not initialise shaders");
}
}
var okToRun = false;
function handleTextureLoaded(gl, texture) {
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, texture.image);
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);
okToRun = true;
}
var webGLTexture;
function initTexture(gl) {
webGLTexture = gl.createTexture();
webGLTexture.image = new Image();
webGLTexture.image.onload = function () {
handleTextureLoaded(gl, webGLTexture)
}
webGLTexture.image.crossOrigin = "anonymous";
webGLTexture.image.src = "../images/webgl-logo-256.jpg";
}
function draw(gl, obj) {
// clear the background (with black)
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// set the shader to use
gl.useProgram(shaderProgram);
// connect up the shader parameters: vertex position, texture coordinate,
// projection/model matrices and texture
// set up the buffers
gl.bindBuffer(gl.ARRAY_BUFFER, obj.buffer);
gl.vertexAttribPointer(shaderVertexPositionAttribute, obj.vertSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, obj.texCoordBuffer);
gl.vertexAttribPointer(shaderTexCoordAttribute, obj.texCoordSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.indices);
gl.uniformMatrix4fv(shaderProjectionMatrixUniform, false, projectionMatrix);
gl.uniformMatrix4fv(shaderModelViewMatrixUniform, false, modelViewMatrix);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, webGLTexture);
gl.uniform1i(shaderSamplerUniform, 0);
// draw the object
gl.drawElements(obj.primtype, obj.nIndices, gl.UNSIGNED_SHORT, 0);
}
var duration = 5000; // ms
var currentTime = Date.now();
function animate() {
var now = Date.now();
var deltat = now - currentTime;
currentTime = now;
var fract = deltat / duration;
var angle = Math.PI * 2 * fract;
mat4.rotate(modelViewMatrix, modelViewMatrix, angle, rotationAxis);
}
function run(gl, cube) {
requestAnimationFrame(function() { run(gl, cube); });
if (okToRun)
{
draw(gl, cube);
animate();
}
}
$(document).ready(
function() {
var canvas = document.getElementById("webglcanvas");
var gl = initWebGL(canvas);
initViewport(gl, canvas);
initMatrices(canvas);
var cube = createCube(gl);
initShader(gl);
initTexture(gl);
run(gl, cube);
}
);
</script>
</head>
<body>
<canvas id="webglcanvas" style="border: none;" width="500" height="500"></canvas>
</body>
</html>