How can I achieve fullscreen freehand drawing using webgl?

One interesting feature I would like to incorporate is mouse gestures in webgl where users can freely draw on the screen. By utilizing 3D webgl, we can enhance the drawing experience with shader effects like fire effects, glows, and other visually appealing graphics.

What is the best method currently recommended for drawing efficiently on the screen using webgl?

Thank you!

Answer №1

There are two possible approaches you can take:

One option is to draw to a frame buffer object (fbo) and then draw the fbo to the canvas.

Alternatively, you could specify 'preserveDrawingBuffer: true' when creating the WebGL context.

var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl", {preserveDrawingBuffer:true});

program = twgl.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
gl.useProgram(program);

var positionLoc = gl.getAttribLocation(program, "v_position");
var offsetLoc = gl.getUniformLocation(program, "u_offset");

var res = 1;
setupQuad(gl, res, positionLoc);

canvas.addEventListener('mousemove', draw, false);

function draw(event) {
  var m = getNoPaddingNoBorderCanvasRelativeMousePosition(event);
  // convert mouse coords to clip space since that's what this
  // particular shader is using. See 
  // https://webglfundamentals.org
  // for how to use pixels instead of clip space. 
  var x = m.x / gl.canvas.width * 2 - 1;
  var y = m.y / gl.canvas.height * -2 + 1;  // flip y

  drawBrush(x, y);
}

function drawBrush(x, y) {
  gl.uniform2f(offsetLoc, x, y);

  gl.drawElements(gl.TRIANGLES, res * res * 6, gl.UNSIGNED_SHORT, 0);
}

drawBrush(0, 0);



function setupQuad(gl, gridRes, positionLoc) {
  var scale = 0.05;
  var objects = [];

  var vertsAcross = gridRes + 1;
  var numVerts = vertsAcross * vertsAcross;
  var positions = new Float32Array(numVerts * 2);
  var indices = new Uint16Array(6 * gridRes * gridRes);
  var poffset = 0;

  for (var zz = 0; zz <= gridRes; ++zz) {
    for (var xx = 0; xx <= gridRes; ++xx) {
      var u = xx / gridRes;
      var v = zz / gridRes;
      positions[poffset + 0] = (-1 + 2 * u) * scale;
      positions[poffset + 1] = (-1 + 2 * v) * scale;
      poffset += 2;
    }
  }
    
  var tbase = 0;
  for (var zz = 0; zz < gridRes; ++zz) {
    var index = zz * vertsAcross;
    for (var xx = 0; xx < gridRes; ++xx) {
      indices[tbase + 0] = index + 0;
      indices[tbase + 1] = index + 1;
      indices[tbase + 2] = index + vertsAcross;
      indices[tbase + 3] = index + vertsAcross;
      indices[tbase + 4] = index + 1;
      indices[tbase + 5] = index + vertsAcross + 1;
      index += 1;
      tbase += 6;
    }
  }
    
  function makeBuffer(data, type, size, loc) {
    var buf = gl.createBuffer();
    gl.bindBuffer(type, buf);
    gl.bufferData(type, data, gl.STATIC_DRAW);
    if (type == gl.ARRAY_BUFFER) {
      gl.enableVertexAttribArray(loc);
      gl.vertexAttribPointer(loc, size, gl.FLOAT, false, 0, 0);
    }
    return buf;
  }
    
  objects.push(makeBuffer(positions, gl.ARRAY_BUFFER, 2, positionLoc));
  objects.push(makeBuffer(indices, gl.ELEMENT_ARRAY_BUFFER));
    
  return objects;
};

function getRelativeMousePosition(event, target) {
  target = target || event.target;
  var rect = target.getBoundingClientRect();

  return {
    x: event.clientX - rect.left,
    y: event.clientY - rect.top,
  }
}

// assumes target or event.target is canvas
function getNoPaddingNoBorderCanvasRelativeMousePosition(event, target) {
  target = target || event.target;
  var pos = getRelativeMousePosition(event, target);

  pos.x = pos.x * target.width  / canvas.clientWidth;
  pos.y = pos.y * target.height / canvas.clientHeight;

  return pos;  
}
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/twgl.min.js"></script>
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 v_position;
uniform vec2 u_offset;
    
void main() {
    gl_Position = vec4(v_position + u_offset, 0, 1);
}
</script>
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
void main() {
    gl_FragColor = vec4(0, 1, 0, 1);  
}
</script>
<canvas id="canvas" width="400" height="300"></canvas>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The UI bootstrap dropdown toggle requires two clicks to reopen after being manually closed

Utilizing the UI Bootstrap drop-down element to display the calendar from angular-bootstrap-datetimepicker upon clicking. Additionally, a $watch has been implemented to close the dropdown once a date is chosen. Access the Plunker here <div uib-dropdow ...

Determine the dimensions of the objects within a JSON array

My JSON response creation looks like this : { "G5LDUHRPEEA6B-39CFBWYA": [], "JMSK0DKOEEA0UXMY750O3W": [], "ISVN8JF1EEAD3W398ZNOSA": [ { "delloData": "1478644629", "ref": "75", "dataType": "String", "somePart": LA, ...

Utilizing Three.js to showcase large 3D images from a JSON file

I am in need of showcasing 3D images. I have a .obj file that is 80 MB in size which I converted to a json file size of nearly 75 MB. While using three js, I managed to display a rotating 3D image, but the main issue is the loading speed, which currently t ...

React App with Material UI V1-beta Integration

I just installed the Create React App example from Material-UI.com. curl https://codeload.github.com/callemall/material-ui/tar.gz/v1-beta | tar -xz --strip=2 material-ui-1-beta/examples/create-react-app Upon installation, I encountered the following erro ...

The require statement in Vue.js is failing to function dynamically

Having trouble dynamically requiring an image in Vue.js and it's not functioning as expected <div class="card" :style="{ background: 'url(' + require(image) + ')',}"> export default { data() { return { ...

Tips for refreshing the current page with passing a parameter in the URL?

To achieve the functionality of reloading the page whenever different buttons are pressed and sending a String to the same page, I need to take that String on created() and use it to send an HTTP Get into my Database. My current setup looks like this: exp ...

Striving to implement a dynamic dropdown menu using React's <select> element and rendering users from an array as selectable options

I am currently working on a project to develop an application that allows users to be added to a MongoDb database and have their names displayed in a dropdown menu. While creating the form, I encountered two issues - one related to the closing tag error a ...

Alter the entity type when passing it as a parameter

Trying to alter the Entity type, I am invoking a function that requires two parameters - one being the entity and the other a location. However, when trying to assign a Type for the first entity, it throws an error: Error: Argument of type 'Node<En ...

Issue with saving cookie from Express.js backend to Nuxt.js frontend

After successfully creating an authorization server using Express.js, I encountered a problem when trying to save the access and rotating refresh tokens as signed cookies. The issue arose from having separate backend and frontend servers with different dom ...

Looking to attach the "addEventListener" method to multiple elements that share the same class?

I'm trying to use an event listener in JS to handle the logic in the "onClick" function, but it's only executing once. I've applied the same class to all four buttons, so I'm not sure why it's only working for the first one. HTML: ...

The getelementbyid function can only target and retrieve the information from the first ID within a PHP

I am facing an issue with a form embedded in a PHP while loop and processed by JavaScript using getelementbyid. When I submit the form, it only submits the first item on the loop. Below is my PHP code: $qsel = "SELECT * FROM sellbusiness ORDER BY sn DESC ...

Steps to show a particular row in a Vue.js API

I have a question about how to retrieve data from an API and display it in a textbox when the edit button on a specific row table is clicked. The data should include its own id along with other details. I apologize for sharing my code in this format, as I ...

Javascript did not provide any prompt

I can't seem to get the function to run when I click the link, it only takes me to the link address. (The final alert() function is not executing). I really need that alert message, what am I missing? Below is my code: <html> <head> ...

The drop-down button unexpectedly disappears when using Bootstrap in combination with jQuery autocomplete

I have some javascript code that is structured like: <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>jQuery UI Autocompl ...

Enhancing web page interactivity through dynamic element names with Javascript and jQuery

I have an interesting HTML setup that looks like this: <div> <input type="text" name="array[a][b][0][foo]" /> <input type="text" name="array[a][b][0][bar]" /> <select name="array[0][a][b][baz]>...</select> </div> ...

Timezones not synchronizing properly with Moment.js

Hello everyone, I am currently exploring the world of moment.js along with the timezone add-on. I'm encountering some issues with a world clock project that involves using moment.js and moment-timezone.js together on a page where highcharts are also p ...

ng-repeat not functioning properly with custom tabs

Everything was working perfectly until I added ng-repeat to the content <ul> <li ng-class="{active:tab===1}"> <a href ng-click="tab = tab==1 ? a : 1">Tab1</a> </li> <l ...

Adjust the size of the text and save it in a cookie for later use

I am in need of a script that can dynamically increase and decrease the font size on a website while retaining the user's chosen setting even after they return to the site. I believe utilizing cookies is the way to achieve this functionality. Despite ...

Dealing with an empty req.body in an Express.js POST request

Having trouble solving this issue with a simple search. Can anyone provide clearer guidance? In the client-side code, I attempted to attach an object using xhr.send(obj). Currently, I'm trying to append to the formData object, but the result remains ...

Issue with Flowtype not properly refreshing when maximizing or minimizing

There is a strange behavior with my Flowtype plugin. When I load the page without maximizing the screen, the font size remains the same even after maximizing the screen. However, if I then restore the window to its original size, the font size adjusts as ...