Advertisement
lignite0

ROBO24 - Particle system

Feb 18th, 2020
200
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
HTML 15.71 KB | None | 0 0
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <title>WebGL Particle System</title>
  5.     <script type="text/x-vertex-shader" id="particle-update-vert">
  6. #version 300 es
  7. precision mediump float;
  8.  
  9. uniform float u_TimeDelta;
  10. uniform sampler2D u_RgNoise;
  11. uniform vec2 u_Gravity;
  12. uniform vec2 u_Origin;
  13. uniform float u_MinTheta;
  14. uniform float u_MaxTheta;
  15. uniform float u_MinSpeed;
  16. uniform float u_MaxSpeed;
  17.  
  18. in vec2 i_Position;
  19. in float i_Age;
  20. in float i_Life;
  21. in vec2 i_Velocity;
  22.  
  23. out vec2 v_Position;
  24. out float v_Age;
  25. out float v_Life;
  26. out vec2 v_Velocity;
  27.  
  28. void main() {
  29.   if (i_Age >= i_Life) {
  30.     ivec2 noise_coord = ivec2(gl_VertexID % 512, gl_VertexID / 512);
  31.     vec2 rand = texelFetch(u_RgNoise, noise_coord, 0).rg;
  32.     float theta = u_MinTheta + rand.r*(u_MaxTheta - u_MinTheta);
  33.     float x = cos(theta);
  34.     float y = sin(theta);
  35.     v_Position = u_Origin;
  36.     v_Age = 0.0;
  37.     v_Life = i_Life;
  38.     v_Velocity =
  39.       vec2(x, y) * (u_MinSpeed + rand.g * (u_MaxSpeed - u_MinSpeed));
  40.   } else {
  41.     v_Position = i_Position + i_Velocity * u_TimeDelta;
  42.     v_Age = i_Age + u_TimeDelta;
  43.     v_Life = i_Life;
  44.     v_Velocity = i_Velocity + u_Gravity * u_TimeDelta;
  45.   }
  46.   gl_Position = vec4(v_Position, 0.0, 1.0);
  47.  
  48. }
  49.  
  50.  
  51.     </script>
  52.     <script type="text/x-fragment-shader" id="passthru-frag-shader">
  53. #version 300 es
  54. precision mediump float;
  55.  
  56. out vec4 o_FragColor;
  57.  
  58. void main() {
  59.   o_FragColor = vec4(1.0);
  60. }
  61.  
  62.  
  63.     </script>
  64.     <script type="text/x-vertex-shader" id="particle-render-vert">
  65. #version 300 es
  66. precision mediump float;
  67.  
  68. in vec2 i_Position;
  69.  
  70.  
  71. void main() {
  72.   gl_PointSize = 1.0;
  73.   gl_Position = vec4(i_Position, 0.0, 1.0);
  74. }
  75.  
  76.  
  77.     </script>
  78.     <script type="text/x-fragment-shader" id="particle-render-frag">
  79. #version 300 es
  80. precision mediump float;
  81.  
  82. out vec4 o_FragColor;
  83.  
  84. void main() {
  85.   o_FragColor = vec4(1.0);
  86. }
  87.  
  88.  
  89.     </script>
  90. </head>
  91. <body style="text-align:center"></body>
  92. <script>
  93.     function createShader(gl, shader_info) {
  94.         var shader = gl.createShader(shader_info.type);
  95.         var i = 0;
  96.         var shader_source = document.getElementById(shader_info.name).text;
  97.         /* skip whitespace to avoid glsl compiler complaining about
  98.         #version not being on the first line*/
  99.         while (/\s/.test(shader_source[i])) i++;
  100.         shader_source = shader_source.slice(i);
  101.         gl.shaderSource(shader, shader_source);
  102.         gl.compileShader(shader);
  103.         var compile_status = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  104.         if (!compile_status) {
  105.             var error_message = gl.getShaderInfoLog(shader);
  106.             throw "Could not compile shader \"" +
  107.             shader_info.name +
  108.             "\" \n" +
  109.             error_message;
  110.         }
  111.         return shader;
  112.     }
  113.  
  114.     /* Creates an OpenGL program object.
  115.        `gl' shall be a WebGL 2 context.
  116.        `shader_list' shall be a list of objects, each of which have a `name'
  117.           and `type' properties. `name' will be used to locate the script tag
  118.           from which to load the shader. `type' shall indicate shader type (i. e.
  119.           gl.FRAGMENT_SHADER, gl.VERTEX_SHADER, etc.)
  120.       `transform_feedback_varyings' shall be a list of varying that need to be
  121.         captured into a transform feedback buffer.*/
  122.     function createGLProgram(gl, shader_list, transform_feedback_varyings) {
  123.         var program = gl.createProgram();
  124.         for (var i = 0; i < shader_list.length; i++) {
  125.            var shader_info = shader_list[i];
  126.            var shader = createShader(gl, shader_info);
  127.            gl.attachShader(program, shader);
  128.        }
  129.  
  130.        /* Specify varyings that we want to be captured in the transform
  131.           feedback buffer. */
  132.        if (transform_feedback_varyings != null) {
  133.            gl.transformFeedbackVaryings(program,
  134.                transform_feedback_varyings,
  135.                gl.INTERLEAVED_ATTRIBS);
  136.        }
  137.        gl.linkProgram(program);
  138.        var link_status = gl.getProgramParameter(program, gl.LINK_STATUS);
  139.        if (!link_status) {
  140.            var error_message = gl.getProgramInfoLog(program);
  141.            throw "Could not link program.\n" + error_message;
  142.        }
  143.        return program;
  144.    }
  145.  
  146.    function randomRGData(size_x, size_y) {
  147.        var d = [];
  148.        for (var i = 0; i < size_x * size_y; ++i) {
  149.            d.push(Math.random() * 255.0);
  150.            d.push(Math.random() * 255.0);
  151.        }
  152.        return new Uint8Array(d);
  153.    }
  154.  
  155.    function initialParticleData(num_parts, min_age, max_age) {
  156.        var data = [];
  157.        for (var i = 0; i < num_parts; ++i) {
  158.            data.push(0.0);
  159.            data.push(0.0);
  160.            var life = min_age + Math.random() * (max_age - min_age);
  161.            data.push(life);
  162.            data.push(life);
  163.            data.push(0.0);
  164.            data.push(0.0);
  165.        }
  166.        return data;
  167.    }
  168.  
  169.    function setupParticleBufferVAO(gl, buffers, vao) {
  170.        gl.bindVertexArray(vao);
  171.        for (var i = 0; i < buffers.length; i++) {
  172.            var buffer = buffers[i];
  173.            gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer_object);
  174.            var offset = 0;
  175.            for (var attrib_name in buffer.attribs) {
  176.                if (buffer.attribs.hasOwnProperty(attrib_name)) {
  177.                    var attrib_desc = buffer.attribs[attrib_name];
  178.                    gl.enableVertexAttribArray(attrib_desc.location);
  179.                    gl.vertexAttribPointer(
  180.                        attrib_desc.location,
  181.                        attrib_desc.num_components,
  182.                        attrib_desc.type,
  183.                        false,
  184.                        buffer.stride,
  185.                        offset);
  186.                    var type_size = 4; /* we're only dealing with types of 4 byte size in this demo, unhardcode if necessary */
  187.                    offset += attrib_desc.num_components * type_size;
  188.                    if (attrib_desc.hasOwnProperty("divisor")) {
  189.                        gl.vertexAttribDivisor(attrib_desc.location, attrib_desc.divisor);
  190.                    }
  191.                }
  192.            }
  193.        }
  194.        gl.bindVertexArray(null);
  195.        gl.bindBuffer(gl.ARRAY_BUFFER, null);
  196.    }
  197.    function init(
  198.        gl,
  199.        num_particles,
  200.        particle_birth_rate,
  201.        min_age,
  202.        max_age,
  203.        min_theta,
  204.        max_theta,
  205.        min_speed,
  206.        max_speed,
  207.        gravity) {
  208.        if (max_age < min_age) {
  209.            throw "Invalid min-max age range.";
  210.        }
  211.        if (max_theta < min_theta ||
  212.            min_theta < -Math.PI ||
  213.            max_theta > Math.PI) {
  214.             throw "Invalid theta range.";
  215.         }
  216.         if (min_speed > max_speed) {
  217.             throw "Invalid min-max speed range.";
  218.         }
  219.         var update_program = createGLProgram(
  220.             gl,
  221.             [
  222.                 {name: "particle-update-vert", type: gl.VERTEX_SHADER},
  223.                 {name: "passthru-frag-shader", type: gl.FRAGMENT_SHADER},
  224.             ],
  225.             [
  226.                 "v_Position",
  227.                 "v_Age",
  228.                 "v_Life",
  229.                 "v_Velocity",
  230.             ]);
  231.         var render_program = createGLProgram(
  232.             gl,
  233.             [
  234.                 {name: "particle-render-vert", type: gl.VERTEX_SHADER},
  235.                 {name: "particle-render-frag", type: gl.FRAGMENT_SHADER},
  236.             ],
  237.             null);
  238.         var update_attrib_locations = {
  239.             i_Position: {
  240.                 location: gl.getAttribLocation(update_program, "i_Position"),
  241.                 num_components: 2,
  242.                 type: gl.FLOAT
  243.             },
  244.             i_Age: {
  245.                 location: gl.getAttribLocation(update_program, "i_Age"),
  246.                 num_components: 1,
  247.                 type: gl.FLOAT
  248.             },
  249.             i_Life: {
  250.                 location: gl.getAttribLocation(update_program, "i_Life"),
  251.                 num_components: 1,
  252.                 type: gl.FLOAT
  253.             },
  254.             i_Velocity: {
  255.                 location: gl.getAttribLocation(update_program, "i_Velocity"),
  256.                 num_components: 2,
  257.                 type: gl.FLOAT
  258.             }
  259.         };
  260.         var render_attrib_locations = {
  261.             i_Position: {
  262.                 location: gl.getAttribLocation(render_program, "i_Position"),
  263.                 num_components: 2,
  264.                 type: gl.FLOAT
  265.             }
  266.         };
  267.         var vaos = [
  268.             gl.createVertexArray(),
  269.             gl.createVertexArray(),
  270.             gl.createVertexArray(),
  271.             gl.createVertexArray()
  272.         ];
  273.         var buffers = [
  274.             gl.createBuffer(),
  275.             gl.createBuffer(),
  276.         ];
  277.         var vao_desc = [
  278.             {
  279.                 vao: vaos[0],
  280.                 buffers: [{
  281.                     buffer_object: buffers[0],
  282.                     stride: 4 * 6,
  283.                     attribs: update_attrib_locations
  284.                 }]
  285.             },
  286.             {
  287.                 vao: vaos[1],
  288.                 buffers: [{
  289.                     buffer_object: buffers[1],
  290.                     stride: 4 * 6,
  291.                     attribs: update_attrib_locations
  292.                 }]
  293.             },
  294.             {
  295.                 vao: vaos[2],
  296.                 buffers: [{
  297.                     buffer_object: buffers[0],
  298.                     stride: 4 * 6,
  299.                     attribs: render_attrib_locations
  300.                 }],
  301.             },
  302.             {
  303.                 vao: vaos[3],
  304.                 buffers: [{
  305.                     buffer_object: buffers[1],
  306.                     stride: 4 * 6,
  307.                     attribs: render_attrib_locations
  308.                 }],
  309.             },
  310.         ];
  311.         var initial_data =
  312.             new Float32Array(initialParticleData(num_particles, min_age, max_age));
  313.         gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]);
  314.         gl.bufferData(gl.ARRAY_BUFFER, initial_data, gl.STREAM_DRAW);
  315.         gl.bindBuffer(gl.ARRAY_BUFFER, buffers[1]);
  316.         gl.bufferData(gl.ARRAY_BUFFER, initial_data, gl.STREAM_DRAW);
  317.         for (var i = 0; i < vao_desc.length; i++) {
  318.            setupParticleBufferVAO(gl, vao_desc[i].buffers, vao_desc[i].vao);
  319.        }
  320.  
  321.        gl.clearColor(0.0, 0.0, 0.0, 1.0);
  322.        var rg_noise_texture = gl.createTexture();
  323.        gl.bindTexture(gl.TEXTURE_2D, rg_noise_texture);
  324.        gl.texImage2D(gl.TEXTURE_2D,
  325.            0,
  326.            gl.RG8,
  327.            512, 512,
  328.            0,
  329.            gl.RG,
  330.            gl.UNSIGNED_BYTE,
  331.            randomRGData(512, 512));
  332.        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT);
  333.        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT);
  334.        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  335.        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  336.        gl.enable(gl.BLEND);
  337.        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
  338.        return {
  339.            particle_sys_buffers: buffers,
  340.            particle_sys_vaos: vaos,
  341.            read: 0,
  342.            write: 1,
  343.            particle_update_program: update_program,
  344.            particle_render_program: render_program,
  345.            num_particles: initial_data.length / 6,
  346.            old_timestamp: 0.0,
  347.            rg_noise: rg_noise_texture,
  348.            total_time: 0.0,
  349.            born_particles: 0,
  350.            birth_rate: particle_birth_rate,
  351.            gravity: gravity,
  352.            origin: [0.0, 0.0],
  353.            min_theta: min_theta,
  354.            max_theta: max_theta,
  355.            min_speed: min_speed,
  356.            max_speed: max_speed
  357.        };
  358.    }
  359.  
  360.    function render(gl, state, timestamp_millis) {
  361.        var num_part = state.born_particles;
  362.        var time_delta = 0.0;
  363.        if (state.old_timestamp != 0) {
  364.            time_delta = timestamp_millis - state.old_timestamp;
  365.            if (time_delta > 500.0) {
  366.                 time_delta = 0.0;
  367.             }
  368.         }
  369.         if (state.born_particles < state.num_particles) {
  370.            state.born_particles = Math.min(state.num_particles,
  371.                Math.floor(state.born_particles + state.birth_rate * time_delta));
  372.        }
  373.        state.old_timestamp = timestamp_millis;
  374.        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  375.        gl.useProgram(state.particle_update_program);
  376.        gl.uniform1f(
  377.            gl.getUniformLocation(state.particle_update_program, "u_TimeDelta"),
  378.            time_delta / 1000.0);
  379.        gl.uniform1f(
  380.            gl.getUniformLocation(state.particle_update_program, "u_TotalTime"),
  381.            state.total_time);
  382.        gl.uniform2f(
  383.            gl.getUniformLocation(state.particle_update_program, "u_Gravity"),
  384.            state.gravity[0], state.gravity[1]);
  385.        gl.uniform2f(
  386.            gl.getUniformLocation(state.particle_update_program, "u_Origin"),
  387.            state.origin[0],
  388.            state.origin[1]);
  389.        gl.uniform1f(
  390.            gl.getUniformLocation(state.particle_update_program, "u_MinTheta"),
  391.            state.min_theta);
  392.        gl.uniform1f(
  393.            gl.getUniformLocation(state.particle_update_program, "u_MaxTheta"),
  394.            state.max_theta);
  395.        gl.uniform1f(
  396.            gl.getUniformLocation(state.particle_update_program, "u_MinSpeed"),
  397.            state.min_speed);
  398.        gl.uniform1f(
  399.            gl.getUniformLocation(state.particle_update_program, "u_MaxSpeed"),
  400.            state.max_speed);
  401.        state.total_time += time_delta;
  402.  
  403.        gl.activeTexture(gl.TEXTURE0);
  404.        gl.bindTexture(gl.TEXTURE_2D, state.rg_noise);
  405.        gl.uniform1i(
  406.            gl.getUniformLocation(state.particle_update_program, "u_RgNoise"),
  407.            0);
  408.        gl.bindVertexArray(state.particle_sys_vaos[state.read]);
  409.        gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, state.particle_sys_buffers[state.write]);
  410.  
  411.        //gl.enable(gl.RASTERIZER_DISCARD);
  412.        gl.beginTransformFeedback(gl.POINTS);
  413.        gl.drawArrays(gl.POINTS, 0, num_part);
  414.        gl.endTransformFeedback();
  415.        //gl.disable(gl.RASTERIZER_DISCARD);
  416.  
  417.        gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
  418.        //gl.bindVertexArray(state.particle_sys_vaos[state.read + 2]);
  419.        //gl.useProgram(state.particle_render_program);
  420.        //gl.drawArrays(gl.POINTS, 0, num_part);
  421.  
  422.        var tmp = state.read;
  423.        state.read = state.write;
  424.        state.write = tmp;
  425.        window.requestAnimationFrame(function (ts) {
  426.            render(gl, state, ts);
  427.        });
  428.    }
  429.  
  430.    function main() {
  431.        var canvas_element = document.createElement("canvas");
  432.        canvas_element.width = 800;
  433.        canvas_element.height = 600;
  434.        var webgl_context = canvas_element.getContext("webgl2");
  435.        if (webgl_context != null) {
  436.            document.body.appendChild(canvas_element);
  437.            var state =
  438.                init(
  439.                    webgl_context,
  440.                    100000,
  441.                    0.5,
  442.                    1.01,
  443.                    5.15,
  444.                    Math.PI / 2.0 - 0.5, Math.PI / 2.0 + 0.5,
  445.                    0.5, 1.0,
  446.                    [0.0, -0.8]);
  447.            canvas_element.onmousemove = function (e) {
  448.                var x = 2.0 * (e.pageX - this.offsetLeft) / this.width - 1.0;
  449.                var y = -(2.0 * (e.pageY - this.offsetTop) / this.height - 1.0);
  450.                state.origin = [x, y];
  451.            };
  452.            window.requestAnimationFrame(
  453.                function (ts) {
  454.                    render(webgl_context, state, ts);
  455.                });
  456.        } else {
  457.            document.write("WebGL2 is not supported by your browser");
  458.        }
  459.    }
  460.    main();
  461. </script>
  462. </html>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement