Source

WebGL Beginner's Guide - Code / 1727_10 / ch10_PointSprites.html

Full commit
<html>

<head>
<title>WebGL Beginner's Guide - Chapter 10 - Point Sprites</title>
<meta http-equiv='content-type' content='text/html; charset=ISO-8859-1'>

<!-- CSS Styles //-->
<link href='css/styles.css'   type='text/css' rel='stylesheet'>
<link href='css/prettify_desert.css'  type='text/css' rel='stylesheet'/>
<link href='css/colorpicker.css'  type='text/css' rel='stylesheet'/>

<link href='css/smoothness/jquery-ui-1.8.13.custom.css' type='text/css' rel='stylesheet' />
<!-- GUI Libraries //-->
<script type='text/javascript' src='js/gui/jquery-1.5.1.min.js'></script>
<script type='text/javascript' src='js/gui/jquery-ui-1.8.13.custom.min.js'></script> 
<script type='text/javascript' src='js/gui/colorpicker.js'></script>
<script type='text/javascript' src='js/gui/prettify.js'></script>
<script type='text/javascript' src='js/gui/codeview.js'></script>
<!-- MATH Libraries //-->
<script type='text/javascript' src='js/math/gl-matrix-min.js'></script>
<!-- WEBGL Libraries //-->
<script type='text/javascript' src='js/webgl/Globals.js'></script>
<script type='text/javascript' src='js/webgl/Utils.js'></script>
<script type='text/javascript' src='js/webgl/Program.js'></script>
<script type='text/javascript' src='js/webgl/Scene.js'></script>
<script type='text/javascript' src='js/webgl/Axis.js'></script>
<script type='text/javascript' src='js/webgl/Floor.js'></script>
<script type='text/javascript' src='js/webgl/Camera.js'></script>
<script type='text/javascript' src='js/webgl/CameraInteractor.js'></script>
<script type='text/javascript' src='js/webgl/SceneTransforms.js'></script>
<script type='text/javascript' src='js/webgl/Texture.js'></script>
<script type='text/javascript' src='js/webgl/WebGLApp.js'></script>

<script id="shader-vs" type="x-shader/x-vertex">
attribute vec4 aParticle;

uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform float uPointSize;

varying float vLifespan;

void main(void) {
    gl_Position = uPMatrix * uMVMatrix * vec4(aParticle.xyz, 1.0);
    vLifespan = aParticle.w;
    gl_PointSize = uPointSize * vLifespan;
}
</script>

<script id="shader-fs" type="x-shader/x-fragment">
precision highp float;

uniform sampler2D uSampler;

varying float vLifespan;

void main(void) {
    vec4 texColor = texture2D(uSampler, gl_PointCoord);
    //if (texColor.a == 0.) discard;
    gl_FragColor = vec4(texColor.rgb, texColor.a * vLifespan);
}
</script>

<script id='code-js' type="text/javascript">

var camera      = null;
var interactor  = null;
var transforms  = null;
var spriteTexture = null;
var particles = [];
var particleArray = null;
var particleBuffer = null;

var particleSize = 14.0;
var particleLifespan = 3.0;

var lastFrameTime = 0.0;

function configure(){
    gl.clearColor(0.3,0.3,0.3, 1.0);
    gl.clearDepth(100.0);
    gl.disable(gl.DEPTH_TEST);
    gl.depthFunc(gl.LESS);
    gl.blendFunc(gl.SRC_ALPHA,gl.ONE);
    
    //Creates and sets up the camera location
    camera = new Camera(CAMERA_ORBITING_TYPE);
    camera.goHome([0,0,40]);
    camera.setFocus([0.0,0.0,0.0]);
    camera.setElevation(-40);
    camera.setAzimuth(-30);
    camera.hookRenderer = render;
    
    var canvas  = document.getElementById('canvas-element-id');

    // Texture to use for the point sprite
    spriteTexture = new Texture();
    spriteTexture.setImage("textures/spark.png");
    
    //Creates and sets up the mouse and keyboard interactor
    interactor = new CameraInteractor(camera, canvas);
        
    //Scene Transforms
    transforms = new SceneTransforms(camera);
   
    //init transforms
    transforms.init();
    
    //Program
    attributeList = ["aParticle"];

    uniformList = [ "uPMatrix", 
                    "uMVMatrix", 
                    "uPointSize",
                    "uSampler",
                    ];
    
    Program.load(attributeList, uniformList);

    configureParticles(1024);
}

function resetParticle(p) {
    p.pos = [0.0, 0.0, 0.0];

    p.vel = [
        (Math.random() * 20.0) - 10.0,
        (Math.random() * 20.0),
        (Math.random() * 20.0) - 10.0,
    ];

    p.lifespan = (Math.random() * particleLifespan);
    p.remainingLife = p.lifespan;
}

function configureParticles(count) {
    var i, p;

    particleArray = new Float32Array(count * 4);

    for(i = 0; i < count; ++i) {
        p = {};
        resetParticle(p);
        particles.push(p);

        particleArray[(i*4) + 0] = p.pos[0];
        particleArray[(i*4) + 1] = p.pos[1];
        particleArray[(i*4) + 2] = p.pos[2];
        particleArray[(i*4) + 3] = p.remainingLife / p.lifespan;
    }

    particleBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, particleBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, particleArray, gl.STATIC_DRAW);
    gl.bindBuffer(gl.ARRAY_BUFFER, null);
}

function updateParticles(elapsed) {
    var i, p, count = particles.length;

    // Loop through all the particles in the array
    for(i = 0; i < count; ++i) {
        p = particles[i];

        // Track the particles lifespan
        p.remainingLife -= elapsed;
        if(p.remainingLife <= 0) {
            resetParticle(p); // Once the particle expires, reset it to the origin with a new velocity
        }

        // Update the particle position
        p.pos[0] += p.vel[0] * elapsed;
        p.pos[1] += p.vel[1] * elapsed;
        p.pos[2] += p.vel[2] * elapsed;
        
        // Apply gravity to the velocity
        p.vel[1] -= 9.8 * elapsed;
        if(p.pos[1] < 0) {
            p.vel[1] *= -0.75; // Allow particles to bounce off the floor
            p.pos[1] = 0;
        }

        // Update the corresponding values in the array
        particleArray[(i*4) + 0] = p.pos[0];
        particleArray[(i*4) + 1] = p.pos[1];
        particleArray[(i*4) + 2] = p.pos[2];
        particleArray[(i*4) + 3] = p.remainingLife / p.lifespan;
    }

    // Once we are done looping through all the particles, update the buffer once
    gl.bindBuffer(gl.ARRAY_BUFFER, particleBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, particleArray, gl.STATIC_DRAW);
    gl.bindBuffer(gl.ARRAY_BUFFER, null);
}

/**
* Loads the scene
*/
function load(){
    Floor.build(80,2);
    Scene.addObject(Floor);

    lastFrameTime = Date.now();
}

function render(){
    var time = Date.now();

    // Update the particle positions
    updateParticles((time - lastFrameTime) / 1000.0);

    lastFrameTime = time;

    // Render scene
    draw();
}

/**
* invoked on every rendering cycle
*/
function draw() {
    gl.viewport(0, 0, c_width, c_height);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    transforms.updatePerspective();
    try{
        gl.enable(gl.BLEND);
        gl.useProgram(prg);

        transforms.calculateModelView();
        transforms.setMatrixUniforms();

        gl.uniform1f(Program.uPointSize, particleSize);

        gl.bindBuffer(gl.ARRAY_BUFFER, particleBuffer);
        gl.vertexAttribPointer(Program.aParticle, 4, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(Program.aParticle);

        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, spriteTexture.tex);
        gl.uniform1i(Program.uSampler, 0);

        gl.drawArrays(gl.POINTS, 0, particles.length);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
    }
    catch(err){
        alert(err);
        console.error(err.description);
    }
}


/**
* Entry point. This function is invoked when the page is loaded
*/
var app = null;
function runWebGLApp() {
    app = new WebGLApp("canvas-element-id");
    app.configureGLHook = configure;
    app.loadSceneHook   = load;
    app.drawSceneHook   = render;
    app.run();
}
</script>
</head>

<body onLoad='runWebGLApp()'>
<div id='top'>
<h1>WebGL Beginner's Guide - Chapter 10</h1>
<h2 id='title-id'>Point Sprites</h2>

<div id='logo-packt'><img src='packt.gif'/></div>
<p></p>
</div>

<div id='contents'>
<div id='canvasContainer'>
<canvas id='canvas-element-id' width='480' height='400'>
Your browser does not support the HTML5 canvas element.
</canvas>
</div>
</div>

<div id='bottom'>
<table style='padding=0px'>
<tr>
    <td style='vertical-align:center;'>
        <table>
        <tr>
            <td>Size:</td><td id='slider-particle-size-label'>14</td><td width='150px'><div id='slider-particle-size'/></td>
        </tr>
        <tr>
            <td>Lifespan:</td><td id='slider-particle-lifespan-label'>3.0</td><td width='150px'><div id='slider-particle-lifespan'/></td>
        </tr>
        </table>
    </td>
</tr>
</div>
<script> 
$('#slider-particle-size').slider({value:14.0, min: 4.0, max:32.0, step:1.0, slide:function(){
    particleSize = $('#slider-particle-size').slider("value");
    $('#slider-particle-size-label').html(particleSize);
}});

$('#slider-particle-lifespan').slider({value:3.0, min:0.1, max:10.0, step:0.1, slide:function(){
    particleLifespan = $('#slider-particle-lifespan').slider("value");
    $('#slider-particle-lifespan-label').html(particleLifespan);
}});
</script>
<script>cview.run(cview.MODE_VIEW,false,470);</script>
</body>
</html>