Open In App

How to Load 3D Models in WebGL?

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

3D Models in WebGL are digital representations of objects within a 3D space, rendered directly in the browser. They can range from simple shapes to complex designs and are crucial for creating immersive and interactive graphics on the web.

Below are the approaches to Creating a 3D Model in WebGL:

Using Plain WebGL for Loading a 3D Model

In this approach, we are using plain WebGL to render a 3D cube with colored faces by defining vertex and fragment shaders, setting up buffers for positions and colors, and applying transformations. The cube is animated with rotation using simple matrix manipulations and rendering techniques.

Example: The below example uses Plain WebGL for Loading a 3D Model.

HTML
<!DOCTYPE html>

<head>

    <title>3D Model</title>
    <style>
        body {
            margin: 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            background-color: #f0f0f0;
            font-family: Arial, sans-serif;
        }

        canvas {
            width: 400px;
            height: 400px;
            border: 1px solid #000;
        }

        h1 {
            color: green;
            margin: 0;
        }

        h3 {
            margin: 10px 0 20px;
        }
    </style>
</head>

<body>
    <h1>GeeksforGeeks</h1>
    <h3>Approach 1: Using Plain WebGL for Loading a 3D Model</h3>
    <canvas id="glcanvas"></canvas>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>
    <script src="script.js"></script>
</body>

</html>
JavaScript
function main() {
    const canvas = document.querySelector("#glcanvas");
    const gl = canvas.getContext("webgl") ||
    canvas.getContext("experimental-webgl");

    if (!gl) {
        alert("WebGL not supported");
        return;
    }

    const vsSource = `
        attribute vec4 aVertexPosition;
        attribute vec4 aVertexColor;
        uniform mat4 uModelViewMatrix;
        uniform mat4 uProjectionMatrix;
        varying lowp vec4 vColor;
        void main(void) {
            gl_Position = uProjectionMatrix *
            uModelViewMatrix * aVertexPosition;
            vColor = aVertexColor;
        }
    `;

    const fsSource = `
        varying lowp vec4 vColor;
        void main(void) {
            gl_FragColor = vColor;
        }
    `;

    const shaderProgram = initShaderProgram(gl, vsSource, fsSource);

    const programInfo = {
        program: shaderProgram,
        attribLocations: {
            vertexPosition: gl.getAttribLocation(shaderProgram,
            "aVertexPosition"),
            vertexColor: gl.getAttribLocation(shaderProgram,
            "aVertexColor"),
        },
        uniformLocations: {
            projectionMatrix: gl.getUniformLocation(shaderProgram,
            "uProjectionMatrix"),
            modelViewMatrix: gl.getUniformLocation(shaderProgram,
            "uModelViewMatrix"),
        },
    };

    const buffers = initBuffers(gl);

    let then = 0;

    function render(now) {
        now *= 0.001;
        const deltaTime = now - then;
        then = now;

        drawScene(gl, programInfo, buffers, deltaTime);

        requestAnimationFrame(render);
    }

    requestAnimationFrame(render);
}

function initShaderProgram(gl, vsSource, fsSource) {
    const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
    const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);

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

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
        alert("Unable to initialize the shader program: " 
        + gl.getProgramInfoLog(shaderProgram));
        return null;
    }

    return shaderProgram;
}

function loadShader(gl, type, source) {
    const shader = gl.createShader(type);
    gl.shaderSource(shader, source);
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        alert("An error occurred compiling the shaders: " 
        + gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
    }

    return shader;
}

function initBuffers(gl) {
    const positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

    const positions = [
        -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
        -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0,
        -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0,
        -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,
        1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0,
        -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(positions), gl.STATIC_DRAW);

    const faceColors = [
        [0.8, 0.2, 0.2, 1.0],
        [0.2, 0.8, 0.2, 1.0],
        [0.2, 0.2, 0.8, 1.0],
        [0.8, 0.8, 0.2, 1.0],
        [0.8, 0.2, 0.8, 1.0],
        [0.2, 0.8, 0.8, 1.0],
    ];

    let colors = [];

    faceColors.forEach(color => {
        colors = colors.concat(color, color, color, color);
    });

    const colorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

    const indexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);

    const indices = [
        0, 1, 2, 0, 2, 3,
        4, 5, 6, 4, 6, 7,
        8, 9, 10, 8, 10, 11,
        12, 13, 14, 12, 14, 15,
        16, 17, 18, 16, 18, 19,
        20, 21, 22, 20, 22, 23,
    ];

    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
    new Uint16Array(indices), gl.STATIC_DRAW);

    return {
        position: positionBuffer,
        color: colorBuffer,
        indices: indexBuffer,
    };
}

function drawScene(gl, programInfo, buffers, deltaTime) {
    gl.clearColor(0.2, 0.35, 0.15, 1.0);
    gl.clearDepth(1.0);
    gl.enable(gl.DEPTH_TEST);
    gl.depthFunc(gl.LEQUAL);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    const fieldOfView = (45 * Math.PI) / 180; // in radians
    const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    const zNear = 0.1;
    const zFar = 100.0;
    const projectionMatrix = mat4.create();
    mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar);

    const modelViewMatrix = mat4.create();
    mat4.translate(modelViewMatrix, modelViewMatrix, [-3.7, -1.0, -16.0]);
    mat4.rotate(modelViewMatrix, modelViewMatrix, cubeRotation, [0, 1, 0]);
    mat4.rotate(modelViewMatrix, modelViewMatrix, cubeRotation * 0.7, [1, 0, 0]);

    {
        const numComponents = 3;
        const type = gl.FLOAT;
        const normalize = false;
        const stride = 0;
        const offset = 0;
        gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
        gl.vertexAttribPointer(programInfo.attribLocations
        .vertexPosition, numComponents, type, normalize, stride, offset);
        gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
    }

    {
        const numComponents = 4;
        const type = gl.FLOAT;
        const normalize = false;
        const stride = 0;
        const offset = 0;
        gl.bindBuffer(gl.ARRAY_BUFFER, buffers.color);
        gl.vertexAttribPointer(programInfo.attribLocations
        .vertexColor, numComponents, type, normalize, stride, offset);
        gl.enableVertexAttribArray(programInfo.attribLocations.vertexColor);
    }

    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);

    gl.useProgram(programInfo.program);
    gl.uniformMatrix4fv(programInfo.uniformLocations
    .projectionMatrix, false, projectionMatrix);
    gl.uniformMatrix4fv(programInfo.uniformLocations
    .modelViewMatrix, false, modelViewMatrix);

    {
        const vertexCount = 36;
        const type = gl.UNSIGNED_SHORT;
        const offset = 0;
        gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
    }

    cubeRotation += deltaTime;
}

let cubeRotation = 0.0;

main();

Output:

1
Output

Using Three.js for Loading a 3D Model

In this approach, we are using Three.js to create a 3D scene with basic geometric shapes—a cube and a sphere. We set up a scene, camera, and renderer, and apply materials with specific properties to the shapes. Lighting is added for illumination, and an animation loop is implemented to continuously rotate the objects, creating a dynamic and interactive visual effect.

Example: The below example uses Three.js for Loading a 3D Model.

HTML
<!DOCTYPE html>

<head>
    <title>3D Models with Three.js</title>
    <style>
        body {
            margin: 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            background-color: #f0f0f0;
            font-family: Arial, sans-serif;
        }

        canvas {
            width: 400px;
            height: 200px;
            border: 1px solid #000;
        }

        h1 {
            color: green;
            margin: 0;
        }

        h3 {
            margin: 10px 0 20px;
            color: #333;
        }
    </style>
</head>

<body>
    <h1>GeeksforGeeks</h1>
    <h3>Approach 2: Using Three.js for Loading 3D Models</h3>
    <canvas id="webgl-canvas"></canvas>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="script.js"></script>
</body>

</html>
JavaScript
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 400 / 400, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ 
canvas: document.querySelector("#webgl-canvas") });
renderer.setSize(400, 400);

const geometry1 = new THREE.BoxGeometry();
const material1 = new THREE.MeshStandardMaterial(
{ color: 0x00ff00, roughness: 1.5, metalness: 0.5 });
const cube = new THREE.Mesh(geometry1, material1);
cube.position.x = -2;
scene.add(cube);

const geometry2 = new THREE.SphereGeometry(1, 32, 32);
const material2 = new THREE.MeshStandardMaterial(
{ color: 0x0000ff, roughness: 1.7, metalness: 0.3 });
const sphere = new THREE.Mesh(geometry2, material2);
sphere.position.x = 2;
scene.add(sphere);

const light = new THREE.PointLight(0xffffff, 1, 100);
light.position.set(10, 10, 10);
scene.add(light);

const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);

camera.position.z = 5;

function animate() {
    requestAnimationFrame(animate);

    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    sphere.rotation.x += 0.01;
    sphere.rotation.y += 0.01;

    renderer.render(scene, camera);
}

animate();

Output:

2
Output

Next Article

Similar Reads