Jump to content

Let's create WebGL examples for practice


8Observer8
 Share

Recommended Posts

To create a desktop application from WebGL, you can rewrite it to OpenGL. Python is one of the easiest languages to build desktop applications.

Drawing Box2D physics engine colliders with b2Draw, OpenGL1, PyQt6 and Python

The example shows how to draw colliders using b2Draw. Uses OpenGL version 1 for simplicity.

pyBox2D works with Python 3.8. Download and install Python 3.8 from here: https://www.python.org/downloads/

Install the required packages with this command from CMD:

pip install Box2D PyQt6 PyOpenGL

Download the source code: https://github.com/8Observer8/edit-gravity-debug-drawer-opengl1-pyqt6

Go to your project folder and type this command to run the application: python main.py (or double click on main.py)

Install the very useful isort module (pip install isort), which allows you to sort plugins alphabetically with the command: isort . (dot means to sort in all files in the current directory)

The Notepad++ code editor has syntax highlighting for Python, JavaScript, HTML, C++, and more by default. There is a plugin (https://github.com/danielscherzer/NotepaddPP-glsl-integration) for syntax highlighting GLSL (OpenGL Shading Language). You can make the working directory the directory of the current file, which will open in the left pane. To do this, right-click on the file in the editor tab and select "Open into" -> "Open Containing Folder as Workspace". You can invoke the CMD (console) on Windows from Notapad++ by opening a code file in the editor, right-clicking on the file in the editor tab and selecting "Open into" -> "Open Containing Folder in cmd".

fb996c81-97b7-4638-bf04-ecbc7e6a32c7.gif.c29f73dc82e7ede8f27b541c69194060.gif

edit-gravity-debug-drawer-opengl3-pyqt6.gif.5a140bca68cd5dd0b1eebdd5c8b5fe9a.gif

Edited by 8Observer8
Link to comment
Share on other sites

  • 3 weeks later...

Here are two examples of using WebGL to create simple graphics in a web browser:

  1. Drawing a Triangle:
<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.js"></script>
    <script>
      // Set up the scene
      const scene = new THREE.Scene();
      const camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      const renderer = new THREE.WebGLRenderer();
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);

      // Create the triangle
      const geometry = new THREE.Geometry();
      geometry.vertices.push(
        new THREE.Vector3(-1, -1, 0),
        new THREE.Vector3(0, 1, 0),
        new THREE.Vector3(1, -1, 0)
      );
      geometry.faces.push(new THREE.Face3(0, 1, 2));
      const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
      const triangle = new THREE.Mesh(geometry, material);
      scene.add(triangle);

      // Render the scene
      camera.position.z = 5;
      renderer.render(scene, camera);
    </script>
  </head>
  <body>
  </body>
</html>
  1. Rotating a Cube:
<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.js"></script>
    <script>
      // Set up the scene
      const scene = new THREE.Scene();
      const camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      const renderer = new THREE.WebGLRenderer();
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);

      // Create the cube
      const geometry = new THREE.BoxGeometry(1, 1, 1);
      const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
      const cube = new THREE.Mesh(geometry, material);
      scene.add(cube);

      // Render the scene
      camera.position.z = 5;
      renderer.render(scene, camera);
      const render = function () {
        requestAnimationFrame(render);
        cube.rotation.x += 0.01;
        cube.rotation.y += 0.01;
        renderer.render(scene, camera);
      };
      render();
    </script>
  </head>
  <body>
  </body>
</html>

These examples show the basics of setting up a scene, creating objects, and rendering them in a web browser using Three.js.

Link to comment
Share on other sites

Translated a platformer with self-written physics from Olga28 to WebGL

Demo in browser: https://8observer8.github.io/webgl10-js/olga28s-platformer-2d/ Controls: left/right arrows and up arrow - jump.

Forum topic: https://www.cyberforum.ru/javascript-canvas/thread3075879.html Right-click in Chrome and select "Translate to English"

Sly7UHzitjc.jpg?size=548x397&quality=96&sign=8cd32bd70313a5dec2eb3dda44b9b403&type=album

Link to comment
Share on other sites

  • 2 weeks later...
  • 2 weeks later...

Some progress in pure WebGL. Placed tiles and colliders in Tiled. Packed tiles and sprites into one texture atlas using Free Texture Packer. Physics at OimoPhysics. Drawn with pure WebGL 1.0 and linear algebra with glMatrix

Demo in browser: https://8observer8.github.io/webgl10-js/mario-2d-jumps-oimophysics-webgl-js/

mario-2d-jumps-oimophysics-webgl-js.gif.1d46ea99f26740fae2ea2808276c344a.gif

Link to comment
Share on other sites

  • 2 months later...

I implemented the simplest multiplayer using WebSockets (package ws - npm) and Node.js. I send keyboard input to the server, which relays it to other clients. Used by WebGL 1.0, glMatrix and OimoPhysics. Extracted original models, textures and animations using RE1MV. Created a non-skinned skeletal animation using the Blender Python API. Skinning is expensive in terms of resources and is not needed for the models in this game, in which all parts of the animation models are separate objects.

multiplayer.gif.b42740ed22a23381ba1272ec43d549f8.gif

 

 

Link to comment
Share on other sites

My multiplayer above is not working properly. The idea was to have the same physics world on the clients and apply the same keyboard event (to apply the same pulses) without the physics world on the server for smooth movements. The server simply forwards the array of keys pressed to other clients. But after a while, the physical worlds on the clients are no longer the same. I should have an authoritarian server, that is, a physical world on the server to synchronize the positions and rotation angles of the clients.

Link to comment
Share on other sites

I completed the first version of the demo with an authoritarian server, where you can navigate in cooperative and single player modes from the first and third person. Made to show models, animations made in Blender and interactive interactions on WebGL. Demo in browser: https://8observer8.github.io/webgl10-js/room-webgl-js/

KiC35IC-VFo.jpg?size=363x376&quality=96&sign=bf21935c8d66b88cdb417a5d11379c28&type=album

Edited by 8Observer8
Link to comment
Share on other sites

  • 4 weeks later...

This example shows how to load the spritesheet files (png and json) from Free TexturePacker Online (https://www.codeandweb.com/tp-online) using Fetch API:

index.js

import loadTexture from "./load-texture.js";

async function init() {
    const characterTexturePath = "assets/spritesheets/character.png";
    const characterTexture = await loadTexture(characterTexturePath);

    const characterJSONPath = "assets/spritesheets/character.json";
    const characterResponse = await fetch(characterJSONPath);
    const characterContent = await characterResponse.text();
    console.log(characterContent);
}

init();

load-texture.js

import { gl } from "./webgl-context.js";

export default function loadTexture(url, minType = gl.NEAREST, magType = gl.NEAREST) {
    return new Promise(resolve => {
        const image = new Image();
        image.onload = () => {
            const texture = gl.createTexture();
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minType);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magType);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
            resolve(texture);
        };
        image.src = url;
    });
}

index.html

<!DOCTYPE html>

<html>

<head>
    <meta charset="utf-8">
    <title>Example</title>
    <link rel="stylesheet" type="text/css" href="./css/style.css">
</head>

<body>
    <canvas id="renderCanvas" width="300" height="300"></canvas>

    <!-- Since importmap is not yet supported by all browsers, it is
        necessary to add the polyfill es-module-shims.min.js -->
    <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.min.js"></script>

    <script type="importmap">
        {
            "imports": {
                "gl-matrix": "https://cdn.skypack.dev/[email protected]"
            }
        }
    </script>

    <script type="module" src="./js/index.js"></script>
</body>

</html>

 

Edited by 8Observer8
Link to comment
Share on other sites

  • 3 weeks later...
  • 4 months later...

document.addEventListener("DOMContentLoaded", function () {
    // Get the WebGL context
    const canvas = document.getElementById("webgl-canvas");
    const gl = canvas.getContext("webgl");

    // Check if WebGL is available
    if (!gl) {
        console.error("Unable to initialize WebGL. Your browser may not support it.");
        return;
    }

    // Vertex shader program
    const vsSource = `
        attribute vec4 aVertexPosition;
        uniform mat4 uModelViewMatrix;
        uniform mat4 uProjectionMatrix;
        void main(void) {
            gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
        }
    `;

    // Fragment shader program
    const fsSource = `
        void main(void) {
            gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
        }
    `;

    // Initialize shaders
    const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource);
    const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource);

    // Create program and link shaders
    const shaderProgram = createProgram(gl, vertexShader, fragmentShader);

    // Set up the buffers for the cube's vertices
    const vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);

    const vertices = [
        // 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,
    ];

    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

    // Set up the position attribute
    const position = gl.getAttribLocation(shaderProgram, "aVertexPosition");
    gl.vertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(position);

    // Set the clear color and enable depth testing
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clearDepth(1.0);
    gl.enable(gl.DEPTH_TEST);

    // Set up the perspective matrix
    const projectionMatrix = mat4.create();
    mat4.perspective(projectionMatrix, Math.PI / 4, canvas.width / canvas.height, 0.1, 100.0);

    // Set up the model-view matrix
    const modelViewMatrix = mat4.create();

    // Set up animation variables
    let then = 0;

    // Draw the scene
    function drawScene(now) {
        now *= 0.001;  // convert to seconds
        const deltaTime = now - then;
        then = now;

        // Rotate the cube
        mat4.rotateY(modelViewMatrix, modelViewMatrix, deltaTime);

        // Set the uniform matrices
        const uModelViewMatrix = gl.getUniformLocation(shaderProgram, "uModelViewMatrix");
        const uProjectionMatrix = gl.getUniformLocation(shaderProgram, "uProjectionMatrix");

        gl.uniformMatrix4fv(uModelViewMatrix, false, modelViewMatrix);
        gl.uniformMatrix4fv(uProjectionMatrix, false, projectionMatrix);

        // Clear the canvas and draw the cube
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
        gl.drawArrays(gl.TRIANGLE_FAN, 4, 4);
        gl.drawArrays(gl.LINE_LOOP, 0, 4);
        gl.drawArrays(gl.LINE_LOOP, 4, 4);

        // Request the next animation frame
        requestAnimationFrame(drawScene);
    }

    // Start the animation
    requestAnimationFrame(drawScene);
});

// Helper function to create shaders
function createShader(gl, type, source) {
    const shader = gl.createShader(type);
    gl.shaderSource(shader, source);
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        console.error("Shader compilation error:", gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
    }

    return shader;
}

// Helper function to create shader program
function createProgram(gl, vertexShader, fragmentShader) {
    const program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);

    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
        console.error("Shader program linking error:", gl.getProgramInfoLog(program));
        gl.deleteProgram(program);
        return null;
    }

    gl.useProgram(program);
    return program;
}
 

mca leads

Link to comment
Share on other sites

I copied codyjohnson1416's example on Plunker: https://plnkr.co/edit/frieLjHeWBwGPcJt?preview

codyjohnson1416-example.gif.556125fb456e97404bfe9a1c80262b01.gif

index.html

<!DOCTYPE html>

<html>

<head>
    <title>codyjohnson1416's Example</title>
</head>

<body>
    <canvas id="webgl-canvas"></canvas>

    <!-- Since import maps are not yet supported by all browsers, its is
        necessary to add the polyfill es-module-shims.js -->
    <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>

    <script type="importmap">
        {
            "imports": {
                "gl-matrix": "https://cdn.jsdelivr.net/npm/[email protected]/+esm"
            }
        }
    </script>

    <script type="module" src="./js/index.js"></script>
</body>

</html>

index.js

import { mat4 } from "gl-matrix";

document.addEventListener("DOMContentLoaded", function () {
    // Get the WebGL context
    const canvas = document.getElementById("webgl-canvas");
    const gl = canvas.getContext("webgl");

    // Check if WebGL is availablea
    if (!gl) {
        console.error("Unable to initialize WebGL. Your browser may not support it.");
        return;
    }

    // Vertex shader program
    const vsSource = `
        attribute vec4 aVertexPosition;
        uniform mat4 uModelViewMatrix;
        uniform mat4 uProjectionMatrix;
        void main(void) {
            gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
        }
    `;

    // Fragment shader program
    const fsSource = `
        void main(void) {
            gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
        }
    `;

    // Initialize shaders
    const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource);
    const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource);

    // Create program and link shaders
    const shaderProgram = createProgram(gl, vertexShader, fragmentShader);

    // Set up the buffers for the cube's vertices
    const vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);

    const vertices = [
        // 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,
    ];

    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

    // Set up the position attribute
    const position = gl.getAttribLocation(shaderProgram, "aVertexPosition");
    gl.vertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(position);

    // Set the clear color and enable depth testing
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clearDepth(1.0);
    gl.enable(gl.DEPTH_TEST);

    // Set up the perspective matrix
    const projectionMatrix = mat4.create();
    mat4.perspective(projectionMatrix, Math.PI / 4, canvas.width / canvas.height, 0.1, 100.0);

    // Set up the model-view matrix
    const modelViewMatrix = mat4.create();

    // Set up animation variables
    let then = 0;

    // Draw the scene
    function drawScene(now) {
        now *= 0.001;  // convert to seconds
        const deltaTime = now - then;
        then = now;

        // Rotate the cube
        mat4.rotateY(modelViewMatrix, modelViewMatrix, deltaTime);

        // Set the uniform matrices
        const uModelViewMatrix = gl.getUniformLocation(shaderProgram, "uModelViewMatrix");
        const uProjectionMatrix = gl.getUniformLocation(shaderProgram, "uProjectionMatrix");

        gl.uniformMatrix4fv(uModelViewMatrix, false, modelViewMatrix);
        gl.uniformMatrix4fv(uProjectionMatrix, false, projectionMatrix);

        // Clear the canvas and draw the cube
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
        gl.drawArrays(gl.TRIANGLE_FAN, 4, 4);
        gl.drawArrays(gl.LINE_LOOP, 0, 4);
        gl.drawArrays(gl.LINE_LOOP, 4, 4);

        // Request the next animation frame
        requestAnimationFrame(drawScene);
    }

    // Start the animation
    requestAnimationFrame(drawScene);
});

// Helper function to create shaders
function createShader(gl, type, source) {
    const shader = gl.createShader(type);
    gl.shaderSource(shader, source);
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        console.error("Shader compilation error:", gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
    }

    return shader;
}

// Helper function to create shader program
function createProgram(gl, vertexShader, fragmentShader) {
    const program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);

    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
        console.error("Shader program linking error:", gl.getProgramInfoLog(program));
        gl.deleteProgram(program);
        return null;
    }

    gl.useProgram(program);
    return program;
}

 

Edited by 8Observer8
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...