为了在 webgl 中实现鼠标手势,我想允许用户“在屏幕上绘图”徒手模式。在 3D webgl 中执行此操作将允许对正在使用的画笔产生很好的着色器效果,例如火焰效果、发光或其他很酷的图形糖果。
当前在 webgl 中有效地在屏幕上绘制的推荐方法是什么?
谢谢!
为了在 webgl 中实现鼠标手势,我想允许用户“在屏幕上绘图”徒手模式。在 3D webgl 中执行此操作将允许对正在使用的画笔产生很好的着色器效果,例如火焰效果、发光或其他很酷的图形糖果。
当前在 webgl 中有效地在屏幕上绘制的推荐方法是什么?
谢谢!
您有 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>