在webgl中进行全屏徒手画的正确方法是什么?

时间:2012-05-31 12:51:17

标签: javascript webgl three.js

要在webgl中实现鼠标手势,我想让用户“在屏幕上绘画”手绘模式。在3D webgl中进行操作可以对正在使用的画笔产生很好的着色效果,例如火焰效果,发光或其他很酷的图形糖果。

目前在webgl中有效绘制屏幕的推荐方法是什么?

谢谢!

1 个答案:

答案 0 :(得分:0)

您有2个选项。

您可以绘制到fbo,然后将fbo绘制到画布

您可以在创建webgl上下文时请求'preserveDrawingBuffer:true'。

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>