Open In App

How To Compile Shaders In WebGL?

Last Updated : 05 Aug, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Shaders in WebGL are small programs written in GLSL (OpenGL Shading Language) that run on the GPU to control the rendering of graphics. Compiling a shader means converting the GLSL code into a format that the GPU can execute, which involves creating a shader object, attaching source code, and processing it to check for errors.

In this article, we will explore how to compile shaders in WebGL, covering the necessary steps and approaches to get your shaders running smoothly.

Below are the approaches to Compile Shaders in WebGL.

1. Animated Shader Compilation and Rendering

We are performing animated shader compilation and rendering in WebGL by using vertex and fragment shaders loaded from script tags within the HTML. The vertex shader sets the position of vertices, while the fragment shader generates a dynamic color pattern based on a uniform time variable, creating an animated visual effect on the canvas.

Example: The below example performs Animated Shader Compilation and Rendering.

HTML
<!DOCTYPE html>

<head>
    <title>Example 1</title>
    <style>
        body {
            margin: 0;
            font-family: Arial, sans-serif;
            text-align: center;
        }

        canvas {
            display: block;
            margin: 20px auto;
            border: 1px solid #ccc;
        }

        h1 {
            color: green;
            margin-top: 20px;
        }

        h3 {
            color: #000000;
            margin-bottom: 20px;
        }
    </style>
</head>

<body onload="start()">
    <h1>GeeksforGeeks</h1>
    <h3>Approach 1: Animated Shader Compilation and Rendering</h3>
    <canvas id="glcanvas" width="640" height="480">
        Oops, your browser does not support the <code>canvas</code> tag.
    </canvas>

    <script type="text/javascript">
        let gl;
        let sId;
        let vBuff;
        let iBuff;
        let timer = 0;

        function initWebGL() {
            let canvas = document.getElementById("glcanvas");
            gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
            if (!gl) {
                alert("Unable to initialize WebGL. Your browser may not support it.");
                return;
            }
            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            initShaderProgram();
            initBuffers();
        }

        function initShaderProgram() {
            sId = gl.createProgram();
            let vertId = gl.createShader(gl.VERTEX_SHADER);
            let fragId = gl.createShader(gl.FRAGMENT_SHADER);
            let vert = document.getElementById("vertScript").text;
            let frag = document.getElementById("fragScript").text;

            gl.shaderSource(vertId, vert);
            gl.shaderSource(fragId, frag);
            gl.compileShader(vertId);
            gl.compileShader(fragId);
            if (!gl.getShaderParameter(vertId, gl.COMPILE_STATUS)) {
                alert("Vertex Shader Compiler Error: " + gl.getShaderInfoLog(vertId));
                gl.deleteShader(vertId);
                return;
            }
            if (!gl.getShaderParameter(fragId, gl.COMPILE_STATUS)) {
                alert("Fragment Shader Compiler Error: " + gl.getShaderInfoLog(fragId));
                gl.deleteShader(fragId);
                return;
            }
            gl.attachShader(sId, vertId);
            gl.attachShader(sId, fragId);
            gl.linkProgram(sId);
            if (!gl.getProgramParameter(sId, gl.LINK_STATUS)) {
                alert("Shader Linking Error: " + gl.getProgramInfoLog(sId));
            }
        }

        function initBuffers() {
            let vertices = new Float32Array([
                -1.0, -1.0, 0.0,
                -1.0, 1.0, 0.0,
                1.0, 1.0, 0.0,
                1.0, -1.0, 0.0
            ]);
            vBuff = gl.createBuffer()
            gl.bindBuffer(gl.ARRAY_BUFFER, vBuff);
            gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
            let indices = new Uint16Array([0, 1, 3, 2]);
            iBuff = gl.createBuffer();
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff);
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
        }
        window.requestAnimFrame = (function () {
            return window.requestAnimationFrame ||
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame ||
                window.oRequestAnimationFrame ||
                window.msRequestAnimationFrame ||
                function (callback) {
                    window.setTimeout(callback, 1000 / 60);
                };
        })();

        function animationLoop() {
            requestAnimFrame(animationLoop);
            render();
        }

        function render() {
            timer += 0.1;
            gl.useProgram(sId);
            let uID = gl.getUniformLocation(sId, "uTime");
            gl.uniform1f(uID, timer);
            let attId = gl.getAttribLocation(sId, "position");
            gl.enableVertexAttribArray(attId);
            gl.bindBuffer(gl.ARRAY_BUFFER, vBuff);
            gl.vertexAttribPointer(attId, 3, gl.FLOAT, false, 0, 0);
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff);
            gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_SHORT, 0);
        }

        function start() {
            initWebGL();
            animationLoop();
        }
    </script>
    <script id="vertScript" type="x-shader/x-vertex">
        attribute vec3 position; 
        void main(void) {
            gl_Position = vec4(position, 1.0);
        }
    </script>
    <script id="fragScript" type="x-shader/x-fragment">
        precision mediump float;
        uniform float uTime;
        void main(void) {
            vec2 st = gl_FragCoord.xy / vec2(640, 480);
            float r = sin(uTime + 8.0 * 3.14159265359 * st.x);
            float g = fract(sin(3.0 * 3.14159265359 * st.y));   
            float b = sin(uTime + 3.14159265359 * st.x * st.y);
            vec3 color = vec3(r, b, g);
            gl_FragColor = vec4(color, 1.0);
        }
    </script>
</body>

</html>


Output

OP1
How To Compile Shaders In WebGL

2. Shader Compilation from Input Fields

We are performing shader compilation directly from user-inputted code using WebGL. The process involves initializing WebGL, creating shaders from the provided code, compiling and linking them into a shader program, and rendering graphics with these shaders.

Example: The below example performs Shader Compilation from Input Fields.

HTML
<!DOCTYPE html>
<head>
    <title>Example 2</title>
    <style>
        body { 
            margin: 0; 
            font-family: Arial, sans-serif; 
            text-align: center; 
            background-color: #f0f0f0; 
        }
        canvas { 
            display: block; 
            margin: 20px auto; 
            border: 1px solid #ccc; 
            width: 400px; 
            height: 300px; 
        }
        h1 { 
            color: green; 
            margin-top: 20px; 
        }
        h3 { 
            color: black; 
            margin-bottom: 20px; 
        }
        .shader-input { 
            width: 80%; 
            max-width: 600px; 
            height: 100px; 
            margin: 10px auto; 
            display: block; 
            font-family: monospace; 
            padding: 10px; 
            border: 1px solid #ccc; 
            box-sizing: border-box; 
            background-color: #fff; 
        }
        .controls { 
            margin: 20px; 
            padding: 10px; 
            background-color: #fff; 
            border-radius: 8px; 
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 
        }
        .compile-btn { 
            padding: 10px 20px; 
            font-size: 16px; 
            cursor: pointer; 
            background-color: darkred; 
            color: #fff; 
            border: none; 
            border-radius: 5px; 
            transition: background-color 0.3s; 
        }
        .compile-btn:hover { 
            background-color: red; 
        }
        h4 { 
            color: #333; 
            margin-bottom: 10px; 
        }
    </style>
</head>
<body onload="start()">
    <h1>GeeksforGeeks</h1>
    <h3>Approach 2: Shader Compilation from Input Fields</h3>
    <canvas id="glcanvas">
        Oops, your browser does not support the <code>canvas</code> tag.
    </canvas>

    <div class="controls">
        <h4>Vertex Shader Code</h4>
        <textarea id="vertexShaderInput" class="shader-input">
attribute vec3 position;
void main(void) {
    gl_Position = vec4(position, 1.0);
}
        </textarea>
        <h4>Fragment Shader Code</h4>
        <textarea id="fragmentShaderInput" class="shader-input">
precision mediump float;
uniform float uTime;
void main(void) {
    vec2 st = gl_FragCoord.xy / vec2(400, 300);
    float r = sin(uTime + 8.0 * 3.14159265359 * st.x);
    float g = fract(sin(3.0 * 3.14159265359 * st.y));   
    float b = sin(uTime + 3.14159265359 * st.x * st.y);
    vec3 color = vec3(r, b, g);
    gl_FragColor = vec4(color, 1.0);
}
        </textarea>
        <button class="compile-btn" onclick="compileShadersFromInput()">Compile Shaders</button>
    </div>

    <script type="text/javascript">
        let gl;
        let shadeProg;
        let vBuff;
        let iBuff;
        let timer = 0;

        function initWebGL(){
            let canvas = document.getElementById("glcanvas");
            gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
            if (!gl) {
                alert("Unable to initialize WebGL. Your browser may not support it.");
                return;
            }
            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            initBuffers();
        }

        function compileShadersFromInput() {
            let vertexShaderCode = document.getElementById('vertexShaderInput').value;
            let fragmentShaderCode = document.getElementById('fragmentShaderInput').value;
            compileShaders(vertexShaderCode, fragmentShaderCode);
        }

        function compileShaders(vertexCode, fragmentCode){
            if (shadeProg) {
                gl.deleteProgram(shadeProg);
            }

            let vertexShader = gl.createShader(gl.VERTEX_SHADER);
            gl.shaderSource(vertexShader, vertexCode);
            gl.compileShader(vertexShader);
            if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
                alert("Vertex Shader Compilation Error: " + gl.getShaderInfoLog(vertexShader));
                return;
            }

            let fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
            gl.shaderSource(fragmentShader, fragmentCode);
            gl.compileShader(fragmentShader);
            if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
                alert("Fragment Shader Compilation Error: " + gl.getShaderInfoLog(fragmentShader));
                return;
            }

            shadeProg = gl.createProgram();
            gl.attachShader(shadeProg, vertexShader);
            gl.attachShader(shadeProg, fragmentShader);
            gl.linkProgram(shadeProg);

            if (!gl.getProgramParameter(shadeProg, gl.LINK_STATUS)) {
                alert("Program Linking Error: " + gl.getProgramInfoLog(shadeProg));
                return;
            }

            render();
        }

        function initBuffers(){
            let vertices = new Float32Array([
                -1.0, -1.0, 0.0,
                -1.0,  1.0, 0.0,
                 1.0,  1.0, 0.0,
                 1.0, -1.0, 0.0
            ]);
            vBuff = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, vBuff);
            gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

            let indices = new Uint16Array([0, 1, 3, 2]);
            iBuff = gl.createBuffer();
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff);
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
        }

        function animationLoop(){
            requestAnimationFrame(animationLoop);
            render(); 
        }

        function render(){
            if (!shadeProg) return;

            timer += 0.1;
            gl.useProgram(shadeProg);
            let attId = gl.getAttribLocation(shadeProg, "position");
            gl.enableVertexAttribArray(attId);
            gl.bindBuffer(gl.ARRAY_BUFFER, vBuff);
            gl.vertexAttribPointer(attId, 3, gl.FLOAT, false, 0, 0);
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff);
            gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_SHORT, 0);
        }

        function start(){
            initWebGL();
            animationLoop();
        }
    </script>
</body>
</html>


Output

2
How To Compile Shaders In WebGL

Next Article

Similar Reads