HTML5與WebGL程式設計(1):介紹

CopperDong發表於2018-04-15

      程式碼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>

相關文章